MYSQL 应该是最风行的 WEB 后端数据库。大量利用于 PHP,Ruby,Python,Java 等 Web 语言开发我的项目中,无论 NOSQL 倒退如许快,都不影响大部分架构师抉择 MYSQL 作为数据存储。
MYSQL 如此不便和稳固,以至于咱们在开发 WEB 程序的时候非常少想到它。即便想到优化也是程序级别的,比如不要写过于耗费资源的 SQL 语句。可是除此之外,在整个零碎上依然有十分多可能优化的中央。
1 优化原理
说起 MySQL 的查问优化,置信大家会想到:不能应用 SELECT *、不应用 NULL 字段、正当创立索引、为字段抉择适合的数据类型 ….. 你是否真的了解这些优化技巧?是否了解其背地的工作原理?在理论场景下性能真有晋升吗?我想未必。因此了解这些优化倡议背地的原理就尤为重要,心愿本文能让你从新扫视这些优化倡议,并在理论业务场景下正当的使用。
MySQL 逻辑架构
如果能在头脑中构建一幅 MySQL 各组件之间如何协同工作的架构图,有助于深刻了解 MySQL 服务器。下图展现了 MySQL 的逻辑架构图。
MySQL 逻辑架构
MySQL 逻辑架构整体分为三层,最上层为客户端层,并非 MySQL 所独有,诸如:连贯解决、受权认证、平安等性能均在这一层解决。MySQL 大多数外围服务均在两头这一层,包含查问解析、剖析、优化、缓存、内置函数(比方:工夫、数学、加密等函数)。所有的跨存储引擎的性能也在这一层实现:存储过程、触发器、视图等。
最上层为存储引擎,其负责 MySQL 中的数据存储和提取。和 Linux 下的文件系统相似,每种存储引擎都有其劣势和劣势。两头的服务层通过 API 与存储引擎通信,这些 API 接口屏蔽了不同存储引擎间的差别。
MySQL 查问过程
咱们总是心愿 MySQL 可能取得更高的查问性能,最好的方法是弄清楚 MySQL 是如何优化和执行查问的。一旦了解了这一点,就会发现:很多的查问优化工作实际上就是遵循一些准则让 MySQL 的优化器可能依照料想的正当形式运行而已。当向 MySQL 发送一个申请的时候,MySQL 到底做了些什么呢?
MySQL 查问过程
–
客户端 / 服务端通信协议
MySQL 客户端 / 服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时产生。一旦一端开始发送音讯,另一端要接管残缺个音讯能力响应它,所以咱们无奈也毋庸将一个音讯切成小块独立发送,也没有方法进行流量管制。客户端用一个独自的数据包将查问申请发送给服务器,所以当查问语句很长的时候,须要设置 max_allowed_packet 参数。然而须要留神的是,如果查问切实是太大,服务端会回绝接管更多数据并抛出异样。与之相同的是,服务器响应给用户的数据通常会很多,由多个数据包组成。然而当服务器响应客户端申请时,客户端必须残缺的接管整个返回后果,而不能简略的只取后面几条后果,而后让服务器进行发送。因此在理论开发中,尽量放弃查问简略且只返回必须的数据,减小通信间数据包的大小和数量是一个十分好的习惯,这也是查问中尽量避免应用 SELECT * 以及加上 LIMIT 限度的起因之一。
–
Linuxc/c++ 服务器开发高阶视频学习材料 +qun720209036 获取
内容包含 C /C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,P2P,K8S,Docker,TCP/IP,,Linux 内核,协程,DPDK 多个高级知识点。
–
查问缓存
在解析一个查问语句前,如果查问缓存是关上的,那么 MySQL 会查看这个查问语句是否命中查问缓存中的数据。如果以后查问恰好命中查问缓存,在查看一次用户权限后间接返回缓存中的后果。这种状况下,查问不会被解析,也不会生成执行打算,更不会执行。
MySQL 将缓存寄存在一个援用表(不要了解成 table,能够认为是相似于 HashMap 的数据结构),通过一个哈希值索引,这个哈希值通过查问自身、以后要查问的数据库、客户端协定版本号等一些可能影响后果的信息计算得来。所以两个查问在任何字符上的不同(例如:空格、正文),都会导致缓存不会命中。
如果查问中蕴含任何用户自定义函数、存储函数、用户变量、长期表、mysql 库中的零碎表,其查问后果都不会被缓存。比方函数 NOW()或者 CURRENT_DATE()会因为不同的查问工夫,返回不同的查问后果,再比方蕴含 CURRENT_USER 或者 CONNECION_ID()的查问语句会因为不同的用户而返回不同的后果,将这样的查问后果缓存起来没有任何的意义。
既然是缓存,就会生效,那查问缓存何时生效呢?MySQL 的查问缓存零碎会跟踪查问中波及的每个表,如果这些表(数据或构造)发生变化,那么和这张表相干的所有缓存数据都将生效。正因为如此,在任何的写操作时,MySQL 必须将对应表的所有缓存都设置为生效。如果查问缓存十分大或者碎片很多,这个操作就可能带来很大的零碎耗费,甚至导致系统僵死一会儿。而且查问缓存对系统的额定耗费也不仅仅在写操作,读操作也不例外:
1. 任何的查问语句在开始之前都必须通过查看,即便这条 SQL 语句永远不会命中缓存 2. 如果查问后果能够被缓存,那么执行实现后,会将后果存入缓存,也会带来额定的零碎耗费
基于此,咱们要晓得并不是什么状况下查问缓存都会进步零碎性能,缓存和生效都会带来额定耗费,只有当缓存带来的资源节约大于其自身耗费的资源时,才会给零碎带来性能晋升。但要如何评估关上缓存是否可能带来性能晋升是一件十分艰难的事件,也不在本文探讨的领域内。如果零碎的确存在一些性能问题,能够尝试关上查问缓存,并在数据库设计上做一些优化,比方:
1. 用多个小表代替一个大表,留神不要适度设计 2. 批量插入代替循环单条插入 3. 正当管制缓存空间大小,一般来说其大小设置为几十兆比拟适合 4. 能够通过 SQL_CACHE 和 SQL_NO_CACHE 来管制某个查问语句是否须要进行缓存
最初的忠告是不要轻易关上查问缓存,特地是写密集型利用。如果你切实是忍不住,能够将 query_cache_type 设置为 DEMAND,这时只有退出 SQL_CACHE 的查问才会走缓存,其余查问则不会,这样能够十分自在地管制哪些查问须要被缓存。当然查问缓存零碎自身是非常复杂的,这里探讨的也只是很小的一部分,其余更深刻的话题,比方:缓存是如何应用内存的?如何管制内存的碎片化?事务对查问缓存有何影响等等,读者能够自行浏览相干材料,这里权当抛砖引玉吧。
语法解析和预处理
MySQL 通过关键字将 SQL 语句进行解析,并生成一颗对应的解析树。这个过程解析器次要通过语法规定来验证和解析。比方 SQL 中是否应用了谬误的关键字或者关键字的程序是否正确等等。预处理则会依据 MySQL 规定进一步查看解析树是否非法。比方查看要查问的数据表和数据列是否存在等等。
查问优化
通过后面的步骤生成的语法树被认为是非法的了,并且由优化器将其转化成查问打算。少数状况下,一条查问能够有很多种执行形式,最初都返回相应的后果。优化器的作用就是找到这其中最好的执行打算。MySQL 应用基于老本的优化器,它尝试预测一个查问应用某种执行打算时的老本,并抉择其中老本最小的一个。在 MySQL 能够通过查问以后会话的 last_query_cost 的值来失去其计算以后查问的老本。
mysql> select * from t_message limit 10;
… 省略后果集
mysql> show status like ‘last_query_cost’; | |
---|---|
Variable_name | Value |
Last_query_cost | 6391.799000 |
示例中的后果示意优化器认为大略须要做 6391 个数据页的随机查找能力实现下面的查问。这个后果是依据一些列的统计信息计算得来的,这些统计信息包含:每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的散布状况等等。有十分多的起因会导致 MySQL 抉择谬误的执行打算,比方统计信息不精确、不会思考不受其管制的操作老本(用户自定义函数、存储过程)、MySQL 认为的最优跟咱们想的不一样(咱们心愿执行工夫尽可能短,但 MySQL 值抉择它认为老本小的,但老本小并不意味着执行工夫短)等等。MySQL 的查问优化器是一个非常复杂的部件,它应用了十分多的优化策略来生成一个最优的执行打算:1. 从新定义表的关联程序(多张表关联查问时,并不一定依照 SQL 中指定的程序进行,但有一些技巧能够指定关联程序)2. 优化 MIN()和 MAX()函数(找某列的最小值,如果该列有索引,只须要查找 B +Tree 索引最左端,反之则能够找到最大值,具体原理见下文)3. 提前终止查问(比方:应用 Limit 时,查找到满足数量的后果集后会立刻终止查问)4. 优化排序(在老版本 MySQL 会应用两次传输排序,即先读取行指针和须要排序的字段在内存中对其排序,而后再依据排序后果去读取数据行,而新版本采纳的是单次传输排序,也就是一次读取所有的数据行,而后依据给定的列排序。对于 I / O 密集型利用,效率会高很多)随着 MySQL 的一直倒退,优化器应用的优化策略也在一直的进化,这里仅仅介绍几个十分罕用且容易了解的优化策略,其余的优化策略,大家自行查阅吧。
查问执行引擎
在实现解析和优化阶段当前,MySQL 会生成对应的执行打算,查问执行引擎依据执行打算给出的指令逐渐执行得出后果。整个执行过程的大部分操作均是通过调用存储引擎实现的接口来实现,这些接口被称为 handler API。查问过程中的每一张表由一个 handler 实例示意。实际上,MySQL 在查问优化阶段就为每一张表创立了一个 handler 实例,优化器能够依据这些实例的接口来获取表的相干信息,包含表的所有列名、索引统计信息等。存储引擎接口提供了十分丰盛的性能,但其底层仅有几十个接口,这些接口像搭积木一样实现了一次查问的大部分操作。
返回后果给客户端
查问执行的最初一个阶段就是将后果返回给客户端。即便查问不到数据,MySQL 依然会返回这个查问的相干信息,比方改查问影响到的行数以及执行工夫等等。如果查问缓存被关上且这个查问能够被缓存,MySQL 也会将后果寄存到缓存中。后果集返回客户端是一个增量且逐渐返回的过程。有可能 MySQL 在生成第一条后果时,就开始向客户端逐渐返回后果集了。这样服务端就毋庸存储太多后果而耗费过多内存,也能够让客户端第一工夫取得返回后果。须要留神的是,后果集中的每一行都会以一个满足①中所形容的通信协议的数据包发送,再通过 TCP 协定进行传输,在传输过程中,可能对 MySQL 的数据包进行缓存而后批量发送。回头总结一下 MySQL 整个查问执行过程,总的来说分为 6 个步骤:1. 客户端向 MySQL 服务器发送一条查问申请 2. 服务器首先查看查问缓存,如果命中缓存,则立即返回存储在缓存中的后果。否则进入下一阶段 3. 服务器进行 SQL 解析、预处理、再由优化器生成对应的执行打算 4.MySQL 依据执行打算,调用存储引擎的 API 来执行查问 5. 将后果返回给客户端,同时缓存查问后果
2 事务及引擎
MySQL 的存储引擎可能是所有关系型数据库产品中最具备特色的了,不仅能够同时应用多种存储引擎,而且每种存储引擎和 MySQL 之间应用插件形式这种十分松的耦合关系。
因为各存储引擎性能个性差别较大,须要关注如何来抉择适合的存储引擎来应答不同的业务场景。
一、MyISAM
o 个性
1. 不反对事务:MyISAM 存储引擎不反对事务,所以对事务有要求的业务场景不能应用 2. 表级锁定:其锁定机制是表级索引,这尽管能够让锁定的实现老本很小然而也同时大大降低了其并发性能 3. 读写相互阻塞:不仅会在写入的时候阻塞读取,MyISAM 还会在读取的时候阻塞写入,但读自身并不会阻塞另外的读 4. 只会缓存索引:MyISAM 能够通过 key_buffer 缓存以大大提高拜访性能缩小磁盘 IO,然而这个缓存区只会缓存索引,而不会缓存数据
o 实用场景
1. 不须要事务反对(不反对)2. 并发绝对较低(锁定机制问题)3. 数据批改绝对较少(阻塞问题)4. 以读为主 5. 数据一致性要求不是十分高
o 最佳实际
1. 尽量索引(缓存机制)2. 调整读写优先级,依据理论需要确保重要操作更优先 3. 启用提早插入改善大批量写入性能 4. 尽量程序操作让 insert 数据都写入到尾部,缩小阻塞 5. 合成大的操作,升高单个操作的阻塞工夫 6. 升高并发数,某些高并发场景通过利用来进行排队机制 7. 对于绝对动态的数据,充分利用 Query Cache 能够极大的进步拜访效率 8.MyISAM 的 Count 只有在全表扫描的时候特地高效,带有其余条件的 count 都须要进行理论的数据拜访
二、InnoDB
o 个性
1. 具备较好的事务反对:反对 4 个事务隔离级别,反对多版本读 2. 行级锁定:通过索引实现,全表扫描依然会是表锁,留神间隙锁的影响 3. 读写阻塞与事务隔离级别相干 4. 具备十分高效的缓存个性:能缓存索引,也能缓存数据 5. 整个表和主键以 Cluster 形式存储,组成一颗均衡树 6. 所有 Secondary Index 都会保留主键信息
o 实用场景
1. 须要事务反对(具备较好的事务个性)2. 行级锁定对高并发有很好的适应能力,但须要确保查问是通过索引实现 3. 数据更新较为频繁的场景 4. 数据一致性要求较高 5. 硬件设施内存较大,能够利用 InnoDB 较好的缓存能力来进步内存利用率,尽可能减少磁盘 IO
o 最佳实际
1. 主键尽可能小,防止给 Secondary index 带来过大的空间累赘 2. 防止全表扫描,因为会应用表锁 3. 尽可能缓存所有的索引和数据,进步响应速度 4. 在大批量小插入的时候,尽量本人管制事务而不要应用 autocommit 主动提交 5. 正当设置 innodb_flush_log_at_trx_commit 参数值,不要适度谋求安全性 6. 防止主键更新,因为这会带来大量的数据挪动
三、NDBCluster
o 个性
1. 分布式:分布式存储引擎,能够由多个 NDBCluster 存储引擎组成集群别离寄存整体数据的一部分 2. 反对事务:和 Innodb 一样,反对事务 3. 可与 mysqld 不在一台主机:能够和 mysqld 离开存在于独立的主机上,而后通过网络和 mysqld 通信交互 4. 内存需求量微小:新版本索引以及被索引的数据必须寄存在内存中,老版本所有数据和索引必须存在与内存中
o 实用场景
1. 具备十分高的并发需要 2. 对单个申请的响应并不是十分的 critical 3. 查问简略,过滤条件较为固定,每次申请数据量较少,又不心愿本人进行程度 Sharding
o 最佳实际
1. 尽可能让查问简略,防止数据的跨节点传输 2. 尽可能满足 SQL 节点的计算性能,大一点的集群 SQL 节点会显著多余 Data 节点 3. 在各节点之间尽可能应用万兆网络环境互联,以缩小数据在网络层传输过程中的延时
3 缓存参数优化
从内存中读取一个数据库的工夫是微秒级别,而从一块一般硬盘上读取一个 IO 是在毫秒级别,二者相差 3 个数量级。所以,要优化数据库,首先第一步须要优化的就是 IO,尽可能将磁盘 IO 转化为内存 IO。从 MySQL 数据库 IO 相干参数(缓存参数)的角度来看看能够通过以下参数进行 IO 优化(倡议级):
· query_cache_type : 如果全副应用 innodb 存储引擎,倡议为 0,如果应用 MyISAM 存储引擎,倡议为 2,同时在 SQL 语句中显式管制是否应用 query cache;
· query_cache_size: 依据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,个别不倡议太大,256MB 可能曾经差不多了,大型的配置型静态数据可适当调大;
· binlog_cache_size: 个别环境 2MB~4MB 是一个适合的抉择,事务较大且写入频繁的数据库环境能够适当调大,但不倡议超过 32MB;
· key_buffer_size: 如果不应用 MyISAM 存储引擎,16MB 足以,用来缓存一些零碎表信息等。如果应用 MyISAM 存储引擎,在内存容许的状况下,尽可能将所有索引放入内存,简略来说就是“越大越好”;
· bulk_insert_buffer_size: 如果经常性的须要应用批量插入的非凡语句(下面有阐明)来插入数据,能够适当调大该参数至 16MB~32MB,不倡议持续增大,某人 8MB;
· innodb_buffer_pool_size: 如果不应用 InnoDB 存储引擎,能够不必调整这个参数,如果须要应用,在内存容许的状况下,尽可能将所有的 InnoDB 数据文件寄存如内存中,同样将但来说也是“越大越好”;
· innodb_additional_mem_pool_size: 个别的数据库倡议调整到 8MB~16MB,如果表特地多,能够调整到 32MB,能够依据 error log 中的信息判断是否须要增大;
· innodb_log_buffer_size: 默认是 1MB,系的如频繁的零碎可适当增大至 4MB~8MB。当然如下面介绍所说,这个参数实际上还和另外的 flush 参数相干。一般来说不倡议超过 32MB;
· innodb_max_dirty_pages_pct: 依据以往的教训,重启复原的数据如果要超过 1G 的话,启动速度会比较慢,简直难以承受,所以倡议不大于 1GB/innodb_buffer_pool_size(GB)*100 这个值。当然,如果你可能忍耐启动工夫比拟长,而且心愿尽量减少内存至磁盘的 flush,能够将这个值调整到 90,但不倡议超过 90。
注:以上取值范畴仅仅只是依据以往遇到的数据库场景所失去的一些优化经验值,并不一定实用于所有场景,所以在理论优化过程中还须要大家本人一直的调整剖析。
4 SQL 优化
4.1 优化指标
1、缩小 IO 次数
IO 永远是数据库最容易瓶颈的中央,这是由数据库的职责所决定的,大部分数据库操作中超过 90% 的工夫都是 IO 操作所占用的,缩小 IO 次数是 SQL 优化中须要第一优先思考,当然,也是收效最显著的优化伎俩。
2、升高 CPU 计算
除了 IO 瓶颈之外,SQL 优化中须要思考的就是 CPU 运算量的优化了。order by,group by,distinct … 都是耗费 CPU 的小户(这些操作基本上都是 CPU 解决内存中的数据比拟运算)。当咱们的 IO 优化做到肯定阶段之后,升高 CPU 计算也就成为了咱们 SQL 优化的重要指标。
4.2 优化办法
一、监控剖析
1、硬件资源监控
关注的次要数据库服务器在 IO 和 CPU 方面的指标。
2、mysql 性能分析器
能够利用 mysql profiling(mysql 性能分析器)来优化 sql 语句,即查看 SQL 执行耗费系统资源的信息(须要开启能力利用该性能)。
3、慢查问剖析
通过慢日志查问能够晓得哪些 SQL 语句执行效率低下,那些 sql 语句应用的频率低等。
对 MySQL 查问语句的监控、剖析、优化是 MySQL 优化十分重要的一步。开启慢查问日志后,因为日志记录操作,在肯定水平上会占用 CPU 资源影响 mysql 的性能,然而能够阶段性开启来定位性能瓶颈。
二、扭转 SQL 执行打算
明确了优化指标之后,咱们须要确定达到咱们指标的办法。对于 SQL 语句来说,达到上述 2 个优化指标的办法其实只有一个,那就是扭转 SQL 的执行打算,让他尽量“少走弯路”,尽量通过各种“捷径”来找到咱们须要的数据,以达到“缩小 IO 次数”和“升高 CPU 计算”的指标。
应用 explain 命令查看 query 语句的性能:
EXPLAIN select * from tablename;## 查看执行打算中的 sql 性能
下面这是最简略的执行打算实例,来剖析一下下面的这几个字段。
1、id:id 次要是用来标识 sql 执行程序,如果没有子查问,一般来说 id 只有 1 个,执行程序也是从上到下。
2、select_type:每个 select 子句的类型,次要分成上面几种:
a:SIMPLE: 查问中不蕴含任何子查问或者 union b:PRIMARY: 查问中蕴含了任何简单的子局部,最外层的就会变成 PRIMARY c:SUBQUERY: 在 SELECT 或者 WHERE 列表中蕴含了子查问 d:DERIVED:在 FROM 中蕴含了子查问 e:UNION: 如果第二个 SELECT 呈现在 UNION 之后,则被标记为 UNION,如果 UNION 蕴含在 FROM 子句的子查问中,外层 SELECT 会被标记为:DERIVED f:UNION RESULT 从 UNION 表获取后果的 select
3、type:是指 MySQL 在表中找到所需行的形式,也就是拜访行的“类型”,从 a 开始,效率逐步回升:
a:all:全表扫描,效率最低 b:index:index 会依据索引树遍历 c:range: 索引范畴扫描,返回匹配值域的行。d:ref: 非唯一性索引扫描,返回匹配某个独自值的所有行。个别是指多列的惟一索引中的某一列。e:eq_ref: 唯一性索引扫描,表中只有一条记录与之匹配。f:const、system:次要针对查问中有常量的状况,如果后果只有一行会变成 system g:NULL: 不言而喻,既不走表,也不走索引
4、possible_keys
possible_keys 列预估了 mysql 可能为以后查问抉择的索引,这个字段是齐全独立于执行打算中输入的表的程序,意味着在理论查问中可能用不到这些索引。
如果该字段为空则意味着没有可应用的索引,这个时候你能够思考为 where 前面的字段建设索引。
5、key
这个字段示意了 mysql 实在应用的索引(如果为 NULL,则没有应用索引)。如果 mysql 优化过程中没有加索引,能够强制加 hint 应用索引。
6、key_len
索引长度字段顾名思义,示意了 mysql 查问中应用的索引的长度(最大可能长度),并非理论应用长度,实践上长度越短越好。key_len 是依据表定义计算而得的,不是通过表内检索出的。
7、ref
这个字段个别是指一些常量用于抉择过滤(显示索引的那一列被应用了,如果可能,是一个常量 const)。
8、rows
预估后果集的条数,可能不肯定齐全精确(依据表统计信息及索引选用状况,大抵估算出找到所需的记录所须要读取的行数)。
9、Extra
不适宜在其余字段中显示,然而非常重要的额定信息:
a:Using filesort:mysql 对数据应用一个内部的索引排序,而不是依照表内的索引进行排序读取。也就是说 mysql 无奈利用索引实现的排序操作成为“文件排序”。b:Using temporary:应用长期表保留两头后果,也就是说 mysql 在对查问后果排序时应用了长期表,常见于 order by 和 group by。c:Using index:示意相应的 select 操作中应用了笼罩索引(Covering Index),防止了拜访表的数据行,效率高(不要应用 select );如果同时呈现 Using where,表明索引被用来执行索引键值的查找;如果没有同时呈现 Using where,表明索引用来读取数据而非执行查找动作。d:Using join buffer:应用了链接缓存。e:eq_ref: 唯一性索引扫描,表中只有一条记录与之匹配。f:Impossible WHERE:where 子句的值总是 false,不能用来获取任何元祖。g:select tables optimized away:在没有 group by 子句的状况下,基于索引优化 MIN/MAX 操作或者对于 MyISAM 存储引擎优化 COUNT()操作,不用等到执行阶段在进行计算,查问执行打算生成的阶段即可实现优化。H:distinct:优化 distinct 操作,在找到第一个匹配的元祖后即进行找同样值得动作。
4.3 常见误区
1、count(1)和 count(primary_key)优于 count(*)
很多人为了统计记录条数,就应用 count(1) 和 count(primary_key) 而不是 count(),他们认为这样性能更好,其实这是一个误区。对于有些场景,这样做可能性能会更差,应为数据库对 count() 计数操作做了一些特地的优化。
2、count(column)和 count(*)是一样的
这个误区甚至在很多的资深工程师或者是 DBA 中都普遍存在,很多人都会认为这是天经地义的。实际上,count(column) 和 count(*) 是一个齐全不一样的操作,所代表的意义也齐全不一样。
count(column) 是示意后果集中有多少个 column 字段不为空的记录;
count(*) 是示意整个后果集有多少条记录。
3、select a,bfrom …比 selecta,b,c from …能够让数据库拜访更少的数据量
这个误区次要存在于大量的开发人员中,次要起因是对数据库的存储原理不是太理解。
实际上,大多数关系型数据库都是依照行(row)的形式存储,而数据存取操作都是以一个固定大小的 IO 单元(被称作 block 或者 page)为单位,个别为 4KB,8KB…大多数时候,每个 IO 单元中存储了多行,每行都是存储了该行的所有字段(lob 等非凡类型字段除外)。
所以,咱们是取一个字段还是多个字段,实际上数据库在表中须要拜访的数据量其实是一样的。
当然,也有例外情况,那就是咱们的这个查问在索引中就能够实现,也就是说当只取 a,b 两个字段的时候,不须要回表,而 c 这个字段不在应用的索引中,须要回表获得其数据。在这样的状况下,二者的 IO 量会有较大差别。
4、order by 肯定须要排序操作
咱们晓得索引数据实际上是有序的,如果咱们的须要的数据和某个索引的程序统一,而且咱们的查问又通过这个索引来执行,那么数据库个别会省略排序操作,而间接将数据返回,因为数据库晓得数据曾经满足咱们的排序需要了。
实际上,利用索引来优化有排序需要的 SQL,是一个十分重要的优化伎俩
5、执行打算中有 filesort 就会进行磁盘文件排序
有这个误区其实并不能怪咱们,而是因为 MySQL 开发者在用词方面的问题。filesort 是咱们在应用 explain 命令查看一条 SQL 的执行打算的时候可能会看到在“Extra”一列显示的信息。
实际上,只有一条 SQL 语句须要进行排序操作,都会显示“Using filesort”,这并不示意就会有文件排序操作。
4.4 根本准则
1、尽量少 join
MySQL 的劣势在于简略,但这在某些方面其实也是其劣势。MySQL 优化器效率高,然而因为其统计信息的量无限,优化器工作过程呈现偏差的可能性也就更多。对于简单的多表 Join,一方面因为其优化器受限,再者在 Join 这方面所下的功夫还不够,所以性能体现离 Oracle 等关系型数据库前辈还是有肯定间隔。但如果是简略的单表查问,这一差距就会极小甚至在有些场景下要优于这些数据库前辈。
2、尽量少排序
排序操作会耗费较多的 CPU 资源,所以缩小排序能够在缓存命中率低等 IO 能力足够的场景下会较大影响 SQL 的响应工夫。
对于 MySQL 来说,缩小排序有多种方法,比方:
o 下面误区中提到的通过利用索引来排序的形式进行优化 o 缩小参加排序的记录条数 o 非必要不对数据进行排序 o 防止应用消耗资源的操作,带有 DISTINCT,UNION,MINUS,INTERSECT,ORDERBY 的 SQL 语句会启动 SQL 引擎 执行,消耗资源的排序 (SORT) 性能. DISTINCT 须要一次排序操作, 而其余的至多须要执行两次排序 o …
3、尽量避免 select *
很多人看到这一点后感觉比拟难了解,下面不是在误区中刚刚说 select 子句中字段的多少并不会影响到读取的数据吗?
是的,大多数时候并不会影响到 IO 量,然而当咱们还存在 order by 操作的时候,select 子句中的字段多少会在很大水平上影响到咱们的排序效率。
此外,下面误区中不是也说了,只是大多数时候是不会影响到 IO 量,当咱们的查问后果仅仅只须要在索引中就能找到的时候,还是会极大缩小 IO 量的。
4、尽量用 join 代替子查问
尽管 Join 性能并不佳,然而和 MySQL 的子查问比起来还是有十分大的性能劣势。MySQL 的子查问执行打算始终存在较大的问题,尽管这个问题曾经存在多年,然而到目前曾经公布的所有稳固版本中都普遍存在,始终没有太大改善。尽管官网也在很早就抵赖这一问题,并且承诺尽快解决,然而至多到目前为止咱们还没有看到哪一个版本较好的解决了这一问题。
5、尽量少 or
当 where 子句中存在多个条件以“或”并存的时候,MySQL 的优化器并没有很好的解决其执行打算优化问题,再加上 MySQL 特有的 SQL 与 Storage 分层架构形式,造成了其性能比拟低下,很多时候应用 union all 或者是 union(必要的时候)的形式来代替“or”会失去更好的成果。
6、尽量用 union all 代替 union
union 和 union all 的差别次要是前者须要将两个(或者多个)后果汇合并后再进行唯一性过滤操作,这就会波及到排序,减少大量的 CPU 运算,加大资源耗费及提早。所以当咱们能够确认不可能呈现反复后果集或者不在乎反复后果集的时候,尽量应用 union all 而不是 union。
7、尽量早过滤
这一优化策略其实最常见于索引的优化设计中(将过滤性更好的字段放得更靠前)。在 SQL 编写中同样能够应用这一准则来优化一些 Join 的 SQL。比方咱们在多个表进行分页数据查问的时候,咱们最好是可能在一个表上先过滤好数据分好页,而后再用分好页的后果集与另外的表 Join,这样能够尽可能多的缩小不必要的 IO 操作,大大节俭 IO 操作所耗费的工夫。
8、防止类型转换
这里所说的“类型转换”是指 where 子句中呈现 column 字段的类型和传入的参数类型不统一的时候产生的类型转换:
o 人为在 column_name 上通过转换函数进行转换
间接导致 MySQL(实际上其余数据库也会有同样的问题)无奈应用索引,如果非要转换,应该在传入的参数上进行转换
o 由数据库本人进行转换
如果咱们传入的数据类型和字段类型不统一,同时咱们又没有做任何类型转换解决,MySQL 可能会本人对咱们的数据进行类型转换操作,也可能不进行解决而交由存储引擎去解决,这样一来,就会呈现索引无奈应用的状况而造成执行打算问题。
SELECT emp.ename, emp.job FROM emp WHERE emp.empno = 7369;
不要应用:SELECT emp.ename, emp.job FROM emp WHEREemp.empno = ‘7369’
9、能用 DISTINCT 的就不必 GROUP BY
group by 操作特地慢,比方:
SELECT OrderID FROM DetailsWHERE UnitPrice > 10 GROUP BY OrderID
可改为:
SELECT DISTINCT OrderID FROMDetails WHERE UnitPrice > 10
10、尽量不要用 SELECT INTO 语句
SELECT INOT 语句会导致表锁定,阻止其余用户拜访该表
11、优先优化高并发的 SQL,而不是执行频率低某些“大”SQL
对于破坏性来说,高并发的 SQL 总是会比低频率的来得大,因为高并发的 SQL 一旦呈现问题,甚至不会给咱们任何喘息的机会就会将零碎压垮。而对于一些尽管须要耗费大量 IO 而且响应很慢的 SQL,因为频率低,即便遇到,最多就是让整个零碎响应慢一点,但至多可能撑一会儿,让咱们有缓冲的机会。
12、从全局登程优化,而不是全面调整
SQL 优化不能是独自针对某一个进行,而应充分考虑零碎中所有的 SQL,尤其是在通过调整索引优化 SQL 的执行打算的时候,千万不能顾此失彼,千里之堤; 溃于蚁穴。
13、尽可能对每一条运行在数据库中的 SQL 进行 explain
优化 SQL,须要做到成竹在胸,晓得 SQL 的执行打算能力判断是否有优化余地,能力判断是否存在执行打算问题。在对数据库中运行的 SQL 进行了一段时间的优化之后,很显著的问题 SQL 可能曾经很少了,大多都须要去挖掘,这时候就须要进行大量的 explain 操作收集执行打算,并判断是否须要进行优化。
14、其余优化形式
(1)适当应用视图减速查问:把表的一个子集进行排序并创立视图,有时能减速查问(特地是要被屡次执行的查问)。它有助于防止多重排序操作,而且在其余方面还能简化优化器的工作。视图中的行要比主表中的行少,而且物理程序就是所要求的程序,缩小了磁盘 I /O,所以查问工作量能够失去大幅缩小。
(2)算法优化:尽量避免应用游标,因为游标的效率较差,如果游标操作的数据超过 1 万行,那么就应该思考改写。. 应用基于游标的办法或长期表办法之前,应先寻找基于集的解决方案来解决问题,基于集的办法通常更无效。与长期表一样,游标并不是不可应用。对小型数据集应用 FAST_FORWARD 游标通常要优于其余逐行解决办法,尤其是在必须援用几个表能力取得所需的数据时。在后果集中包含“共计”的例程通常要比应用游标执行的速度快。如果开发工夫容许,基于游标的办法和基于集的办法都能够尝试一下,看哪一种办法的成果更好。
游标提供了对特定汇合中逐行扫描的伎俩,个别应用游标逐行遍历数据,依据取出的数据不同条件进行不同的操作。尤其对多表和大表定义的游标(大的数据汇合)循环很容易使程序进入一个漫长的期待甚至死机。
在有些场合,有时也非得应用游标,此时也可思考将符合条件的数据行转入长期表中,再对长期表定义游标进行操作,可时性能失去明显提高。
(3)封装存储过程:经编译和优化后存储在数据库服务器中,运行效率高,能够升高客户机和服务器之间的通信量,有利于集中控制,易于保护。
5 表构造优化
因为 MySQL 数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的形式,也就是说,如果咱们每条记录所占用的空间量减小,就会使每个 page 中可寄存的数据行数增大,那么每次 IO 可拜访的行数也就增多了。反过来说,解决雷同行数的数据,须要拜访的 page 就会缩小,也就是 IO 操作次数升高,间接晋升性能。此外,因为咱们的内存是无限的,减少每个 page 中寄存的数据行数,就等于减少每个内存块的缓存数据量,同时还会晋升内存替换中数据命中的几率,也就是缓存命中率。
一、数据类型抉择
数据库操作中最为耗时的操作就是 IO 解决,大部分数据库操作 90% 以上的工夫都花在了 IO 读写下面。所以尽可能减少 IO 读写量,能够在很大水平上进步数据库操作的性能。咱们无奈扭转数据库中须要存储的数据,然而咱们能够在这些数据的存储形式方面花一些心理。准则是:数据行的长度不要超过 8020 字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,升高查问效率;字段的长度在最大限度的满足可能的须要的前提下,应该尽可能的设得短一些,这样能够进步查问的效率,而且在建设索引的时候也能够缩小资源的耗费。
上面的这些对于字段类型的优化倡议次要实用于记录条数较多,数据量较大的场景,因为精细化的数据类型设置可能带来保护老本的进步,适度优化也可能会带来其余的问题:
1、数字类型:非万不得已不要应用 DOUBLE,不仅仅只是存储长度的问题,同时还会存在精确性的问题。同样,固定精度的小数,也不倡议应用 DECIMAL,倡议乘以固定倍数转换成整数存储,能够大大节俭存储空间,且不会带来任何附加保护老本。对于整数的存储,在数据量较大的状况下,倡议辨别开 TINYINT / INT / BIGINT 的抉择,因为三者所占用的存储空间也有很大的差异,能确定不会应用正数的字段,倡议增加 unsigned 定义。当然,如果数据量较小的数据库,也能够不必严格辨别三个整数类型。
可能用数字类型的字段尽量抉择数字类型而不必字符串类型的(电话号码),这会升高查问和连贯的性能,并会减少存储开销。这是因为引擎在解决查问和连贯会一一比拟字符串中每一个字符,而对于数字型而言只须要比拟一次就够了。
2、字符类型:非万不得已不要应用 TEXT 数据类型,其解决形式决定了他的性能要低于 char 或者是 varchar 类型的解决。定长字段,倡议应用 CHAR 类型(char 查问快,然而耗存储空间,可用于用户名、明码等长度变动不大的字段),不定长字段尽量应用 VARCHAR(varchar 查问绝对慢一些然而节俭存储空间,可用于评论等长度变动大的字段),且仅仅设定适当的最大长度,而不是十分随便的给一个很大的最大长度限定,因为不同的长度范畴,MySQL 也会有不一样的存储解决。
3、工夫类型:尽量应用 TIMESTAMP 类型,因为其存储空间只须要 DATETIME 类型的一半。对于只须要准确到某一天的数据类型,倡议应用 DATE 类型,因为他的存储空间只须要 3 个字节,比 TIMESTAMP 还少。不倡议通过 INT 类型类存储一个 unix timestamp 的值,因为这太不直观,会给保护带来不必要的麻烦,同时还不会带来任何益处。
4、ENUM &SET:对于状态字段,能够尝试应用 ENUM 来寄存,因为能够极大的升高存储空间,而且即便须要减少新的类型,只有减少于开端,批改构造也不须要重建表数据。如果是寄存可事后定义的属性数据呢?能够尝试应用 SET 类型,即便存在多种属性,同样能够熟能生巧,同时还能够节俭不小的存储空间。
5、LOB 类型:强烈拥护在数据库中寄存 LOB 类型数据,尽管数据库提供了这样的性能,但这不是他所善于的,咱们更应该让适合的工具做他善于的事件,能力将其施展到极致。在数据库中存储 LOB 数据就像让一个多年前在学校学过一点 Java 的营销业余人员来写 Java 代码一样。
二、字符编码
字符集间接决定了数据在 MySQL 中的存储编码方式,因为同样的内容应用不同字符集示意所占用的空间大小会有较大的差别,所以通过应用适合的字符集,能够帮忙咱们尽可能减少数据量,进而缩小 IO 操作次数。
1、纯拉丁字符能示意的内容,没必要抉择 latin1 之外的其余字符编码,因为这会节俭大量的存储空间;
2、如果咱们能够确定不须要寄存多种语言,就没必要非得应用 UTF8 或者其余 UNICODE 字符类型,这回造成大量的存储空间节约;
3、MySQL 的数据类型能够准确到字段,所以当咱们须要大型数据库中寄存多字节数据的时候,能够通过对不同表不同字段应用不同的数据类型来较大水平减小数据存储量,进而升高 IO 操作次数并进步缓存命中率。
三、适当拆分
有些时候,咱们可能会心愿将一个残缺的对象对应于一张数据库表,这对于利用程序开发来说是很有好的,然而有些时候可能会在性能上带来较大的问题。当咱们的表中存在相似于 TEXT 或者是很大的 VARCHAR 类型的大字段的时候,如果咱们大部分拜访这张表的时候都不须要这个字段,咱们就该义无反顾的将其拆分到另外的独立表中,以缩小罕用数据所占用的存储空间。这样做的一个显著益处就是每个数据块中能够存储的数据条数能够大大增加,既缩小物理 IO 次数,也能大大提高内存中的缓存命中率。
下面几点的优化都是为了缩小每条记录的存储空间大小,让每个数据库中可能存储更多的记录条数,以达到缩小 IO 操作次数,进步缓存命中率。上面这个优化倡议可能很多开发人员都会感觉不太了解,因为这是典型的反范式设计,而且也和下面的几点优化倡议的指标相违反。
四、适度冗余
为什么咱们要冗余?这不是减少了每条数据的大小,缩小了每个数据块可寄存记录条数吗?的确,这样做是会增大每条记录的大小,升高每条记录中可存放数据的条数,然而在有些场景下咱们依然还是不得不这样做:
被频繁援用且只能通过 Join 2 张(或者更多)大表的形式能力失去的独立小字段
这样的场景因为每次 Join 仅仅只是为了获得某个小字段的值,Join 到的记录又大,会造成大量不必要的 IO,齐全能够通过空间换取工夫的形式来优化。不过,冗余的同时须要确保数据的一致性不会受到毁坏,确保更新的同时冗余字段也被更新
五、尽量应用 NOT NULL
NULL 类型比拟非凡,SQL 难优化。尽管 MySQL NULL 类型和 Oracle 的 NULL 有差别,会进入索引中,但如果是一个组合索引,那么这个 NULL 类型的字段会极大影响整个索引的效率。此外,NULL 在索引中的解决也是非凡的,也会占用额定的寄存空间。
很多人感觉 NULL 会节俭一些空间,所以尽量让 NULL 来达到节俭 IO 的目标,然而大部分时候这会事与愿违,尽管空间上可能的确有肯定节俭,倒是带来了很多其余的优化问题,岂但没有将 IO 量省下来,反而加大了 SQL 的 IO 量。所以尽量确保 DEFAULT 值不是 NULL,也是一个很好的表结构设计优化习惯。
6 索引优化
大家都晓得索引对于数据拜访的性能有十分要害的作用,都晓得索引能够进步数据拜访效率。为什么索引能进步数据拜访性能?他会不会有“副作用”?是不是索引创立越多,性能就越好?到底该如何设计索引,能力最大限度的施展其效力?这篇文章次要是带着下面这几个问题来做一个简要的剖析,同时排除了业务场景所带来的特殊性,请不要纠结业务场景的影响。
索引为什么能进步数据拜访性能?
很多人只晓得索引可能进步数据库的性能,但并不是特地理解其原理,其实咱们能够用一个生存中的示例来了解。咱们让一位不太懂计算机的敌人去图书馆确认一本叫做《MySQL 性能调优与架构设计》的书是否在藏,这样对他说:“请帮我借一本计算机类的数据库书籍,是属于 MySQL 数据库领域的,叫做《MySQL 性能调优与架构设计》”。敌人会依据所属类别,返回寄存“计算机”书籍区域的书架,而后再寻找“数据库”类寄存地位,再找到一堆讲述“MySQL”的书籍,最初可能发现指标在藏(也可能曾经借出不在书架上)。在这个过程中:“计算机”->“数据库”->“MySQL”->“在藏”->《MySQL 性能调优与架构设计》其实就是一个“依据索引查找数据”的典型案例,“计算机”->“数据库”->“MySQL”->“在藏”就是敌人查找书籍的索引。假如没有这个索引,那查找这本书的过程会变成怎么呢?敌人只能从图书馆入口一个书架一个书架的“遍历”,直到找到《MySQL 性能调优与架构设计》这本书为止。如果侥幸,可能在第一个书架就找到。但如果可怜呢,那就惨了,可能要将整个图书馆所有的书架都找一遍能力找到咱们想要的这本书。注:这个例子中的“索引”是记录在咱们大脑中的,实际上,每个图书馆都会有一个十分全的理论存在的索引零碎(大多位于入口显眼处),由很多个贴上了显著标签的小抽屉形成。这个索引零碎中寄存着十分齐全详尽的索引数据,标识出咱们须要查找的“指标”在某个区域的某个书架上。而且每当有新的书籍入库,旧的书籍销毁以及书籍信息批改,都须要对索引零碎进行及时的修改。
上面咱们通过下面这个生存中的小示例,来剖析一下索引,看看能的出哪些论断?
一、索引有哪些“副作用”?
1、图书的变更(增,删,改)都须要订正索引,索引存在额定的保护老本;
2、查找翻阅索引零碎须要耗费工夫,索引存在额定的拜访老本;
3、这个索引零碎须要一个中央来寄存,索引存在额定的空间老本。
二、索引是不是越多越好?
1、如果咱们的这个图书馆只是一个进出中转站,外面的新书进来后很快就会转发去其余图书馆而从这个馆藏中“革除”,那咱们的索引就只会一直的批改,而很少会被用来查找图书。
所以,对于相似于这样的存在大量和频繁更新的数据,索引的保护老本会十分高,如果其检索需要很少,而且对检索效率并没有十分高的要求的时候,咱们并不倡议创立索引,或者是尽量减少索引。
2、如果咱们的书籍量少到只有几本或者就只有一个书架,索引并不会带来什么作用,甚至可能还会节约一些查找索引所破费的工夫。
所以,对于数据量极小到通过索引检索还不如间接遍历来得快的数据,也并不适宜应用索引。
3、如果咱们的图书馆只有一个 10 平方的面积,当初连放书架都曾经十分拥挤,而且馆藏还在一直减少,咱们还能思考创立索引吗?
所以,当咱们连存储根底数据的空间都顾此失彼的时候,咱们也应该尽量减少低效或者是去除索引。
三、索引该如何设计才高效?
1、如果咱们仅仅只是这样通知对方的:“帮我确认一本数据库类别的讲述 MySQL 的叫做《MySQL 性能调优与架构设计》的书是否在藏”,后果又会如何呢?敌人只能一个大类区域一个大类区域的去寻找“数据库”类别,而后再找到“MySQL”领域,再看到咱们所需是否在藏。因为咱们少说了一个“计算机类”,敌人就必须到每一个大类去寻找。
所以,咱们应该尽量让查找条件尽可能多的在索引中,尽可能通过索引实现所有过滤,回表只是取出额定的数据字段。
2、如果咱们是这样说的:“帮我确认一本讲述 MySQL 的数据库领域的计算机丛书,叫做《MySQL 性能调优与架构设计》,看是否在藏”。如果这位敌人并不知道计算机是一个大类,也不晓得数据库属于计算机大类,那这位敌人就喜剧了。首先他得遍历每个类别确认“MySQL”存在于哪些类别中,而后从蕴含“MySQL”书籍中再看有哪些是“数据库”领域的(有可能局部是讲述 PHP 或者其余开发语言的),而后再排除非计算机类的(尽管可能并没有必要),而后能力确认。
所以,字段的程序对组合索引效率有至关重要的作用,过滤成果越好的字段须要更靠前。
3、如果咱们还有这样一个需要(尽管根本不可能):“帮我将图书馆中所有的计算机图书借来”。敌人如果通过索引来找,每次都到索引柜找到计算机书籍所在的区域,而后从书架上搬下一格(假如只能以一格为单位从书架上取下,类比数据库中以 block/page 为单位读取),取出第一本,而后再从索引柜找到计算机图书所在区域,再搬下一格,取出一本… 如此往返直至取完所有的书。如果他不通过索引来找又会怎么呢?他须要从第一个书架始终往后找,当找到计算机的书,搬下一格,取出所有计算机的书,再往后,直至所有书架全副看一遍。在这个过程中,如果计算机类书籍较多,通过索引来取所破费的工夫很可能要大于间接遍历,因为一直往返的索引翻阅所耗费的工夫会十分长。(延长浏览:能够参照 Oracle 的索引优化进行解读,索引扫描还是全表扫描(Index Scan Or Full Table Scan))
所以,当咱们须要读取的数据量占整个数据量的比例较大抑或者说索引的过滤成果并不是太好的时候,应用索引并不一定优于全表扫描。
4、如果咱们的敌人不晓得“数据库”这个类别能够属于“计算机”这个大类,抑或者图书馆的索引零碎中这两个类别属性并没有关联关系,又会怎么呢?也就是说,敌人失去的是 2 个独立的索引,一个是告知“计算机”这个大类所在的区域,一个是“数据库”这个小类所在的区域(很可能是多个区域),那么他只能二者选其一来搜寻我的需要。即便敌人能够别离通过 2 个索引检索而后本人在脑中取交加再找,那这样的效率理论过程中也会比拟低下。
所以,在理论应用过程中,一次数据拜访个别只能利用到 1 个索引,这一点在索引创立过程中肯定要留神,不是说一条 SQL 语句中 Where 子句外面每个条件都有索引能对应上就能够了。
5、最初总结一下法令:不要在建设的索引的数据列上进行下列操作:
◆防止对索引字段进行计算操作 ◆防止在索引字段上应用 not,like‘%L’,!=,<>,in,or 连贯 ◆防止在索引列上应用 IS NULL 和 IS NOT NULL ◆防止在索引列上呈现数据类型转换 ◆防止在索引字段上应用函数 ◆防止建设索引的列中应用空值。
7 架构优化
一、分布式和集群化
1、负载平衡
负载平衡集群是由一组互相独立的计算机系统形成,通过惯例网络或专用网络进行连贯,由路由器连接在一起,各节点相互协作、独特负载、平衡压力,对客户端来说,整个群集能够视为一台具备超高性能的独立服务器。MySQL 个别部署的是高可用性负载平衡集群,具备读写拆散,个别只对读进行负载平衡。
2、读写拆散
读写拆散简略的说是把对数据库读和写的操作离开对应不同的数据库服务器,这样能无效地加重数据库压力,也能加重 io 压力。主数据库提供写操作,从数据库提供读操作,其实在很多零碎中,次要是读的操作。当主数据库进行写操作时,数据要同步到从的数据库,这样能力无效保障数据库完整性。
3、数据切分
通过某种特定的条件,将寄存在同一个数据库中的数据扩散寄存到多个数据库上,实现散布存储,通过路由规定路由拜访特定的数据库,这样一来每次拜访面对的就不是单台服务器了,而是 N 台服务器,这样就能够升高单台机器的负载压力。数据切分个别包含垂直切分和程度切分。
数据的垂直切分,也能够称之为纵向切分。将数据库设想成为由很多个一大块一大块的“数据块”(表)组成,咱们垂直的将这些“数据块”切开,而后将他们扩散到多台数据库主机下面。这样的切分办法就是一个垂直(纵向)的数据切分。
数据的垂直切分基本上能够简略的了解为依照表、依照模块来切分数据,而程度切分就不再是依照表或者是功能模块来切分了。一般来说,简略的程度切分次要是将某个拜访极其平庸的表再依照某个字段的某种规定来扩散到多个表之中,每个表中蕴含一部分数据。
二、Cache 与 Search 的利用
通过引入 Cache(Redis、Memcached),缩小数据库的拜访,升高磁盘的 IO,减少性能。
通过引入 Search(Lucene、Solr、ElasticSearch),利用搜索引擎高效的全文索引和分词算法,以及高效的数据检索实现,来解决数据库和传统的 Cache 软件齐全无奈解决的全文含糊搜寻、分类统计查问等性能。