二、SQL语句性能优化常用策略1、 为 WHERE 及 ORDER BY 涉及的列上建立索引对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引。2、where中使用默认值代替null应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,创建表时 NULL 是默认值,但大多数时候应该使用 NOT NULL,或者使用一个特殊的值,如 0,-1 作为默认值。为啥建议where中使用默认值代替null,四个原因:(1)并不是说使用了is null或者 is not null就会不走索引了,这个跟mysql版本以及查询成本都有关;(2)如果mysql优化器发现,走索引比不走索引成本还要高,就会放弃索引,这些条件 !=,<>,is null,is not null经常被认为让索引失效;(3)其实是因为一般情况下,查询的成本高,优化器自动放弃索引的;(4)如果把null值,换成默认值,很多时候让走索引成为可能,同时,表达意思也相对清晰一点。3、慎用 != 或 <> 操作符MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE。所以:应尽量避免在 WHERE 子句中使用 != 或 <> 操作符, 会导致全表扫描。4、慎用 OR 来连接条件使用or可能会使索引失效,从而全表扫描;应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,可以使用 UNION 合并查询:
select id from t where num=10
union all
select id from t where num=20
一个关键的问题是否用到索引。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用 UNION all 执行的效率更高。多个 OR 的字句没有用到索引,改写成 UNION 的形式再试图与索引匹配。5、慎用 IN 和 NOT ININ 和 NOT IN 要慎用,否则会导致全表扫描。对于连续的数值,能用 BETWEEN 就不要用 IN:select id from t where num between 1 and 3。6、慎用 左模糊like ‘%…’ 模糊查询,程序员最喜欢的就是使用like,like很可能让索引失效。比如:
select id from t where name like‘%abc%’
select id from t where name like‘%abc’
而select id from t where name like‘abc%’才用到索引。
所以:首先尽量避免模糊查询,如果必须使用,不采用全模糊查询,也应尽量采用右模糊查询, 即like ‘…%’,是会使用索引的;左模糊like ‘%…’无法直接使用索引,但可以利用reverse + function index的形式,变化成 like ‘…%’;全模糊查询是无法优化的,一定要使用的话建议使用搜索引擎,比如 ElasticSearch。备注:如果一定要用左模糊like ‘%…’检索, 一般建议 ElasticSearch+Hbase架构。7、WHERE条件使用参数会导致全表扫描如下面语句将进行全表扫描:select id from t where num=@num因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。所以, 可以改为强制查询使用索引:select id from t with(index(索引名)) where num=@num8、用 EXISTS 代替 IN 是一个好的选择很多时候用exists 代替in 是一个好的选择:select num from a where num in(select num from b) 用下面的语句替换:select num from a where exists(select 1 from b where num=a.num)9、索引并不是越多越好索引固然可以提高相应的 SELECT 的效率,但同时也降低了 INSERT 及 UPDATE 的效。因为 INSERT 或 UPDATE 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。10、尽量使用数字型字段(1)因为引擎在处理查询和连接时会逐个比较字符串中每一个字符;(2)而对于数字型而言只需要比较一次就够了;(3)字符会降低查询和连接的性能,并会增加存储开销;所以:尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。11、尽可能地使用 varchar, nvarchar 代替 char, nchar(1)varchar变长字段按数据内容实际长度存储,存储空间小,可以节省存储空间;(2)char按声明大小存储,不足补空格;(3)其次对于查询来说,在一个相对较小的字段内搜索,效率更高;因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。12、查询SQL尽量不要使用select *,而是具体字段最好不要使用返回所有:select * from t ,用具体的字段列表代替 “*”,不要返回用不到的任何字段。select *的弊端:(1)增加很多不必要的消耗,比如CPU、IO、内存、网络带宽;(2)增加了使用覆盖索引的可能性;(3)增加了回表的可能性;(4)当表结构发生变化时,前端也需要更改;(5)查询效率低。13、将需要查询的结果预先计算好将需要查询的结果预先计算好放在表中,查询的时候再Select,而不是查询的时候进行计算。14、IN后出现最频繁的值放在最前面如果一定用IN,那么:在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数。15、尽量使用 EXISTS 代替 select count(1) 来判断是否存在记录count 函数只有在统计表中所有行数时使用,而且 count(1) 比 count(*) 更有效率。16、用批量插入或批量更新当有一批处理的插入或更新时,用批量插入或批量更新,绝不会一条条记录的去更新。(1)多条提交INSERT INTO user (id,username) VALUES(1,'xx'); INSERT INTO user (id,username) VALUES(2,'yy');(2)批量提交INSERT INTO user (id,username) VALUES(1,'xx'),(2,'yy'); 默认新增SQL有事务控制,导致每条都需要事务开启和事务提交,而批量处理是一次事务开启和提交,效率提升明显,达到一定量级,效果显著,平时看不出来。17、将不需要的记录在 GROUP BY 之前过滤掉提高 GROUP BY 语句的效率,可以通过将不需要的记录在 GROUP BY 之前过滤掉。下面两个查询返回相同结果,但第二个明显就快了许多。低效:
SELECT JOB, AVG(SAL)
FROM EMP
GROUP BY JOB
HAVING JOB = 'PRESIDENT'
OR JOB = 'MANAGER'
高效:SELECT JOB, AVG(SAL) FROM EMP WHERE JOB = 'PRESIDENT' OR JOB = 'MANAGER' GROUP BY JOB18、避免死锁在你的存储过程和触发器中访问同一个表时总是以相同的顺序;事务应尽可能地缩短,在一个事务中应尽可能减少涉及到的数据量;永远不要在事务中等待用户输入。19、索引创建规则
表的主键、外键必须有索引;
数据量超过 300 的表应该有索引;
经常与其他表进行连接的表,在连接字段上应该建立索引;
经常出现在 WHERE 子句中的字段,特别是大表的字段,应该建立索引;
索引应该建在选择性高的字段上;
索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
复合索引的建立需要进行仔细分析,尽量考虑用单字段索引代替;
正确选择复合索引中的主列字段,一般是选择性较好的字段;
复合索引的几个字段是否经常同时以 AND 方式出现在 WHERE 子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
文章评论