SQL语句: select sql_no_cache * from actor where first_name like 'rMq%';
索引 无索引 first_name耗时 7.23 0.50
可以看到,对于like前缀查询,使用索引与不使用索引差别可达到几百个数量级。如下是使用索引的一个执行计划:
2.3.3 字符串前缀字符串前缀索引指的是只取字符串前几个字符建立的索引。在进行查询时,如果一个字段值较长,那么为其建立索引的成本将非常高,并且查询效率也比较低,字符串前缀索引就是为了解决这一问题而存在的。字符串前缀索引主要应用在两个方面:
字段前缀部分的选择性比较高;
字段整体的选择性不太大(如果字段整体选择性比较大则可以使用哈希索引)。
如图所示为为first_name字段建立字符串前缀索引的示意图:
图中为first_name字段建立了长度为4的前缀索引,可以看到,如果查询使用的是where first_name='qWhNIZqxcbD';,那么MySQL首先会截取等值条件的前四个字符,然后将其与字符串前缀索引进行比较,从而定位到前缀为"qWhN"的索引片,然后获取该索引片对应的磁盘数据,最后将获取的磁盘数据的first_name字段与查询的等值条件的值进行比较,从而得到结果集。
字符串前缀索引最需要注意的一个问题是如何选择前缀的长度,长度选择合适时,前缀索引的过滤性将和对整个字段建立索引的选择性几乎相等。这里我们就需要用到前面讲解的关于字段选择性的概念,即字段选择性为对该字段分组之后,数据量最大的组的数据量占总数据量的比例。这里选择前缀长度时,可以理解为,前缀的选择性为按照前缀分组之后,数据量最大的组占总数据量的比例。如下表所示为计算前缀长度的SQL公式:
SQL语句 前缀长度select count(*) as cnt, first_name as perf from actor group by perf order by cnt desc limit 10; 0
select count(*) as cnt, left(first_name, 2) as perf from actor group by perf order by cnt desc limit 10; 2
select count(*) as cnt, left(first_name, 3) as perf from actor group by perf order by cnt desc limit 10; 3
select count(*) as cnt, left(first_name, 4) as perf from actor group by perf order by cnt desc limit 10; 4
上表中的第一个查询其实就是按照整个字段建立索引的选择性,后续查询就是按照前缀长度分别为2,3,4时数据的选择性。如下图所示为该前缀选择性查询的结果:
可以看到,对于全字段建立索引的选择性为1%;当前缀长度为2时选择性为2%;当前缀长度为3时少量前缀的选择性为2%,其余数据为1%,整体而言还是2%;当前缀长度为4时,选择性为10%,基本上和为全字段建立索引的选择性一样高。
关于前缀索引,这里需要说明的一点是,由于同一前缀对应的实际字段数据可能不同,因而前缀索引是无法像全字段索引那样进行order by,group by和distinct优化的,也无法进行非等值查询。如下SQL为建立前缀索引的SQL语句:
ALTER TABLE actor ADD KEY (first_name(4));SQL语句: select * from actor3 where first_name='qWhNIZqxcbD';
索引类型 无索引 first_name(4)耗时 6.05s 0.15
2.3.4 哈希索引
哈希索引同字符串前缀索引一样,都是为长字符串字段建立的索引,但哈希索引相对于字符串前缀索引解决了两个问题:
如果字符串字段的前缀部分选择性非常低,如URL,那么建立前缀索引成本将非常高;
当字符串字段整体的选择性非常高时,比如基本上每个值都是唯一的,此时无论是建立的全字段索引还是前缀索引其索引片都非常厚,对于内存的消耗比较大。
哈希索引的实现思路是,对一个字符串字段,为其每个值都计算一个哈希值,并且建立一个新字段用于存储这些哈希值,然后为这个新字段建立索引,并且为字符串字段建立插入和更新的触发器,用于更新哈希字段的值。在进行查询时,使用同一哈希算法计算查询的字符串的哈希值,使用该哈希值在哈希字段上进行查询,由于建立了索引,因而查询非常快,对于查询到的结果将查询的字符串与查询结果的字符串字段进行比较,从而得到最后的结果。这里由于新建立的哈希字段是整型的,因而其索引片非常小,并且由于字符串字段的选择性非常高,因而哈希字段的选择性相对非常高,因而总体而言,查询效率是非常高的。如下是针对actor表的email字段建立哈希索引的方式:
建立哈希字段 ALTER TABLE actor ADD COLUMN hash_email BIGINT UNSIGNED NULL DEFAULT 0 COMMENT ’email字段的哈希值';更新哈希字段 UPDATE actor SEThash_email=crc32(email);
建立索引 alter table actor add index `idx_hash_email` (hash_email);
建立插入触发器 DELIMITER $ CREATE TRIGGER actor_hash_insert BEFORE INSERT ON `actor4` FOR EACH ROW BEGINSET NEW.hash_email=crc32(NEW.email); END;$
建立更新触发器 DELIMITER $ CREATE TRIGGER actor_hash_update BEFORE UPDATE ON `actor4` FOR EACH ROW BEGIN SET NEW.hash_email=crc32(NEW.email); END; $