高性能的索引策略

索引是最佳的解决方案吗

索引并不总是最好用的工具

只有在帮助存储引擎快速查找到记录的好处大于带来的额外工作时,索引才是有效的

① 对于小表,大部分时间全表扫描更高效

② 对于中到大型表,索引是有效的

③ 对于特大型表,建立和使用索引的代价随之增长,最好使用分区技术(直接区分查询需要的一组数据,而非一条一条记录匹配)

不同数据结构的索引

一. B-Tree 索引(对索引列顺序存储,适合查找范围数据)

关于 B-Tree 索引的知识回顾 :若某表有一个多列索引 idx(a,b,c) ,那么如下的 Where 条件是否可以走索引,如何走索引?

select * from instance where a=xx and b>xx and c=xx;

答案 :可以走索引,a 和 b 字段可以走索引,c 无法走索引

该 SQL 语句的 Where 条件已经符合最左前缀匹配原则,此处不叙述相关内容

如果查询有某个列的范围查询,那么此列右面(当前例子中即 b 右面)的字段查询无法走索引

二. Hash 索引(哈希索引,Memory 存储引擎支持)

基于哈希表实现,对所有索引列计算得出一个哈希码(hash code)

MySQL 中只有 Memory 存储引擎支持哈希索引,也是 Memory 存储引擎的默认索引类型

三. R-Tree 索引(空间数据索引,MyISAM 存储引擎支持)

四. FullText 索引(全文索引)

全文索引是一种特殊的索引,查找的是文本中的关键词,而不是比较索引中的值(更类似于搜索引擎,而不是 Where 匹配)

全文索引注意更多的细节,停用词、词干、复数、布尔搜索等

在相同的列上创建 B-Tree 索引和 FullText 索引不会有冲突,全文索引适用于 Match Against 匹配,而非 Where 匹配

B-Tree 索引的优点

减少服务器需要扫描的行的数量(通过索引获得回表查询的指针,或实现索引覆盖)

帮助服务器避免排序和临时表(B-Tree 索引按顺序存储,可以 Oder By 和 Group By 操作)

可以将随机 IO 变成顺序 IO(B-Tree 索引按顺序存储,可以 Oder By 和 Group By 操作)

哈希索引(Memory)

哈希索引基于哈希表实现,MySQL 中只有 Memory 存储引擎支持哈希索引

哈希索引不是按照索引值顺序存储的,无法按照字段的顺序进行排序

哈希索引只支持等值比较查询,包括 = 、IN() 、<=> (注意和 <> 不同,<=> 个人理解可以类比做 IS),不支持任何范围查询

哈希索引维护麻烦,如果在某个选择性很低(哈希冲突很高)的字段上建立哈希索引,当在表中删除一行记录时,存储引擎要遍历哈希索引中哈希值链表的每一行,找到并删除对应的行的引用,冲突越多,代价越大

自适应哈希索引(InnoDB)

InnoDB 存储引擎有一个特性,叫自适应哈希索引

某些索引值被使用的非常频繁时,会在内存中基于 B-Tree 索引之上,再建立一个哈希索引

这让 B-Tree 索引也具有了哈希索引的一些优点,比如快速的哈希查找

这是一个完全自动、内部的行为,用户无法控制或者配置,但可以关闭该功能

索引优化案例 - 长字段索引如何查的更快?

案例 :某表存储了大量长字段的 URL,业务需要根据 URL 进行查找,URL 上建立了索引

# 存储大量的 URL, 而且需要根据 URL 进行搜索和查找
mysql> select id from url where url="http://www.mysql.com";
# 使用 B-Tree 存储 URL ,存储的内容很大,因为 URL 很长,查询速度就很慢

解决方案 :删除原 URL 上建立的索引,新增 一个被索引的 url_crc 列,使用 CRC32 哈希

mysql> select id from url where url="http://www.mysql.com"
    -> and url_crc=CRC32("http://www.mysql.com"); 
# 这样做性能非常高,MySQL 优化器会使用这个选择性很高、体积很小的基于 url_crc 列的索引完成查找
# CRC32 获取的是 32 位整数,当数据量大于 93000 之后,有 1% 的概率产生哈希冲突
# 为避免哈希冲突,将字段值和对应 CRC32() 哈希值一起写到 Where 条件中即可

该情况下是伪哈希索引,索引的结构其实还是 B-Tree ,这个 B-Tree 的叶子节点只包含哈希值行指针,而不存储字段值,不能避免读取行,但内存中读取行较快,对性能的影响不明显

哈希索引维护,需要创建一个触发器

delimiter $$
-- 插入时对 url_crc 字段进行赋值
create trigger pseudohash_crc_ins 
before insert on pseduhash
for each row begin
set NEW.url_crc=crc32(NEW.url);
end$$
-- 更新时对 url_crc 字段进行更新
create trigger pseudohash_crc_upd 
before update on pseduhash
for each row begin
set NEW.url_crc=crc32(NEW.url);
end$$
delimiter ;

关于多列索引和多个单列索引

首先,多个单列索引是不推荐的

如下示例中,如果是两个索引,将对两个单列索引进行扫描,将结果进行合并

算法无非有三种 : OR 条件的联合(union)、AND 条件的相交(intersection)、以及前两者的情况都包括的联合和相交

# 有两个单列索引 film_id 和 actor_id
mysql> select film_id,actor_id from ethan.film_actor
    -> where film_id = 1 and actor = 1; 
# 这种情况下,就应该创建一个多列索引了
# OR 条件的联合需要花费大量的 CPU、内存 在算法的缓存、排序和合并操作上
# 多个单列索引的查询成本(消耗CPU和内存资源),甚至不如全表扫描

多列索引选择合适的索引顺序

选择性较高的,需要放在多列索引的前面

mysql> select count(*),sum(age=25),sum(region="河北省") from itzone_m1;
+----------+-------------+-------------------------+
| count(*) | sum(age=25) | sum(region="河北省")    |
+----------+-------------+-------------------------+
|    10000 |         56  |                     297 |
+----------+-------------+-------------------------+
1 row in set (0.01 sec)

# 类似这种,如果想要建立 age、region 字段的联合索引,就应该把 age 放在前面,重复值较少,选择性较高
# 多列索引的第一列,若接近于总行数 count(*) ,也代表着这个索引没什么用

InnoDB 与 MyISAM 数据分布

禁止使用 UUID 作为主键

要避免随机的(不连续、值的范围分布很大)聚集索引

使用 UUID 作为聚集索引的主键,使用 UUID 作为聚集索引,数据的插入时随机的,使数据没有任何的聚集特性

posted @ 2021-05-24 15:47  拨云见日z  阅读(130)  评论(0编辑  收藏  举报