背景
InnoDB 作为目前 MySQL 的主要存储引擎,其中 record 细节信息繁琐,这里仅做整理以便查阅. 版本 MySQL-8.0.25.
数据结构
InnoDB record 的逻辑格式: dtuple_t
/** Structure for an SQL data tuple of fields (logical record) */ |
MySQL SQL 层的 record 可以通过row_sel_convert_mysql_key_to_innobase()
转换为 InnoDB 可识别的dtuple_t
结构.
索引内存结构: dict_index_t
- index->table->n_cols: table 的列数,包含用户定义的列 + 3 列系统列(DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR).
- index->table->cols: 存上面 n_cols 个列的数组, 系统列在倒数后3个.
- index->n_fields: 当前索引包含的列数,小于等于上面的 index->table->n_cols.
index->fields: 记录当前索引 column 的描述信息, 列名,长度, 顺序 or 倒序
对于主键索引 leaf node:
- 如果定义了主键, 那么系统列就没有 DB_ROW_ID,那么此时 n_fields 比 n_cols 小 1.
- 如果没有定义主键, 那么系统列就包含 DB_ROW_ID,那么此时 n_fields 和 n_cols 值一样.
对于主键索引 non-leaf node:
- n_fields 包含所有唯一字段 + Page NO, 数量为 index->n_uniq + 1.
对于二级索引 leaf node:
- n_fields 就是包含二级索引定义的列数 + 主键列数.
对于二级索引 non-leaf node:
- n_fields 就是包含二级索引定义的列数 + Page No, 数量为 index->n_fields + 1.
使用dict_index_build_node_ptr()
构建 non-leaf node.
InnoDB 物理 record: rec_t
offsets 数组由rec_get_offsets()
, 数组大小由 n_fields + 1 + REC_OFFS_HEADER_SIZE 决定.
- offsets[0] = n_alloc / n_alloc 是数组元素个数. /
- offsets[1] = n_fields / n_fields 是 record 列数. /
- offsets[2] = extra size
- offsets[3.. 3 + n_fields] / 记录每个 field 的结束偏移. /
rec_t
可以直接通过cmp_dtuple_rec_with_match_low()
与dtuple_t
比较:
rec_t
可以通过 offsets 数组分别获取对应的 filed 字段, 再与((dfield_t *)tuple->fields + n)
直接进行比较.
B-tree 游标: btr_pcur_t
btr_pcur_t
是在 search 或者 modify 过程中用来定位的游标, 其中记录定位信息, 可以直接通过store_position()
来保存,通过restore_position()
可以直接恢复上一次保存的位置信息.
struct btr_pcur_t { |
store_position()
保存位置信息, 并释放 page 的 mutex, restore_position()
先尝试乐观加锁,即直接判断m_modify_clock
是否变化,假如 b+ tree 发生了 SMO, 需要进行悲观加锁的方式,即通过btr_cur_search_to_nth_level()
重新 search 加锁.
store_position()
会记录buf_block_t
, 在乐观恢复中直接通过尝试对buf_block_t
加锁,当前的 Buffer Pool 支持动态 resize, 这部分的内存可能会被释放, 所以 InnoDB 会首先判断这个buf_block_t
指针是否存在于 Buffer Pool 的 chunk 中:
void Block_hint::buffer_fix_block_if_still_valid() { |