MYSQl 全表扫描以及查询性能
-- 本文章仅用于学习,记录
一. Mysql在一些情况下全表检索比索引查询更快:
1.表格数据很少,使用全表检索会比使用索引检索更快。一般当表格总数据小于10行并且数据行的长度非常小的时候会使用全表检索;
2.在ON或者WHERE中,没有使用索引列作为查询条件;
3.使用了索引列与一个常量进行比较,但是mysql发现该索引列覆盖的数据太大,反而全表检索更快(一般当索引列覆盖的数据大于全表数据的30%时,会触发mysql使用全表检索);
4.使用了一个覆盖了大部分数据的索引列与另外表的列进行关联查询,此时mysql会使用全表检索。
二. 可能造成mysql 全表扫描的原因/查询性能差:
01. 使用null做为判断条件
如:select store from flowers where name = null;
建议在设计字段时尽量将字段的默认值设为0,改为select account where nickname = 0;
02. 左模糊查询Like %XXX%
如:select store from flowers where name like ‘%XXX%’ 或者 select store from flowers where name like ‘%XXX’
建议使用select store from flowers where name like ‘XXX%’,如果必须要用到做查询,需要评估对当前表全表扫描造成的后果;
03. 使用or做为连接条件
如:select store from flowers where id = 1 or id = 2;
建议使用union all,改为 select store from flowers where id = 1 union all select store from flowers where id = 2;
04. 使用in时(not in)
很多时候用 exists 代替 in 是一个好的选择。
如:select store from flowers where id in (1,2,3)
如果是连续数据,可以改为select store where id between 1 and 3;当数据较少时也可以参考union用法;
或者:select store from flowers where id in (select store_id from stores where id = 3 ),可以改为select store from flowers where id exsits (select store_id from stores where id = 3)
not in 可以对应 not exists;
05.使用!=或<>时
建议使用 <,<=,=,>,>=,between等;
06.对字段有操作时也会引起权标索引
如select amount from stores where salary * 0.8 = 1000 或者 select name from stores where sustring(title,1,3) = ‘daffodils’;
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
建议改为: ---> select id from t where num=100*2
不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
07.使用count(*)时
如select count(*) from member
建议改为:---> select count(1) from member;
08.使用参数做为查询条件时
如果在 where 子句中使用参数,也会导致全表扫描。
因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项
select id from t where num=@num
建议改为强制查询使用索引:---> select id from t with(index(索引名)) where num=@num
09.使用手动创建的临时表
建议使用联合(UNION / UNION ALL)来代替手动创建的临时表
当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候尽量使用union all而不是union ,因为union 和union all 的差异主要是前者需要将两个或者多个结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的cpu运算,增加资源消耗及延迟。
10.使用子查询
子查询内部执行计划器是这样执行的:先查外表再匹配内表,而不是先查表t2,当外表的数据很大时,查询速度会非常慢。
建议使用连接(JOIN) 来代替子查询,MYSQl 不需要在内存中创建临时表来完成这个逻辑上需要两个表的查询工作。另外:如果你的应用程序有很多JOIN 查询,你应该确认JOIN 的字段是被建立过索引的。这样MYSQL 内部会启动为你优化JOIN 的sql 语句的机制。而且这些被用来JOIN的字段,应该是相同的类型的。例如:如果你要把DECIMAL 字段和一个INT 字段JOIN在一起,MYSQL 就无法使用他们的索引。对于那些STRING类型,还需要有相同的字符集才行。
11.使用函数索引
比如:select * from t where year(d)>= 2016
由于mysql 不像oracle 那样支持函数索引,也会直接全表扫描。
应改为---> select * from t where d>= '2016-01-01';
12.分组统计使用排序
分组统计可以禁止排序,避免不必要的order by 排序
比如 select name,sum(amount) as total_score from t group by name order by total_score
应改为---> select name,sum(amount) as total_score from t group by name
应改为---> select name,sum(amount) as total_score from t group by name
13.更新 clustered 索引数据列
应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
14.数值信息的字段设计为字符型
尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
15.不合理的字段类型
尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
16.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
17.使用游标
尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
18.大事务操作
尽量避免大事务操作,提高系统并发能力。
19.客户端返回大数据量
尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。