zoukankan      html  css  js  c++  java
  • 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 statusG
    ...
    -------------------------------------
    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>
  • 相关阅读:
    MongoCola使用教程 1 MongoDB的基本操作和聚合功能
    [教程]MongoDB 从入门到进阶 (TextSearch)
    MongoCola使用教程 2 MongoDB的Replset 初始化和配置
    [教程]MongoDB 从入门到进阶 (aggregation数据库状态)
    [教程]MongoDB 从入门到进阶 (概要 以及 高级索引篇 TimeToLive GeoNear)
    C#多线程函数如何传参数和返回值
    QQ邮箱 C# 发邮件 常见错误异常
    关于c#中的Timer控件的简单用法
    要想使用线程 想去方法 应该传入object 传参
    quartz给任务传参数以及维持任务的状态
  • 原文地址:https://www.cnblogs.com/xuwc/p/14039604.html
Copyright © 2011-2022 走看看