说到系统性能优化涉及的内容就比较多了,从业务需求到系统架构设计再到实现过程中的中间件、存储层等等的设计,这些都是需要充分规划的。
涉及存储层,从数据库选型,数据库设计,索引规划、查询方式,任一环节都可能引入性能风险。
本文主要从数据库SQL查询优化的角度优化查询性能,数据库选型是MySQL。
SQL查询优化
应尽量避免在 WHERE 子句中使用 != 或 <> 操作符,否则将导致引擎放弃使用索引而进行全表扫描。MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些方式的LIKE('a%'),如下。
WHERE 子句中使用 LIKE进行模糊查询时,在关键词前加%或者前后都加%号都无法使用索引,而进行全表扫描,如下。
应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,否则将导致引擎放弃使用索引而进行全表扫描,创建表时NULL是默认值,但大多数时候应该使用NOT NULL,或者使用一个默认值,如 0 作为默认值,如下。
应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。使用 OR 的字句可以分解成多个查询,并且通过UNION 连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION ALL执行的效率更高,如下。
应尽量避免在 WHERE 子句中使用 IN 和 NOT IN ,否则将导致全表扫描,对于连续的数值,能用 BETWEEN AND 尽量避免使用 IN。一般,用 EXISTS 代替 IN 。若需要使用 IN,在 IN 后面值的列表中,按照值的分布数量降序排列,减少判断的次数,如下。
使用BETWEEN AND 替换 IN
使用EXISTS 替代IN,用NOT EXISTS 替代 NOT IN
使用LEFT JOIN 替换 IN
如上,我们使用了如下方式优化了IN 和 NOT IN:
- 使用between 替换 in
- 使用exists替代in、用not exists替代 not in
- 使用left join 替换 in
应尽量避免在 WHERE 子句中对 “=” 左边的字段进行函数、算术运算及其他表达式运算,可以将表达式运算移至“=”右边,否则将导致引擎放弃使用索引而进行全表扫描,如下。
如果在 WHERE 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时。它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项,可以改为强制查询使用索引,如下。
避免使用 select * from table,用具体的字段列表代替 * ,避免返回用不到的任何字段,如下。
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引(遵循最左前缀原则),否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
最后,现代计算机科学的鼻祖 Donald Knuth曾提到“过早的优化是万恶之源……”。我想有性能风险意识 不等同于 需要过早或者过度优化。