三.MySQL索引

数据库的索引事为了提高数据查询效率。

索引的常见模型

索引比较常见的模型有:哈希表,有序数组和搜索树。

哈希表

哈希表是一种以k-v存储数据的结构,我们只要输入待查找的key,就可以找到对应的value。哈希表的实现很简单,用哈希函数把key换算成一个地址,然后把value放在这个地址。如果不同的key经过哈希函数计算后得到一个相同的地址,往后拉一个链表即可。

哈希表适用于等值查询的场景,比如Memcached和一些NoSQL。哈希表不适用于区间查询的场景,因为哈希表不是有序的,区间查询时要全部扫描一遍,非常慢。

有序数组

有序数组在等值查询和范围查询的场景下表现都很优秀,使用二分查找,时间复杂度只有O(log(n))。但有序数组不适合更新数据,往中间插入一条数据就需要移动后面索引的记录,成本太高。

有序数组只适合做静态存储引擎。比如要保存的是某城市2017年所有的人口信息,这类不再修改的数据。

搜索树

搜索树的特点是,父节点左子树所有节点的值小于父节点的值,父节点右子树所有节点的值大于父节点的值。按照这个特性进行搜索的话,搜索的时间复杂度是O(log(N))。为了维持查询的时间复杂度是log(N),在更新节点时要保证搜索树是平衡搜索树,更新的时间复杂度也是log(N)。

在实际的数据库引擎中,搜索树一般不用二叉搜索树,都是多叉搜索树。与多叉搜索树相比,二叉搜索树在理论上更快。但是在实际场景中,访问磁盘的速度是很慢的。假设一棵100万节点的平衡二叉树,树高20,一次查询可能需要访问20次磁盘。如果使用的机械磁盘,每访问一次磁盘花费10ms,一次查询耗费了200ms,太慢了。

为了尽可能少的访问磁盘,在实际应用中会使用平衡N叉树。在InnoDB中,N大约是1200。100万条记录在此时只有两层,根节点一般是在内存中的,访问磁盘的次数就更少了。

N叉树在读写性能上的优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎上。

B-树和B+树的区别
  • B+树内节点不存储数据,所有数据都存储在也节点,查询时间复杂度固定为logN。B-树内节点存储数据,数据查询时间复杂度不固定,与数据在树中的位置有关,最好为O(1).

  • B+树叶节点两两相连可大大增加区间访问性,可使用范围查询等。而B-树每个节点key和data在一起,则无法区间查找。
    在这里插入图片描述

  • B+树更适合外部存储。由于内节点无data域,每个节点能索引的范围更大更精确。

  • https://www.jianshu.com/p/ace3cd6526c4

InnoDB的索引模型

InnoDB使用了B+树索引模型,数据都存储在B+树中。每一个索引在InnoDB里面对应一颗B+树。

// 假设我们有一张表T,主键列为ID,表中有字段k,并且在k上有索引。
create table T(
id int primary key,
k int not null,
name varchar(16),
index(k)) engine = InnoDB;

在这里插入图片描述

从图中可以看出,根据叶子节点的类型,索引可以分为主键索引和非主键索引。

主键索引叶子节点存的是整行数据,在InnoDB中,主键索引也被成为聚簇索引。非主键索引叶子节点里面存的是主键的值,在InnoDB中,非主键索引称为二级索引。

基于主键索引和普通索引查询的区别:

如果语句是select * from T where id = 500,即主键查询的方式,只需要搜索ID搜索树。
如果语句是select * from T where k = 5,即普通搜索的查询方式。首先需要搜索K搜索树,找到ID=500,然后再利用ID,去搜索ID搜索树,这个过程成为回表。

基于非主键搜索树需要多扫描一颗索引树,在实际使用中,应该尽可能使用主键查询。

索引维护

B+树为了维护索引有序性,在插入新值时会进行必要的维护。

在上面的图中,如果插入的值是700,直接在500后面插入一条记录就行。如果插入的是400,需要挪动后面的数据,空出位置。更糟的是,如果R5所在的数据已经满了,根据B+树的算法,这时就需要申请一个新数据页,挪动部分数据过去(页分裂),在这种情况下,性能肯定会下降。除了性能,页分裂还会影响磁盘的利用率。分裂后,两个数据页的整体利用率下降到了50%。

为了避免上面的问题,一些建表规范要求一点要有自增主键。自增主键是指在自增列上定义的主键,在建表语句中一般这么定义:NOT NULL PRIMARY KEY AUTO_INCREMENT.

使用自增主键后,插入记录时可以不指定ID值,数据库在插入记录时会自动指定当前ID最大值+1作为下一条记录的ID。主键自增插入数据。每次插入一条数据都是追加操作,不会涉及数据的移动,也不会触发叶子节点的分裂。

而如果使用业务字段做自增主键,则往往不容易保证有序插入,这样写数据的成本比较高。

重建索引

alter table T drop index(k);
alter table T add index(k);

索引可能因为删除,页分裂等原因,导致数据页有漏洞。重建索引的过程中会创建一个新的索引,把数据按顺序插入。这样数据页的利用效率更高。
重建主键索引

// 不能重建主键索引
// 无论是删除主键,还是创建主键,都会导致整个表被重建
// 重建主键索引的效果相当于alter table T engine = InnoDB
alter table T drop primary key;
alter table T add primary key(id);

覆盖索引

如果要查找的数据在索引树上,不用回表就能查询到结果,这种索引叫覆盖索引。

在上图中,如果执行的语句是select ID from T where k between 3 and 5,这时就只查ID的值,而ID的值已经在索引树上了,因此可以直接提供查询结果,不用回表。也就是说,索引K已经覆盖了我们的查询请求,称为覆盖索引。

覆盖索引可以减少树的搜索次数,显著提高查询性能,所以覆盖索引是一个常用的性能优化手段。如果某个查询非常频繁,建立覆盖索引就是值得的。在实际场景中,需要综合各方面综合考虑。

最左前缀原则

B+树可以利用索引的“最左前缀”,定位记录。

有一个联合索引(name,age)。当查询姓张的人时,SQL查询条件是where name like '张%',此时利用联合索引(name,age)就可以快速查询出符合条件的记录,不需要新建一个索引(name)。

只要满足“最左前缀”,就可以利用索引加速检索。这个最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。在创建联合索引时,如果可以通过调整顺序,可以减少维护的索引数,这个顺序就是需要优先考虑的。

CREATE TABLE `geek` (
    a int(11) not null,
    b int(11) not null,
    c int(11) not null,
    d int(11) not null,
    PRIMARY KEY(a,b)
    key(c)
    key(c,a)
    key(c,b)
)ENGINE=InnoDB;

有一张表geek,上面创建了三个索引©,(c,a),(c,b),为了支持下面两个查询,(c,a)和(c,b)是否都是必须的?

select * from geek where c=N order by a limit 1;
select * from geek where c=N order by b limit 1;

(c,a)不是必须的,可以去掉,(c,b)不能去掉。

索引下推

在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数,称为索引下推。

表上有联合索引(name,age)。现在有一个需求,检索出表中“名字第一个是张,而且年龄是10岁的所有男孩”。SQL语句是这样的:

select * f rom tuser where name like '张%' and age = 10 and ismale = 1;

无索引下推的流程是这样的:
在这里插入图片描述

索引下推执行流程:
在这里插入图片描述

优化MySQL索引是提高数据库查询性能的关键步骤之一。下面是一些优化MySQL索引的常用方法: 1. 分析查询索引的执行计划:通过使用EXPLAIN语句来分析查询的执行计划,了解MySQL是如何使用索引的。可以查看是否使用了合适的索引索引是否覆盖了查询所需的列等。 2. 选择合适的数据类型:选择适当的数据类型可以减小索引的大小,提高查询效率。例如,对于字符串类型的列,可以使用最短的适当字符集和根据实际情况选择字符集和排序规则。 3. 创建合适的索引:根据查询的特点和频率,创建合适的索引。考虑到查询的选择性和覆盖度,选择合适的列作为索引,并根据查询的顺序和条件进行列顺序的选择和组合。 4. 删除不必要的索引:过多或不必要的索引会增加写操作的开销,并占用额外的存储空间。定期检查和删除不再使用或重复的索引,以减少索引维护的负担。 5. 使用覆盖索引:在查询中使用覆盖索引,即索引中包含了查询所需的所有列,避免了回表操作,可以提高查询性能。 6. 避免索引列上的函数操作:对索引列使用函数操作会导致无法使用索引,应尽量避免在索引列上进行函数操作。 7. 更新统计信息:MySQL会根据统计信息来优化查询计划。因此,定期更新索引的统计信息可以帮助MySQL选择更优的查询计划。 8. 注意索引和表的大小:索引和表的大小对查询性能有影响。较大的索引可能导致更多的磁盘I/O,而较大的表可能会增加查询的开销。可以考虑对较大的表进行分区或分表,以减少查询的范围和开销。 以上是一些常见的MySQL索引优化方法,根据具体的应用场景和需求,还可以进行更加细致的索引设计和性能调优。在实际使用中,可以结合实际情况进行测试和调整,以获得最佳的查询性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值