Sql 调优总结
1前言
Sql 语句调优对应用性能非常重要,看了几篇文章,总结了一下数据库优化的方法。
2 数据库 Sql 优化
1 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
关于索引请看我的这篇文章
SELECT sid FROM Student WHERE Gradeid=1
执行时间:17.609s (多次执行,在17s左右徘徊)
优化后:给Gradeid字段添加索引后
执行时间为:11.377s(多次执行,在11s左右徘徊)
备注:我们一般在什么字段上建索引?
这是一个非常复杂的话题,需要对业务及数据充分分析后再能得出结果。主键及外键通常都要有索引,其它需要建索引的字段应满足以下条件:
a、字段出现在查询条件中,并且查询条件可以使用索引;
b、语句执行频率高,一天会有几千次以上;
c、通过字段条件可筛选的记录集很小,那数据筛选比例是多少才适合?
这个没有固定值,需要根据表数据量来评估,以下是经验公式,可用于快速评估:
小表(记录数小于10000行的表):筛选比例<10%;
大表:(筛选返回记录数)<(表总记录数*单条记录长度)/10000/16
单条记录长度≈字段平均内容长度之和+字段数*2
2 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。
select sid from Student where num is null
最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.
备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select sid from Student where num = 0
3 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则引擎可能放弃使用索引而进行全表扫描。
4 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描。(用union all 代替)
GRADE_ID字段有索引,QUESTION_TYPE没索引
SELECT sid FROM Student WHERE Gradeid=15 OR Gradetype=1
优化方案:
SELECT sid FROM Student WHERE Gradeid=15 union all SELECT sid FROM Student WHERE Gradetype=1
通过union all 方式,把有索引字段和非索引字段分开。索引字段就有效果了。
5 in 和 not in 也要慎用,否则会导致全表扫描
注:在mysql数据库中where 子句中对索引字段使用 in 和 not in操作符,引擎不会放弃使用索引。
注:在mysql数据库中where 子句中对不是索引字段使用 in 和 not in操作符,会导致全表扫描。
6 负向查询不能使用索引
select name from user where id not in (1,3,4);
应该修改为:
select name from user where id in (2,5,6);
7 前导模糊查询不能使用索引
如:
select name from user where name like '%zhangsan'
非前导则可以:
select name from user where name like 'zhangsan%'
建议可以考虑使用 Lucene 等全文索引工具来代替频繁的模糊查询。
8 数据区分不明显的不建议创建索引
如 user 表中的性别字段,可以明显区分的才建议创建索引,如身份证等字段。
9 在字段上进行计算不能命中索引
select name from user where FROM_UNIXTIME(create_time) < CURDATE();
应该修改为:
select name from user where create_time < FROM_UNIXTIME(CURDATE());
10 最左前缀问题
如果给 user 表中的 username pwd 字段创建了复合索引那么使用以下SQL 都是可以命中索引:
但是使用
select username from user where pwd ='axsedf1sd'
是不能命中索引的。
11 如果明确知道只有一条记录返回
select name from user where username='zhangsan' limit 1
可以提高效率,可以让数据库停止游标移动。
12 不要让数据库帮我们做强制类型转换
select name from user where telno=18722222222
这样虽然可以查出数据,但是会导致全表扫描。
需要修改为
select name from user where telno='18722222222'
13 如果需要进行 join 的字段两表的字段类型要相同
不然也不会命中索引。