唯一索引和普通索引 change buffer
学习检测
-
什么是change buffer?
-
唯一索引和普通索引查找数据流程及性能对比?
-
唯一索引和普通索引更新数据流程及性能对比?
-
change buffer 适用场景?
-
change buffer 和 redo log 的区别?
-
change buffer 是持久化的吗?
-
chabge buffer 的merge 操作是什么?
-
数据搜索修改的流程,是否加载内存中?
-
当有where 条件的时候,change buffer 是否其作用,为什么?
-
二级索引中的普通索引也会更新主键索引,什么情况下change buffer起作用?
-
change buffer 参数了解一下?默认值是多少
-
change buffer 刷新数据到磁盘的情况?
-
服务掉电,change buffer 的数据会丢失吗?什么情况有丢失?
-
唯一索引可以change buffer 吗?
总结
-
change buffer 是对数据进行修改的时候(insert、update、delete)的时候将修改优先写入change buffer,减少磁盘的随机IO消耗
-
数据查找过程 以 select * from where id = 3;为例
在内存中
唯一索引,在索引树中找到id为3的就停止搜索,返回数据
普通索引,在搜索到id为3的数据后继续向下搜索,直到找到第一个不满足条件的数据,返回
不在内存中
性能差异----微乎其微
性能只是普通索引多一次判断,忽略不计。数据是以页加载到内存中处理的,除非次数据是数据页的最后一条,会再次进行一次磁盘访问
-
数据修改过程(无 where情况)
在内存中
唯一索引,修改数据,直接返回
普通索引,修改数据,直接返回
不在内存中
唯一索引,加载数据所在页,判断唯一性,修改数据
普通索引,修改数据到change buffer,返回
普通和唯一索引的最大区别在于
数据不在内存时候,唯一索引要加载数据判断唯一性,普通直接存入change buffer,返回
-
change buffer 适用于普通索引和写多读少的场景,读的操作会出发数据merge操作
-
change buffer 的内存变化页记录在redo log中,是减少磁盘的读IO,redo log是物理逻辑日志,记录的是数据页的状态,减少磁盘的写IO
-
change buffer 除了在buffer pool中的内存拷贝,在系统表空间也有存储(ibdata1)中
-
change buffer 的merge操作是将change buffer 中的数据操作记录刷新到磁盘,保证数据的一致性
-
mysql 对数据的操作都是加载到内存中,以页的单位16k,此参数可以修改,扇区是512byte,一个block是4k
-
当有 where条件的操作,也是可以用到change buffer的,删除操作(当前索引标记为删除)更新动作是(插入和删除的结合)
-
缓存条件是
11. innodb_change_buffering值包括
all 默认值:缓冲池插入、删除标记操作和清楚
none 不做任何缓冲操作
inserts 缓冲区插入操作
deletes 缓冲区删除标记操作
changes 缓冲插入和删除标记操作(update)
purges 缓冲在后台发生的物理删除事件、
innodb_change_buffer_max_size 占用缓冲池大小比例 默认25 最大50%
-
读取数据的时候
后台线程定期刷新,默认10s
服务shutdown的时候
-
服务掉电,数据记录在系统表空间和change buffer 中,这时候redo log中的事务为prepare的时候,还没有commit的操作,重启后,此事务会丢失
-
唯一索引,update 和insert的时候不可以,delete的时候可以
官方文档 和 toabao mysql 月报 有说明(太牛逼了)
https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html
http://mysql.taobao.org/monthly/2015/07/01/
change buffer
就是将二级索引的insert、update、delete的操作记录放在change buffer中,避免此类操作频繁进行磁盘IO
change buffer会占用buffer pool的空间
在磁盘上会占用系统表空间
change buffer是存放二级索引的没有在buffer pool的变更页的缓存区,变更的buffer是由insert,update,delete等操作导致的。等页被加载进buffer pool中后会将change buffer中的页合并。
当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
需要说明的是,虽然名字叫作 change buffer,实际上它是可以持久化的数据。也就是说,change buffer 在内存中有拷贝,也会被写入到磁盘上。
将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会定期 merge。在数据库正常关闭(shutdown)的过程中,也会执行 merge 操作。
显然,如果能够将更新操作先记录在 change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用 buffer pool 的,所以这种方式还能够避免占用内存,提高内存利用率。
change buffer 触发merge条件
-
访问这个数据页会触发merge操作,将change buffer操作应用到原始数据页
-
后台线程会定期的merge操作,默认10s
-
数据库正常关闭(shutdown)的过程中,也会执行merge操作
change buffer 使用场景
-
change buffer 只限于用在普通索引的场景下,而不适用唯一索引
-
对于写少读多的场景不适合,每次读操作都会触发merge操作,反而起到副作用
-
适用于写多读少的场景
对于唯一索引来说,**所有的更新操作都要先判断这个操作是否违反唯一性约束。比如,要插入 (4,400) 这个记录,就要先判断现在表中是否已经存在 k=4 的记录,而这必须要将数据页读入内存才能判断。**如果都已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer 了。
因此,唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用。
首先会进行merge操作后才能判断
唯一索引不使用change buffer。唯一索引在执行更新操作时,要先判断是否符合唯一约束条件,那么在插入数据时会先从表中读取数据到内存中,然后判断是否符合唯一约束条件。所以唯一索引插入时肯定内存中会有这个数据页,即使没有也会查询磁盘数据保存到内存。所以内存中有数据页,直接更新即可,不需要保存到change buffer中。
参数
show variables like '%change_buffer%';
change buffer 和查询时候的数据怎么合并
page hash 来记录change buffer 中是否有此条记录
查询区别
查询语句
select id from T where k=5
这条查询语句在索引树上查找的过程,先是通过B+树丛树根开始,按层搜索到叶子节点,也就是图中右下角的这个数据页,然后通过数据也中的二分法来定位记录
-
对于普通索引来说,查找到满足条件的第一个记录 (5,500) 后,需要查找下一个记录,直到碰到第一个不满足 k=5 条件的记录。
-
对于唯一索引来说,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。
性能消耗的影响
微乎其微
因为数据树按照页的单位进行读写的,也就是说,当需要读一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。在 InnoDB 中,每个数据页的大小默认是 16KB。
因为引擎是按页读写的,所以说,当找到 k=5 的记录的时候,它所在的数据页就都在内存里了。那么,对于普通索引来说,要多做的那一次“查找和判断下一条记录”的操作,就只需要一次指针寻找和一次计算。
当然,如果 k=5 这个记录刚好是这个数据页的最后一个记录,那么要取下一个记录,必须读取下一个数据页,这个操作会稍微复杂一些。
但是,我们之前计算过,对于整型字段,一个数据页可以放近千个 key,因此出现这种情况的概率会很低。所以,我们计算平均性能差异时,仍可以认为这个操作成本对于现在的 CPU 来说可以忽略不计。
修改性能区别
如果要在这张表中插入一个新记录 (4,400) 的话,InnoDB 的处理流程是怎样的。
数据在内存中
- 对于唯一索引,找到3和5之间的位置,判断到有没有冲突,插入这个值.语句执行结束
- 对于普通索引,找到3和5之间的位置,插入这个值,语句执行结束
这样看来普通索引和唯一索引对更新语句性能的差别只是一个判断,置灰消耗微小的CPU时间
数据页不在内存中
- 对于唯一索引,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束;
- 对于普通索引来说,则是将更新记录在 change buffer,语句执行就结束了
将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。change buffer 因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。
change buffer 和redo log
mysql> insert into t(id,k) values(id1,k1),(id2,k2);
情况1
K1数据页子在内存中,k2数据也不再内存中
分析这条更新语句,你会发现它涉及了四个部分:内存、redo log(ib_log_fileX)、 数据表空间(t.ibd)、系统表空间(ibdata1)。
-
page1在内存中,直接更新内存
-
page2不在内存中,就在change buffer记录下”page2操作行的”信息
-
将这两个动作记录在redo log中,返回语句执行成功
同时,图中的两个虚线箭头,是后台操作,不影响更新的响应时间。
读请求
select * from t where k in (k1, k2)。
如果读语句发生在更新语句后不久,内存中的数据都还在,那么此时的这两个读操作就与系统表空间(ibdata1)和 redo log(ib_log_fileX)无关了。所以,我在图中就没画出这两部分。
-
读page1的时候,直接从内存返回.有几位同学在前面文章的评论中问到,WAL 之后如果读数据,是不是一定要读盘,是不是一定要从 redo log 里面把数据更新以后才可以返回?其实是不用的。你可以看一下图 3 的这个状态,虽然磁盘上还是之前的数据,但是这里直接从内存返回结果,结果是正确的。
-
要读page2的时候,数据不在内存中,读取磁盘数据和change buffer 操作日志进行应用,生成一个新的正确版本,返回数据
可以看到,直到需要读取page2的时候,这个数据页才被读取到内存中
所以,如果要简单地对比这两个机制在提升更新性能上的收益的话,redo log 主要节省的是随机写磁盘的 IO 消耗(转成顺序写),而 change buffer 主要节省的则是随机读磁盘的 IO 消耗。