MySQL中的change buffer

参考:

https://blog.csdn.net/weixin_38304221/article/details/88635432

https://blog.csdn.net/nanjingitany/article/details/102856472

https://www.cnblogs.com/abclife/p/7625281.html

https://www.cnblogs.com/ging/p/13467833.html

 

 

 

 

 

MySQL中的change buffer

MySQL的一条语句,大致流程查看内存→读取磁盘数据页→返回数据。

当比如查找一个 a=5的记录的时候,并不是只查找出这一条数据,它所在的整个数据页都会查找出来(每个数据页16KB)。

下次查找a=6的记录的时候,发现该页已经在内存中了,直接返回,不需要磁盘IO。

但是当时增、删、改操作时,并不会每一次操作都进行一次磁盘IO,使用change buffer可以降低磁盘随机IO。

change buffer首先是可持久化的数据。

当更新某个数据页时,该页在内存中,那么直接更新。

如果该页不在内存中,那么先将更新操作记录在change buffer中,这时不需要从磁盘中读出数据页。

将操作记录到redo log中,防止机器意外关闭导致数据丢失。

这时已经可以返回给客户端更新成功了,因为即使机器意外重启,也可以通过redo log找回数据。

change buffer 使用的是 buffer pool里的内存,不能无限增大,可以通过参数 innodb_change_buffer_max_xize来动态设置,这个参数为50的时候,标识change buffer 的大小最多只能占用 buffer pool 的50%。

什么时候将change buffer中的数据更新到磁盘中?

当下一次查询命中这个数据页的时候,会先从磁盘中读取数据页到内存中,然后先执行change buffer的merge操作,保证数据逻辑的正确性。除了查询操作外,系统有后台线程会定期merge,数据库正常关闭(shutdown)的时候,也会进行merge操作。

 

change buffer 和 redo log 对于磁盘的随机IO影响。

redo log是减少 随机写磁盘IO 的消耗。每个操作先记录redo log,系统空闲时或redo log满时进行磁盘IO。

change buffer是减少 随机读磁盘IO 的消耗。更新时如果内存中不存在该数据页,也不需要马上进行磁盘IO,而是先记录在change buffer中,等待时机统一merge

 

所以根据业务场景选择是否使用change buffer。

当写少读多的情况下:change buffer的利用率不高,因为可能刚一更新完可能就触发merge操作,change buffer没有起到减少随机IO,还多了一个维护change buffer的成本。

当写多读少的情况下:change buffer的提升效果较明显,可能很多条更新后,也没有一个查询线程来触发merge操作,可以大幅减少磁盘的随机IO。

 

唯一索引和 普通索引(在业务逻辑层保证数据唯一)的选择

命中唯一索引和普通索引时MySQL筛选数据的逻辑不同。

比如一个查询语句 select * from table where k=5;先通过B+树从树根开始,按层搜索到叶子节点,也就是数据页。

唯一索引 k:由于索引定义了唯一性,所以在数据页中找到第一个满足条件的记录后,直接返回。

普通索引 k:没有定义索引的唯一性,所以在查找到第一条满足k=5后,还会继续遍历下一条,直到查到的k != 5的时候,结束遍历。

那么两种索引的效率差距有多大呢?

其实是很小,几乎可忽略。普通索引相对于唯一索引多的一步操作就是"查找并判断下一条记录",因为都存在于同一个数据页,所以内存遍历对于CPU的开销可忽略。一个数据页可以存在上千个key,刚好下一条数据在下一个数据页而触发再次读取数据页的概率很低。所以这两种索引的效率几乎一样。

但是当进行增删改操作时,两种索引的效率就有明显的差距了。

因为唯一索引每个插入或者更新都要判断是否违反唯一约束,所以每次更新都要从内存中找到这个记录对应的数据页,没有的话需要从磁盘中读出,反正都要从磁盘读数据页了,还不如直接更新内存/磁盘,那么也就意味着change buffer失去意义,所以唯一索引使用change buffer 的话反倒会降低效率,所以只有普通索引能使用到change buffer。

 

 

 

 

 

 

 

浅谈mysql的change buffer

1.数据的更新过程

当mysql执行更新操作时,需要先更新一个数据页。如果这个数据页在内存中,则会直接更新,如果
这个数据页不在内存中,InnoDB会将这个更新操作缓存在change buffer中,当下次需要查询访问这个数据页的时候,会将数据页读入内存,然后执行change buffer中和这个数据页相关的操作,这样就保证了数据的逻辑正确性。

2.merge触发

将change buffer中的操作应用到原数据页,得到最新结果的过程称为merge。除了访问这个数据页会触发merge外,系统有后台线程会定期merge。在数据库正常关闭(shutdown)的过程中,也会执行merge操作。

3.使用change buffer的场景

只有普通索引的更新会使用change buffer,用来提高内存利用率,但是如果被更新的字段是唯一索引,就不会使用change buffer,因为更新操作会判断是否违反唯一约束的条件,必须将数据页读入内存才能判断,因此唯一索引不会使用change buffer,只有普通索引才能使用。
在日常业务场景中,对于写多读少的操作,使用change buffer的效果最好,例如账单类、日志类的操作,如果是读写操作都是很多的情况下,写完后将操作记录保存在change buffer中然后立即查询,这样会频繁触发merge,增加了IO的操作频率,这种场景使用change buffer并不能带来性能的提升,这种业务场景应该关闭change buffer。

4.change buffer的大小设置

在InnoDB中,可以通过参数innodb_change_buffer_max_size来设置,默认值是25,最大是50。例如当这个参数值是50 的时候,表示change buffer最多只能占用buffer pool的50%

5.索引的选择

唯一索引和普通索引在查询上并无区别,但是在更新操作上两者对性能的影响是不一样的,所以建议尽量使用普通索引。

 

 

 

 

 

 

MySQL-对Change Buffer的理解

Change Buffer的处理过程

非唯一普通索引的新增或更新操作,如果索引B+树的需要新增或更新的数据页不在内存中,则直接更新change buffer,等到后面需要使用这个数据页(真正读到内存中来)的时候,再根据change buffer在内存中做merge合并操作

Change Buffer有什么好处?

先想想没有change buffer时候,在缓冲池中没有对应数据页时会怎么更新。概括来说,有两个步骤:

  1. 首先需要从磁盘中读取对应的数据页到内存中
  2. 然后更新内存中的数据页。

首先分析主键索引或者非主键索引中的唯一索引,插入或者更新的操作。

  • 主键如果是自增的,只需要读取顺序读取磁盘中的页,然后插入最新的行即可。主键如果是非自增的或者是自己设置的值,那么可能需要做一次随机磁盘IO操作,读取到对应的页,做一下唯一性判断,然后插入数据即可。
  • 针对非主键索引中的唯一索引,大概率需要做随机磁盘IO读取,然后判断唯一性,再插入对应的行。

所以对于主键索引和非主键的唯一索引,因为有唯一性判断,所以更新操作时,必须要从磁盘中读取数据页,判断唯一性,然后才能确定这个更新操作是否成功,即这个磁盘的IO操作是不可避免的。

对于非唯一索引来说,其实步骤也是类似的。但是因为不需要做唯一性判断,所以为了提高更新的性能,Mysql给出的解决方案就是使用change buffer来保存对非唯一索引的更新。也就是说,当需要更新非唯一索引时,直接操作change buffer,成功即可返回。

那么什么时候会真正更新数据页呢?有两种情况会触发:

  1. 被动:在后续的真正需要读这个非唯一索引时,把索引的数据页从磁盘读取到内存中,再通过change buffer做一个merge操作,merge操作以后,内存中的数据页就是最新的了。
  2. 主动:innoDB引擎中有线程会主动的定期做merge操作
业务实践
  1. 利用普通索引的change buffer特性,当业务场景中的写远大于读时,常见场景为日志表,当某些列必须建立索引时,可以考虑建立普通索引,提高写入性能
  2. 如果业务场景的写之后立即伴随读,如果列的值是唯一的
  • 那么其实建立普通索引是不合适的,因为写的过程,虽然利用了change buffer暂时提高了写的性能,但是在读的时候还是需要磁盘IO。可以考虑建立唯一索引,在索引写的时候,就提前读取数据到缓冲池中,提高读的性能。

 

 

 

 

 

MySQL -- Innodb中的change buffer

change buffer是一种特殊的数据结构,当要修改的辅助索引页不在buffer pool中时,用来cache对辅助索引页的修改。对辅助索引页的操作可能是insert、update和delete操作。等到相关的索引页被读入buffer pool中后,才会使用change buffer中的内容对辅助索引页进行修改(即merge操作)。

和聚集索引不同,辅助索引通常是不唯一的,插入辅助索引通常也是随机的。同样,对辅助索引的删除、更新也通常是不连续的。

等到相关的索引页被读入buffer pool中后,才会使用change buffer中的内容对辅助索引页进行修改(即merge操作)可以避免大量的磁盘随机访问I/O。

间歇性的,在系统空闲或关闭过程中,会执行purge操作,将新的索引页写入磁盘。purge操作一次写多个索引值会比每次修改后就立即写入磁盘的效率高。

对change buffer的merge可能需要好几个小时,如果被更新的辅助索引行比较多。在merge过程中,磁盘的I/O会增加,可能会引起其他查询的性能的降低。
merge操作也可能发生在事务提交后。事实上,即使在实例重启后,还会可能发生merge操作。

在内存中,change buffer会占用buffer pool的空间;在物理磁盘上,change buffer是system tablespace的一部分,所以对索引的修改在数据库重启后仍然存在change buffer中。

change buffer包含的特性也叫作change buffering,包含insert buffering、delete buffering、purge buffering。

change buffer中数据类型和总量由参数innodb_change_buffering和innodb_chagne_buffer_max_size配置。查看change buffer中数据的信息,可以通过show engine innodb status查看。

change buffer在老版本中被称作insert buffer。

 

1.监控change buffer 

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> show engine innodb status\G
...
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 4425293, used cells 32, node heap has 1 buffer(s)
13577.57 hash searches/s, 202.47 non-hash searches/s
...

2.查看information_schema
information_schema.innodb_metrics提供了change buffer的统计信息名称和说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
mysql> select name, comment from information_schema.innodb_metrics where name like '%ibuf%';
+-----------------------------------------+-------------------------------------------------------------+
name                                    | comment                                                     |
+-----------------------------------------+-------------------------------------------------------------+
| buffer_page_read_index_ibuf_leaf        | Number of Insert Buffer Index Leaf Pages read               |
| buffer_page_read_index_ibuf_non_leaf    | Number of Insert Buffer Index Non-Leaf Pages read           |
| buffer_page_read_ibuf_free_list         | Number of Insert Buffer Free List Pages read                |
| buffer_page_read_ibuf_bitmap            | Number of Insert Buffer Bitmap Pages read                   |
| buffer_page_written_index_ibuf_leaf     | Number of Insert Buffer Index Leaf Pages written            |
| buffer_page_written_index_ibuf_non_leaf | Number of Insert Buffer Index Non-Leaf Pages written        |
| buffer_page_written_ibuf_free_list      | Number of Insert Buffer Free List Pages written             |
| buffer_page_written_ibuf_bitmap         | Number of Insert Buffer Bitmap Pages written                |
| ibuf_merges_insert                      | Number of inserted records merged by change buffering       |
| ibuf_merges_delete_mark                 | Number of deleted records merged by change buffering        |
| ibuf_merges_delete                      | Number of purge records merged by change buffering          |
| ibuf_merges_discard_insert              | Number of insert merged operations discarded                |
| ibuf_merges_discard_delete_mark         | Number of deleted merged operations discarded               |
| ibuf_merges_discard_delete              | Number of purge merged  operations discarded                |
| ibuf_merges                             | Number of change buffer merges                              |
| ibuf_size                               | Change buffer size in pages                                 |
| innodb_ibuf_merge_usec                  | Time (in microseconds) spent to process change buffer merge |
+-----------------------------------------+-------------------------------------------------------------+
17 rows in set (0.00 sec)
 
mysql>

information_schema.innodb_buffer_page提供了buffer pool中每个页的元数据,包含change buffer 索引页和change buffer位图页。
change buffer页中page_type.ibuf_index表示change buffer索引页;page_type.ibuf_bitmap表示change buffer位图页。

提醒:查看innodb_buffer_page会有很大的性能开销。最好是在空闲时间或测试环境执行。

比如,可以查看change buffer的索引页和位图页占据buffer pool的比例:

1
2
3
4
5
6
7
8
9
10
11
mysql> select (select count(*) from information_schema.innodb_buffer_page
    ->        where page_type like 'ibuf%'as change_buffer_pages,
    ->        (select count(*) from information_schema.innodb_buffer_page) as total_pages,
    ->        (select ((change_buffer_pages/total_pages)*100))
    ->        as change_buffer_page_percentage;
+---------------------+-------------+-------------------------------+
| change_buffer_pages | total_pages | change_buffer_page_percentage |
+---------------------+-------------+-------------------------------+
|                  14 |        8192 |                        0.1709 |
+---------------------+-------------+-------------------------------+
1 row in set (0.05 sec)

performance_schema还为高级性能监控提供了change buffer mutex的等待指令:

1
2
3
4
5
6
7
8
9
10
mysql> SELECT FROM performance_schema.setup_instruments
    ->        WHERE NAME LIKE '%wait/synch/mutex/innodb/ibuf%';
+-------------------------------------------------------+---------+-------+
NAME                                                  | ENABLED | TIMED |
+-------------------------------------------------------+---------+-------+
| wait/synch/mutex/innodb/ibuf_bitmap_mutex             | NO      NO    |
| wait/synch/mutex/innodb/ibuf_mutex                    | NO      NO    |
| wait/synch/mutex/innodb/ibuf_pessimistic_insert_mutex | NO      NO    |
+-------------------------------------------------------+---------+-------+
rows in set (0.00 sec)

3.配置change buffer

可以通过参数innodb_change_buffering来控制是否启用change buffer。

1
2
3
4
5
6
--all:      默认值。开启buffer inserts、delete-marking operations、purges
--none: 不开启change buffer
--inserts:  只是开启buffer insert操作
--deletes:  只是开delete-marking操作
--changes:  开启buffer insert操作和delete-marking操作
--purges:   对只是在后台执行的物理删除操作开启buffer功能

从5.6.2开始,参数innodb_change_buffer_max_size设置了change buffer可以占用buffer pool的百分比,默认是25,最大可以设置为50。

1
2
3
4
5
6
7
8
9
mysql> show variables like 'innodb_change_buffer_max_size';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
+-------------------------------+-------+
1 row in set (0.00 sec)
 
mysql>

 

posted on 2020-11-25 23:20  秦羽的思考  阅读(1227)  评论(0编辑  收藏  举报