索引的使用和设计原则
索引的声明和使用
索引分类
- 从功能逻辑上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引。
- 按照物理实现方式,索引可以分为 2 种:一级索引(聚簇索引)和二级索引(非聚簇索引)。
- 一级索引:索引和数据存储在一起,都存储在同一个 B+tree 中的叶子节点。一般主键索引都是一级索引。
- 二级索引树的叶子节点存储的是主键而不是数据。也就是说,在找到索引后,得到对应的主键,再回到一级索引中找主键对应的数据记录。
- 按照作用字段个数进行划分,分成单列索引和联合索引。
创建索引
# 建表时建立索引
CREATE TABLE table_name [col_name data_type]
[UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY] [index_name] (col_name [length]) [ASC | DESC]
# 在已存在的表上创建索引
ALTER TABLE table_name
ADD [UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY] [index_name] (col_name[length],...) [ASC | DESC]
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
ON table_name (col_name[length],...) [ASC | DESC]
UNIQUE
、FULLTEXT
和SPATIAL
为可选参数,分别表示唯一索引、全文索引和空间索引;INDEX
与KEY
为同义词,两者的作用相同,用来指定创建索引;index_name
指定索引的名称,为可选参数,如果不指定,那么 MySQL 默认 col_name 为索引名;col_name
为需要创建索引的字段列,该列必须从数据表中定义的多个列中选择;length
为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度;ASC
或DESC
指定升序或者降序的索引值存储。
查看索引
SHOW INDEX FROM table_name
删除索引
ALTER TABLE table_name DROP INDEX index_name;
或
DROP INDEX index_name ON table_name;
隐藏索引(8.0 新特性)
从 MySQL8.x 开始支持隐藏索引(invisible indexes),只需要将待删除的索引设置为隐藏索引,使查询优化器不再使用这个索引(即使使用 force index(强制使用索引),优化器也不会使用该索引),确认将索引设置为隐藏索引后系统不受任何响应,就可以彻底删除索引。这种通过先将索引设置为隐藏索引,再删除索引的方式就是软删除。
# 切换成隐藏索引
ALTER TABLE tablename ALTER INDEX index_name INVISIBLE;
# 切换成非隐藏索引
ALTER TABLE tablename ALTER INDEX index_name VISIBLE;
# 也可以在创建索引时指定 INVISIBLE
# 索引创建时默认是 VISIBLE
(当索引被隐藏时,其内容仍是实时更新的,因此不推荐长期隐藏索引)
创建索引原则(推荐创建索引的场景)
字段的数值有唯一性的限制
索引本身可以起到约束的作用,比如唯一索引、主键索引都可以起到唯一性约束的,因此在我们的数据表中,如果 某个字段是唯一的 ,就可以 直接创建唯一性索引 ,或者 主键索引 。这样可以更快速地通过该索引来确定某条记录。
参考: 普通索引的性能会不会比唯一索引好? - 掘金 (juejin.cn)
Mysql - 普通索引与唯一索引之间性能差别 change buffer_it_lihongmin 的博客-CSDN 博客
频繁作为 WHERE 查询条件且具有明显区分特性的字段
某个字段在 SELECT 语句的 WHERE 条件中经常被使用到并且具有明显区分特性(例如 username,而不是 sex 这种),那么就需要给这个字段创建索引了。尤其是在数据量大的情况下,创建普通索引就可以大幅提升数据查询的效率。
经常 GROUP BY 和 ORDER BY 的列
某个字段在 SELECT 语句的 WHERE 条件中经常被使用到,那么就需要给这个字段创建索引了。尤其是在数据量大的情况下,创建普通索引就可以大幅提升数据查询的效率。索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,就需要对 分组或者排序的字段进行索引 。如果待排序的列有多个,那么可以在这些列上建立 组合索引 。
UPDATE、DELETE 的 WHERE 条件列
对数据按照某个条件进行查询后再进行 UPDATE 或 DELETE 的操作,如果对 WHERE 字段创建了索引,就能大幅提升效率。原理是因为我们需要先根据 WHERE 条件列检索出来这条记录,然后再对它进行更新或删除。 如果进行更新的时候,更新的字段是非索引字段,提升的效率会更明显,这是因为非索引字段更新不需要对索引进行维护。
查询 DISTINCT 字段
有时候我们需要对某个字段进行去重,使用 DISTINCT,那么对这个字段创建索引,也会提升查询效率。
多表 JOIN
- 连接表的数量尽量不要超过 3 张 ,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率。
- 对 WHERE 条件创建索引 ,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
- 对用于连接的字段创建索引 ,并且该字段在多张表中的 类型必须一致 。
使用列的类型小的创建索引
我们这里所说的类型大小指的就是 该类型表示的数据范围的大小 。
- 数据类型越小,在查询时进行的比较操作越快
- 数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以放下更多的记录,从而减少磁盘 I/O 带来的性能损耗,也就意味着可以把更多的数据页缓存在内存中,从而加快读写效率。
这个建议对于表的主键来说更加适用,因为不仅是聚簇索引中会存储主键值,其他所有的二级索引的节点处都会存储一份记录的主键值,如果主键使用更小的数据类型,也就意味着节省更多的存储空间和更高效的 I/O。
使用字符串前缀创建索引
如果某个字段是 varchar 类型的(或者 text 等等),那么其中可能存储了很长的字符串,当建立索引时,可以不用整个字符串建立索引,而是指截取前面部分建立索引
拓展:Alibaba《Java 开发手册》
【强制】 在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90% 以上。
区分度计算公式: count(distinct left(列名, 索引长度))/count(*)
区分度高的列适合作为索引
列的基数指的是某一列中不重复数据的个数,比方说某个列包含值 2,5,8,2,5,8,2,5,8,虽然有 9 条记录,但该列的基数却是 3 。也就是说, 在记录行数一定的情况下,列的基数越大,该列中的值越分散;列的基数越小,该列中的值越集中。 这个列的基数指标非常重要,直接影响我们是否能有效的利用索引。最好为列的基数大的列建立索引,为基数太小的列建立索引效果可能不好。
可以使用公式 select count(distinct a)/count(*) from t1
计算区分度,越接近 1 越好,一般超过 33% 就算是比较高效的索引了。
拓展:联合索引把区分度高(散列性高)的列放在前面。
使用频率高的列放到联合索引的左侧
多个字段都要创建索引的情况下,联合索引优于单值索引
单张表的索引不超过 6 个
不适合创建索引的场景
- where 中使用不到的字段不要使用索引
- 数据量小的表不要使用索引
- 有大量重复数据的列上不要创建索引
- 避免对经常更新的表创建过多索引
- 不建议用无序的值作为索引
- 删除不再使用或者很少使用的索引
- 不要定义冗余或重复的索引
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论