帮助中心/最新通知

质量为本、客户为根、勇于拼搏、务实创新

< 返回文章列表

【服务器相关】SQL开发知识:为MySQL创建高性能索引

发表时间:2025-06-16 03:46:00 小编:主机乐-Yutio

1 索引基础

1.1 索引作用

在MySQL中,查找数据时先在索引中找到对应的值,然后根据匹配的索引记录找到对应的数据行,假如要运行下面查询语句:

实例图说明:

每个节点占用一个磁盘块,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为 16 和 34,P1 指针指向的子树的数据范围为小于 16,P2 指针指向的子树的数据范围为 16~34,P3 指针指向的子树的数据范围为大于 34。查找关键字过程:

  • 根据根节点找到磁盘块 1,读入内存。【磁盘 I/O 操作第 1 次】
  • 比较关键字 28 在区间(16,34),找到磁盘块 1 的指针 P2。
  • 根据 P2 指针找到磁盘块 3,读入内存。【磁盘 I/O 操作第 2 次】
  • 比较关键字 28 在区间(25,31),找到磁盘块 3 的指针 P2。
  • 根据 P2 指针找到磁盘块 8,读入内存。【磁盘 I/O 操作第 3 次】
  • 在磁盘块 8 中的关键字列表中找到关键字 28。 

缺点

  • 每个节点都有key,同时也包含data,而每个页存储空间是有限的,如果data比较大的话会导致每个节点存储的key数量变小;
  • 当存储的数据量很大的时候会导致深度较大,增大查询时磁盘io次数,进而影响查询性能。

1.2.2 B+Tree索引

B+树是对B树的变种。与B树区别:B+树只在叶子节点存储数据,非叶子节点只存储key值及指针。

在B+树上有两个指针,一个指向根叶子节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构,因此可以对B+树进行两种查找运算:一种是对于组件的范围查找,另一种是从根节点开始,进行随机查找。

B*树与B+数类似,区别在于B*数非叶子节点之间也有链式环结构。

1.2.3 Hash索引

哈希索引基于哈希表实现,只有精准匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。

在MySQL中只有Memory默认索引类型就是使用的哈希索引,memory也支持B-Tree索引。同时,Memory引擎支持非唯一哈希索引,如果多个列的哈希值相同,索引会以链表的方式存放多个指针相同一个哈希条目中。类似HashMap。

优点
索引自身只需要存储对应的哈希值,所以索引的结构十分紧凑,哈希所以查找的速度非常快。
缺点

  • 利用hash存储的话需要将所有的数据文件添加到内存,比较耗费内存空间;
  • 哈希索引数据并不是按顺序存储的,所以无法用于排序;
  • 如果所有的查询都是等值查询,那么hash确实很快,但是在企业或者实际工作环境中范围查找的数据更多,而不是等值查询,因此hash就不太适合了;
  • 如果哈希冲突很多的话,索引维护操作的代价也会很高,这也是HashMap后期通过增加红黑树解决Hash冲突的问题;

2 高性能索引策略

2.1 聚簇索引与非聚簇索引

聚簇索引

不是单独的索引类型,而是一种数据存储方式,在InnoDB存储引擎中聚簇索引实际在同一个结构中保存了键值和数据行。当表中有聚簇索引时,它的数据行实际上存放在索引的叶子页中。因为无法同时把数据行存放在不同的地方,所以一个表中只能有一个聚簇索引(索引覆盖可以模拟出多个聚簇索引的情况)。

聚簇索引优点:

可以把相关数据保存在一起;数据访问更快,因为索引和数据保存在同一个树中;使用覆盖索引扫描的查询可以直接使用页节点中的主键值;

缺点:

聚簇数据最大限度地提高了IO密集型应用的性能,如果数据全部在内存,那么聚簇索引就没有什么优势;插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式;更新聚簇索引列的代价很高,因为会强制将每个被更新的行移动到新的位置;基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题;聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候;

非聚簇索引

数据文件跟索引文件分开存放

2.2 前缀索引

有时候需要索引很长的字符串,这会让索引变的大且慢,通常情况下可以使用某个列开始的部分字符串,这样大大的节约索引空间,从而提高索引效率,但这会降低索引的选择性,索引的选择性是指:不重复的索引值(也称为基数cardinality)和数据表记录总数的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,因为选择性更高的索引可以让mysql在查找的时候过滤掉更多的行。

一般情况下某个列前缀的选择性也是足够高的,足以满足查询的性能,但是对应BLOB,TEXT,VARCHAR类型的列,必须要使用前缀索引,因为mysql不允许索引这些列的完整长度,使用该方法的诀窍在于要选择足够长的前缀以保证较高的选择性,通过又不能太长。

举例

表结构及数据MySQL官网或GItHub下载。

city Table Columns

字段名含义
city_id城市主键ID
city城市名
country_id国家ID
last_update:创建或最近更新时间

可以看到当前缀长度到达7之后,再增加前缀长度,选择性提升的幅度已经很小了。由此最佳创建前缀索引长度为7。

2.3 回表

要理解回表需要先了解聚族索引和普通索引。聚族索引即建表时设置的主键索引,如果没有设置MySQL自动将第一个非空唯一值作为索引,如果还是没有InnoDB会创建一个隐藏的row-id作为索引(oracle数据库row-id显式展示,可以用于分页);普通索引就是给普通列创建的索引。普通列索引在叶子节点中存储的并不是整行数据而是主键,当按普通索引查找时会先在B+树中查找该列的主键,然后根据主键所在的B+树中查找改行数据,这就是回表。

2.4 覆盖索引

覆盖索引在InnoDB中特别有用。MySQL中可以使用索引直接获取列的数据,如果索引的叶子节点中已经包含要查询的数据,那么就没必要再回表查询了,如果一个索引包含(覆盖)所有需要查询的字段的值,那么该索引就是覆盖索引。简单的说:不回表直接通过一次索引查找到列的数据就叫覆盖索引。

表信息

覆盖索引在组合索引中用的比较多,举例

设置组合索引后再次查询

2.5 索引匹配方式

2.5.1 最左匹配

在使用组合索引中,比如设置(age,name)为组合索引,单独使用组合索引中最左列是可以匹配索引的,如果不使用最左列则不走索引。例如下面SQL

下面的SQL不走索引

explain select * from t_user whereuname='zhang';

2.5.2 匹配列前缀

可以匹配某一列的值的开头部分,比如like 'abc%'。

2.5.3 匹配范围值

可以查找某一个范围的数据。

2.5.4 精确匹配某一列并范围匹配另外一列

可以查询第一列的全部和第二列的部分

2.5.5 只访问索引的查询

查询的时候只需要访问索引,不需要访问数据行,本质上就是覆盖索引。

3 索引优化最佳实践

1. 当使用索引列进行查询的时候尽量不要使用表达式,把计算放到业务层而不是数据库层。

5. union all,in,or都能够使用索引,但是推荐使用in

6. 范围列可以用到索引范围条件是:<、<=、>、>=、between。范围列可以用到索引,但是范围列后面的列无法用到索引,索引最多用于一个范围列。

7. 更新十分频繁,数据区分度不高的字段上不宜建立索引

  • 更新会变更B+树,更新频繁的字段建议索引会大大降低数据库性能;
  • 类似于性别这类区分不大的属性,建立索引是没有意义的,不能有效的过滤数据;
  • 一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 来计算;

8. 创建索引的列,不允许为null,可能会得到不符合预期的结果

9.当需要进行表连接的时候,最好不要超过三张表,如果需要join的字段,数据类型必须一致

10. 能使用limit的时候尽量使用limit

11. 单表索引建议控制在5个以内

12. 单索引字段数不允许超过5个(组合索引)

13. 创建索引的时候应该避免以下错误概念

  • 索引越多越好
  • 过早优化,在不了解系统的情况下进行优化

4 索引监控

参数说明
Handler_read_first读取索引第一个条目的次数
Handler_read_key通过index获取数据的次数
Handler_read_last读取索引最后一个条目的次数
Handler_read_next通过索引读取下一条数据的次数
Handler_read_prev通过索引读取上一条数据的次数
Handler_read_rnd从固定位置读取数据的次数
Handler_read_rnd_next从数据节点读取下一条数据的次数

到此这篇关于SQL开发知识:SQL开发知识:SQL开发知识:为MySQL创建高性能索引的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。


联系我们
返回顶部