查询 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
表信息,insert
到change 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
表信息,还是insert
到change 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
;比上一次多加载了两个索引页进来,只有一个索引页的LSN
和IBUF_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
的,找了半天还以为这里还有隐藏的什么东西。