1)最左前缀匹配原则,非常重要的原则,mysql
会一直向右匹配直到遇到范围查询(>、<、between、like)
就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4
如果建立(a,b,c,d)
顺序的索引,d
是用不到索引的,如果建立(a,b,d,c)
的索引则都可以用到,a,b,d
的顺序可以任意调整。
2)=
和in
可以乱序,比如a = 1 and b = 2 and c = 3
建立(a,b,c)
索引可以任意顺序,mysql
的查询优化器会帮你优化成索引可以识别的形式。
3)尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*)
,表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join
的字段我们都要求是0.1以上,即平均1条扫描10条记录
4)索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’
就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)
的索引,那么只需要修改原来的索引即可。
6)定义有外键的数据列一定要建立索引。
(1) 外键没有索引,确实可能导致子表产生表锁,但是有前提:
a. 子表有删改操作。
b. 主表有删操作,或者更新主键的操作。
满足以上两个条件才会出现主表操作hang状态。
(2) 外键不建索引,则删除主表记录或主子表关联查询,都会进行子表的全表扫描。
(3) 主子表任何插入操作,无论顺序,不会产生锁或hang状态。
(4) 只有外键创建索引,(1)中的操作才不会出现锁或hang状态,(2)中的操作才有可能使用索引。
通过以上实验,至少对外键不建立索引产生的影响,有了一些感性的认识,对外键为何要建立索引,应该有了更深入的理解。
7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
8)对于定义为text
、image
和bit
的数据类型的列不要建立索引。
9)对于经常存取的列避免建立索引
1、一般说来,索引应建立在那些将用于JOIN
,WHERE
判断和ORDER BY
排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM
类型的字段来说,出现大量重复值是很有可能的情况。
2、应尽量避免在 where
子句中对字段进行 null
值判断,否则将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num is null
最好不要给数据库留NULL
,尽可能的使用 NOT NULL
填充数据库.
备注、描述、评论之类的可以设置为 NULL
,其他的,最好不要使用NULL
。
不要以为 NULL
不需要空间,比如:char(100)
型,在字段建立时,空间就固定了, 不管是否插入值(NULL
也包含在内),都是占用 100个字符的空间的,如果是varchar
这样的变长字段, null
不占用空间。
可以在num
上设置默认值0,确保表中num
列没有null
值,然后这样查询:
3、应尽量避免在 where
子句中使用 !=
或 <>
操作符,否则将引擎放弃使用索引而进行全表扫描。
4、应尽量避免在 where
子句中使用 or
来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num=10 or Name = ‘xiaoming‘
可以这样查询,充分利用索引:
select id from t where num = 10 union all select id from t where Name = ‘xiaoming‘
5、in
和 not in
也要慎用,否则会导致全表扫描。
而且负向查询(not , not in, not like, <>, != ,!>,!< )
不会使用索引
select id from t where num in(1,2,3)
对于连续的数值,能用 between
就不要用 in
了:
select id from t where num between 1 and 3
很多时候用 exists
代替 in
是一个好的选择,当然exists
也不跑索引。
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)
6、)下面的模糊查询也将导致全表扫描:
select id from t where name like ‘%abc%’
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%”
不会使用索引,而like “aaa%”
可以使用索引。
若要提高效率,可以考虑全文检索。
既然谈到模糊查询下使用索引,我们就顺便详细地讲讲吧。
like %keyword
索引失效,使用全表扫描。但可以通过翻转函数+like前模糊查询+建立翻转函数索引=走翻转函数索引,不走全表扫描like keyword%
索引有效。like %keyword%
索引失效,也无法使用反向索引。//可以用explain测试,测一下有没有走索引 select * from table where code like ‘Classify_Description%‘ select * from table where code like ‘%Classify_Description%‘ select * from table where code like ‘%Classify_Description‘
7、)如果在 where
子句中使用参数,也会导致全表扫描。因为SQL
只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num = @num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num = @num
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2 = 100
正上面的应改为:
select id from t where num = 100*2
8、)应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3) = ’abc’ //name以abc开头的id select id from t where datediff(day,createdate,’2005-11-30′) = 0 -–‘2005-11-30’ //生成的id
应改为:
select id from t where name like ‘abc%‘ select id from t where createdate >= ‘2005-11-30‘ and createdate < ‘2005-12-1‘
9、不要在 where
子句中的“=”
左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10、在使用索引字段作为条件时,如果该索引是复合索引(多列索引),那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
11、索引并不是越多越好,索引固然可以提高相应的 select
的效率,但同时也降低了 insert
及 update
的效率,因为 insert
或 update
时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
12、应尽可能的避免更新 clustered
索引数据列,因为 clustered
索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered
索引数据列,那么需要考虑是否应将该索引建为 clustered
索引。
13、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
14、MySQL
查询只使用一个索引,因此如果where
子句中已经使用了索引的话,那么order by
中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
http://www.voidcn.com/article/p-vdgnnhwr-bws.html
https://blog.csdn.net/fengzongfu/article/details/104830923