前言

今天突然奇想,想着如果开启了一个事务,事务操作是修改一条数据记录的索引字段数据,那么这个索引会不会立马更新掉,还是等事务提交后再更新?

我的理解

我理解是会立马更新的,因为一般来说除了串行化这种隔离级别,其他隔离级别都是支持并行的,某种程度上是可以读取到其他事务的东西,只不过RC这个隔离级别采取了MVCC,但其实还是会更新数据,只不过通过版本链来控制数据透出,所以这里还是会更新索引的,这样就存在下面两个情况

  • 本事务内还可以根据索引字段搜索到数据
  • 其他事务根据索引查不到数据

一条SQL的索引更新流程

mysql索引什么时候生效 mysql索引文件什么时候更新_mysql

一个SQL执行更新操作,我们简单看下执行流程:

  • 是否命中缓存,没有命中加载缓存磁盘读取数据至内存
  • 内存中修改数据
  • 写入redo log
  • 写入bin log
  • 事物提交

另一个问题

索引更新这么频繁,那么索引树调整也会很频繁,我们从聚簇索引的B+树结构知道,采取这个结构是因为减少磁盘IO次数,那么索引树结构呢?首先还是B+树作为索引结构,但二级索引不一定就是递增的,索引频繁更新会产生大量数据移动的页分裂,在寻找合适位置进行插入时,这样造成的结果就是大量的性能消耗,那么这个问题mysql如何解决呢?

缓冲

mysql索引什么时候生效 mysql索引文件什么时候更新_mysql索引什么时候生效_02

内存缓冲池(buffer pool)以页为单位,缓存最热的数据页(data page)与索引页(index page),一条更新sql的操作就变为:

  • 是否命中缓存,没有命中加载缓存磁盘读取数据至内存
  • 内存中修改数据
  • 写入redo log

这样看,没命中缓存也会有一次磁盘IO操作,有没有更好的方式呢

change buffer写缓冲优化

在MySQL5.5之前,叫插入缓冲(insert buffer),只针对insert做了优化;现在对delete和update也有效,叫做写缓冲(change buffer)。它是一种应用在非唯一普通索引页(non-unique secondary index page)不在缓冲池中,对页进行了写操作,并不会立刻将磁盘页加载到缓冲池,而仅仅记录缓冲变更(buffer changes),等未来数据被读取时,再将数据合并(merge)恢复到缓冲池中的技术。写缓冲的目的是降低写操作的磁盘IO,提升数据库性能。步骤大概是:

  • 如果在缓存区,则直接修改。
  • 如果不在缓存区,则先放入一个插入缓冲区中。
  • 定期刷盘,查询会将操作将数据合并到缓冲池

其中第二步好似欺骗数据库,表明非聚集的索引已经插到叶子节点了,然后再以一定的频率执行插入缓冲和非聚集索引页子节点的合并操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对非聚集索引执行插入和修改操作的性能,主要还是减少非聚集索引频繁修改带来的性能问题。但是这种方式会带来另一些问题:

  • 内存存数据,如果数据库宕机,这些索引就会损失了,造成数据不一致,
  • 占用缓存池的大小,短期的过多请求会造成大量占用

要求非唯一普通索引

这里主要是因为唯一索引会有校验逻辑,校验索引唯一性,肯定要去磁盘读取才能判断,那么这种场景下也就无法优化,而主键索引大多是自增有序,大量的操作影响较小,所以优化的针对就是非唯一普通索引。

参考博客

Change BufferMysql-Innodb特性之插入缓存Insert Buffer