MySQL优化五-高性能的8个索引策略
正确创建和使用索引策略是实现高性能查询的基础,本文总结7个索引策略。
一、独立的列
独立的列是指索引不能是表达式的一部分,也不能是函数的参数。
mysql>select id from actor where id+1 = 5; //错误
mysql>select id from actor where id = 5-1; //正确
二、前缀索引和索引的特性
有时索引的很长的字符列,会让索引变得很慢且大,一个策略是使用模拟的哈希索引MySQL优化四-MySQL Innodb 自定义Hash索引_一个高效工作的家伙的博客-CSDN博客
但有时候这样做还不够,需使用前缀索引。
前缀索引是指,将字符列的前N位,作为索引列,而不是索引整个字符。比如
mysql> alter table city_demo add KEY ( city(7) )
前缀索引是一种能使用索引更小,更快的办法,但同时也有一个缺点,mysql无法用前缀索引做order by 和 group by ,也无法使用前缀索引做覆盖扫描。
同时还出现一个问题,选择前几位作为前缀索引,选太多则索引变大,选太少则选择性太低,同一索引的结果数据太多。
根据以上地市的例子,使用前3,前4,前5。。。前7位分别计算其选择性。
mysql> select count(distinct left(city,3))/count(*) as sel3,
count(distinct left(city,4))/count(*) as sel4,
count(distinct left(city,5))/count(*) as sel5,
count(distinct left(city,6))/count(*) as sel6,
count(distinct left(city,7))/count(*) as sel7,
from city_demo ;
当前缀长度到7的时候,再增加前缀长度,其选择性提升的幅度很小了。此时可以将长度为7,作为前缀索引的字符长度。
三、多列索引
多列索引是指where子句,有两个及以上的查询列,需要将两列作为一个索引,以不是对每列单独作索引。
mysql> select id,city from city_demo where id=1 and city='XX'
此时将合并索引
mysql> alter table city_demo add KEY ( id,city )
四、选择合适的索引顺序
在一个多列索引中,索引列的顺序意味着,索引按照先左列进度排序,其次是第二列等,所以多列索引的顺序很重要。有一个经验法则是,将选择性最高的列,放在索引最前列。
比如:
mysql> select * from payment where staffid=2 and customerid=584
合并索引的顺序,应该是staffid在前,还是customerid在前面呢。
mysql> select sum(staffid=2),sum(cusotmer=584) from paument
********************************
sum(staffid=2) : 7992
sum(cusomerid=584) : 30
根据前面的经验法则,应将索引customerid放在前面,先找到customerid=584的30条记录,再从中找到staffid=2的记录,比先找到staffid=2的7992条记录,再从中找到customerid=584的记录,会快很多。
但这种判断,有时也许会有偏差,特别对于随机分布的数据来说,更是这样。所以需要用另一种评估方式。
myql> select count(distinct staffid>/count(*) as staffid,
count(distinct customid>/count(*) as customid
from payment;
************************************
staffid : 0.0001,
customid: 0.0373
从上结果可知,customerid的选择性更高。所以将其作为索引列的第一列。
mysql> alter table payment add KEY( customerid , staffid )
五、聚簇索引
- 聚簇索引:
- 将数据存储和索引放到了一块,找到了索引也就找到了数据
- 一般情况下主键会默认创建聚簇索引,且一张表只允许存在一个聚簇索引。
- 非聚簇索引:
- 将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行。
- MyISAM通过key_buffer把索引先缓存到了内存中,当需要访问数据时(通过索引访问数据),在内存中直接查找索引,然后通过索引找到磁盘相应数据。这也就是为什么索引不在key buffer命中时,速度慢的原因。
聚集索引⼀个表只能有⼀个,⽽⾮聚集索引⼀个表可以存在多个。聚集索引存储记录是物理上连续存在,⽽⾮聚集索引是逻辑上的连续,物理存储并不连续。
澄清一个概念:
- InnoDB中,在聚集索引上创建的索引叫做辅助索引
- 辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引、辅助引擎叶子节点存储的不再是行的物理位置,而是主键值。
一句话总结: innoDB中,每个表都有一个聚簇索引,如果设置了主键,它就是聚簇索引,一般要求字段自增。
六、覆盖索引
覆盖索引是指,一个索引包含所有需要查询的字段值。比如payment表有一个多列索引(staffid,customerid),如果只需访问这两列,就可使用这个索引作为覆盖索引。
mysql> select staffid,customerid from reyment
*******以上是覆盖索引
以下情况,无法用到覆盖索引:
mysql> select * from reyment where staffid=2 and customer like '%11'
1、查询选择了所有列,或选择了非索引的列。
2、覆盖索引无法执行like操作,索引只能作左前缀的like匹配;对于开头是%通配符的like查询,只有作全表扫描。
可以用另一种办法解决,叫延迟满足,即延迟了对列的访问。 将覆盖索引扩展至三个(staffid,customerid,prodid),查询语句重构如下:
mysql> select * from reyment t1
join (
select prodid from reyment where staffid=2 and customer like '%11'
) t2 on t1.prodid = t2.prodid
性能是否提升,取决于子句 select prodid from reyment where staffid=2 ,是否筛选掉大部分行,如果是,就可以这么优化。
七、使用索引扫描来做排序
mysql有两种方式生成有序的结果:通过排序操作,或者按索引顺序扫描。如果explain出来的type列的值为“index”,则说明使用了索引扫描来排序。
比如创建一个多列索引,包含以及顺序是:customerid , staffid ,inventoryid,以下测试均以此例。
mysql> alter table payment add KEY( customerid , staffid ,inventoryid )
以下条件满足索引扫描:
1、只有索引列的顺序和order by子句的顺序一致,且所有列的排序方向一致;
mysql> order by customerid , staffid ,inventoryid
*****顺序一致,使用索引排序
mysql> order by inventoryid , customerid , staffid
*****顺序不一致,不使用索引排序
2、多表关联,只有当order by 子句引用的字段,全部是第一个表时;
mysql>select * from payment t1 , rental t2 where t1.staffid = t2.staffid
order by t1.customerid , t1.staffid ,t1.inventoryid
3、满足索引的最左前缀的要求;
mysql>..... where customerid = 33034 order by staffid desc
mysql>..... where customerid = 33034 order by staffid , inventoryid
mysql>..... where customerid > 33034 order by customerid,staffid
以下不能使用索引排序:
1、使用不同的排序方向,但索引列都是正序排序:
mysql>.... order by customerid asc , staffid desc
2、order by 引用一个不在索引的列:
mysql>.... order by customerid , rental_date
******rental_date不在索引列
3、where和order by的列,无法组合成索引的最左前缀:
mysql>.....where customerid =33034 order by inventoryid
******索引列是customerid , staffid ,inventoryid,缺少了staffid列,导致无法走索引排序
4、索引的第一列是范围条件:
mysql>.....where customerid > 33034 order by staffid,inventoryid
*********where 和 order by 组合列中,第一列是范围条件,不走索引扫描
mysql>.....where customerid > 33034 order by customerid ,staffid,inventoryid
*********order by就有走索引扫描
5、多个等于条件,对于排序来说,也是一种范围条件:
mysql>.....where customerid = 33034 and staffid in (1,2) order by inventoryid
八、冗余索引和重复索引
重复索引是指在相同的列上,按相同的顺序创建相的的索引,应避免创建重复索引。冗余索引
mysql> create table test (
ID int not null PRIMARY KEY,
UNIQUE(ID),
INDEX(ID),
)
冗余索引是指创建了(A,B)索引,再创建(A);或者将一个索引扩展为(A,ID),其中 ID是主键,就是冗余索引。