InnoDB什么时候使用插入缓冲 insert buffer 了,怎么验证 -- InnoDB存储梳理(四)

查询 change buffer 的操作记录

SELECT NAME,SUBSYSTEM,COUNT,MAX_COUNT,MIN_COUNT,AVG_COUNT,COUNT_RESET,MAX_COUNT_RESET,MIN_COUNT_RESET,AVG_COUNT_RESET FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE 'ibuf%';

查询表空间的页面信息,xx换成你表空间的ID,不会找,看看我前面的博客 缓冲池里有什么

select SPACE,PAGE_NUMBER,PAGE_TYPE,FLUSH_TYPE,NEWEST_MODIFICATION,TABLE_NAME,INDEX_NAME,NUMBER_RECORDS,PAGE_STATE,IO_FIX,IS_OLD from INFORMATION_SCHEMA.innodb_buffer_page where space=xx;

查询表空间页面的统计信息

SELECT PAGE_TYPE,TABLE_NAME,INDEX_NAME,COUNT(*) AS PAGES FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE where SPACE=xx or page_type='ibuf_index' GROUP BY PAGE_TYPE,TABLE_NAME,INDEX_NAME;

查询信息,这个是不走索引的逻辑,会把页都占用了;把二级索引页清理掉;只要缓冲池中不存在二级索引页,我们做一次insert操作,必然会走change buffer

select * from test2 where uid='yuan0019' limit 1000;

第一次新增操作

第一次查询表空间中加载进来的页面,没有索引信息
在这里插入图片描述

我们做一次insert操作

INSERT INTO `test2` (`job_name`, `finish`, `uid`, `external1`, `external2`, `external3`, `external4`, `external5`, `create_time`) VALUES('random019982', 0, 'yuan0011982', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', '2024-08-04 18:09:35');

在这里插入图片描述

再次查询表空间加载进来的信息,有二级索引信息被加载进来了,而且多了一个IBUF_INDEX类型的页,就是使用了change buffer
在这里插入图片描述

表空间被载入的详细信息,我们找到这几个页号,后面把所有的索引信息,找出来,然后再insert一条change buffer 里面没有的索引数据,再看看各个表的变化
在这里插入图片描述
把几页的索引信息都读取出来了
在这里插入图片描述

再看INNODB_METRICS表信息,insertchange buffer中一次,并且合并一次
在这里插入图片描述

第二次新增操作

我们再做一次insert操作

INSERT INTO `test2` (`job_name`, `finish`, `uid`, `external1`, `external2`, `external3`, `external4`, `external5`, `create_time`) VALUES('wuyew019982', 0, 'yuan0011982', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', '2024-08-04 18:09:35');

比上一次多加载了一个索引页进来,我们查一查这一页的索引信息,发现新插入的索引列值在该页中

但是change buffer的操作没有增,这一次insert没有使用change buffer。上一次的ibuf_index页的LSN号也是原来的,新加载进来的二级索引页的LSN比他的要大,确实没用,直接加载出来写进去了。

我猜这个应该是 23760 这一页是因为已有的索引页增加wuyew019982,需要分裂而加载进来的;这个不会做验证…
在这里插入图片描述
在这里插入图片描述
再看INNODB_METRICS表信息,还是insertchange buffer中一次,并且合并一次
在这里插入图片描述

第三次新增操作

INSERT INTO `test2` (`job_name`, `finish`, `uid`, `external1`, `external2`, `external3`, `external4`, `external5`, `create_time`) VALUES('e31a019982', 0, 'yuan0011982', 'aaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', '2024-08-04 18:09:35');

在这里插入图片描述

这次这个索引值要保存的页不是已有的三个页,所以走了change buffer;比上一次多加载了两个索引页进来,只有一个索引页的LSNIBUF_INDEX一样,那么索引数据肯定在这一页里面,打开数据页查一查,确实是有这个数据。我事务还没有提交,表空间的页里面有这个数据了

没有提交的事务,也会把数据放到索引页中,我们能直接看到数据
在这里插入图片描述
在这里插入图片描述

再看INNODB_METRICS表信息,这次变成2次了,这次的insert使用了change buffer
在这里插入图片描述
再使用没有索引的表,做一下这些操作,发现 change buffer 是一直不会有操作记录的。
还是有不少细节理解不清楚的,以后再看!!!

读取表空间二级索引页的所有索引信息

https://github.com/yjysanshu/py_innodb_page_info


INNODB_PAGE_SIZE = 16 * 1024


def read_page(file_path, page_no):
    with open(file_path, 'rb') as f:
        f.seek(page_no * INNODB_PAGE_SIZE)
        page = f.read(INNODB_PAGE_SIZE)
    return page


def read_index_name(page_data):
    # 把读取到的数据转成字节数组
    byte_arr = bytearray(page_data)

    # 前面92个字节是 File Header 和 Page Header,从第93个字节开始
    offset = 93
    # 读取 infimum recorder header 的头记录最后两个字节是第一条记录的偏移量
    # 最后2个字节才是第一条数据的偏移位置,直接先读取到第一个记录信息,基本上是固定的位置了
    # first_offset = int(hex_str[(offset + 5)*2:(offset + 6)*2], 16)
    first_offset = int.from_bytes(byte_arr[(offset + 5):(offset + 6)], byteorder='big')

    index_vale_map = {}
    index_char_offset = 93 + first_offset
    while True:
        # 当前索引的字符串长度
        char_len = int.from_bytes(byte_arr[index_char_offset:(index_char_offset + 1)], byteorder='big')
        # 如果下一行的开始是0,说明我们已经读完了,我们这测试就不那么严谨了;正常的找偏移节点的,会跳过了很多,就用这个笨办法了
        if char_len == 0:
            break
        cur_offset = char_len + 8 + 6  # 字符串长度+主键索引+(头信息+1个字符串长度)
        finish_char_offset = index_char_offset + cur_offset
        chunk = byte_arr[index_char_offset:finish_char_offset]

        # 读取索引列数据、和主键数据
        index_value_hex = chunk[6:(char_len + 6)]
        index_value = ''.join(chr(byte) if 32 <= byte <= 126 else '.' for byte in index_value_hex)
        if index_value not in index_vale_map:
            index_vale_map[index_value] = []
        index_vale_map[index_value].append(int.from_bytes(chunk[(char_len + 8):(char_len + 6 + 8)], byteorder='big'))

        # 输出读取到的一行数据
        # hex_chunk = " ".join(f"{byte:02x}" for byte in chunk)
        # print(f"{index_char_offset:08x} {hex_chunk:<39}")

        index_char_offset = index_char_offset + cur_offset

    for item in index_vale_map:
        print(item + ": " + str(index_vale_map[item]))
    return index_vale_map.keys()


def start():
    # 修改这个页号信息,INFORMATION_SCHEMA.INNODB_BUFFER_PAGE 表中的 PAGE_NUMBER
    page_nos = [23760]
    file_path = '/usr/local/mysql/data/test/test2.ibd'
    index_names = []
    for page_no in page_nos:
        page_data = read_page(file_path, page_no)
        # hexdump(page_data, page_no)

        index_names.extend(read_index_name(page_data))
    print(index_names)


if __name__ == '__main__':
    start()

把自己坑了,一条数据是索引列为null的,找了半天还以为这里还有隐藏的什么东西。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三书yjy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值