定量遍历次数
在编写一条查询语句的时候,可以依据你要查询数据表的数据总量,估算一下这条查询大致需要遍历多少行数据:
如果遍历行数在百万以内的,只要不是每秒钟都要执行几十上百次的频繁查询,可以认为是安全的。
遍历数据行数在几百万的,查询时间最少也要几秒钟,你就要仔细考虑有没有优化的办法。
遍历行数达到千万量级和以上的,我只能告诉你,这种查询就不应该出现在你的系统中。当然我们这里说的都是在线交易系统,离线分析类系统另说。
遍历行数在千万左右,是 MySQL 查询的一个坎儿。MySQL 中单个表数据量,也要尽量控制在一千万条以下,最多不要超过二三千万这个量级。原因也很好理解,对一个千万级别的表执行查询,加上几个 WHERE 条件过滤一下,符合条件的数据最多可能在几十万或者百万量级,这还可以接受。但如果再和其他的表做一个联合查询,遍历的数据量很可能就超过千万级别了。所以,每个表的数据量最好小于千万级别。
避免全表索引
绝大多数情况下,我们编写的查询语句,都应该使用索引,避免去遍历整张表,也就是通常说的,避免全表扫描。你在每次开发新功能,需要给数据库增加一个新的查询时,都要评估一下,是不是有索引可以支撑新的查询语句,如果有必要的话,需要新建索引来支持新增的查询。
增加索引付出的代价是,会降低数据插入、删除和更新的性能。这个也很好理解,增加了索引,在数据变化的时候,不仅要变更数据表里的数据,还要去变更每个索引。所以,对于更新频繁并且对更新性能要求较高的表,可以尽量少建索引。而对于查询较多更新较少的表,可以根据查询的业务逻辑,适当多建一些索引。
分析 SQL 执行计划
在 MySQL 中使用执行计划也非常简单,只要在你的 SQL 语句前面加上 EXPLAIN 关键字,然后执行这个查询语句就可以了。
比如有一个用户表,包含用户 ID、姓名、部门编号和状态这几个字段:
我们希望查询某个二级部门下的所有人,查询条件就是,部门代号以 00028 开头的所有人。下面这两个 SQL,他们的查询结果是一样的,都满足要求,但是,哪个查询性能更好呢?
SELECT * FROM user WHERE left(department_code, 5) = '00028';SELECT * FROM user WHERE department_code LIKE '00028%';
我们分别查看一下这两个 SQL 的执行计划:
row 列:
MySQL 预估执行这个 SQL 可能会遍历的数据行数。第一个 SQL 遍历了四千多行,这就是整个 User 表的数据条数;第二个 SQL 只有 8 行,这 8 行其实就是符合条件的 8 条记录。显然第二个 SQL 查询性能要远远好于第一个 SQL。
type 列:
表示这个查询的访问类型。ALL 代表全表扫描,这是最差的情况。range 代表使用了索引,在索引中进行范围查找,因为第二个 SQL 语句的 WHERE 中有一个 LIKE 的查询条件。如果直接命中索引,type 这一列显示的是 index。如果使用了索引,可以在 key 这一列中看到,实际上使用了哪个索引。
总结
在开发阶段,衡量一个 SQL 查询语句查询性能的手段是,估计执行 SQL 时需要遍历的数据行数。遍历行数在百万以内,可以认为是安全的 SQL,百万到千万这个量级则需要仔细评估和优化,千万级别以上则是非常危险的。为了减少慢 SQL 的可能性,每个数据表的行数最好控制在千万以内。 索引可以显著减少查询遍历数据的数量,所以提升 SQL 查询性能最有效的方式就是,让查询尽可能多的命中索引,但索引也是一把双刃剑,它在提升查询性能的同时,也会降低数据更新的性能。 对于复杂的查询,最好使用 SQL 执行计划,事先对查询做一个分析。在 SQL 执行计划的结果中,可以看到查询预估的遍历行数,命中了哪些索引。执行计划也可以很好地帮助你优化你的查询语句。