MySQL 记录、页、索引的数据结构简析( 七 )

到这里就定位到了数据插入的位置,开始真正的数据插入 。
dtuple_t* entry -> byte调用 btr_cur_optimistic_insert 函数乐观插入 。
page_cur_t* page_cursor; buf_block_t* block;// 通过 cursor 获取 block block = btr_cur_get_block(cursor); // buf_block_t::frame 中保存 page page = buf_block_get_frame(block); index = cursor->index; // btr_cur_t->page_cursor page_cursor = btr_cur_get_page_cur(cursor); /* Check locks and write to the undo log, if specified */// 真正插入 entry 前,会检查事务锁和记录 undo// 其中修改 inherit 值,如果下一条记录上没有锁,就不需要锁分裂err = btr_cur_ins_lock_and_undo(flags, cursor, entry,thr, mtr, &inherit);if (err != DB_SUCCESS) {goto fail_err;}// 插入数据*rec = page_cur_tuple_insert(page_cursor, entry, index, offsets, heap,n_ext, mtr);其中:

  • buf_block_t::frame 是记录要插入的数据页,page_cursor 是记录要插入的位置;
  • 插入数据前调用 btr_cur_ins_lock_and_undo 函数检查是否有与插入意向锁冲突的锁、是否需要发生锁分裂;
  • 调用 page_cur_tuple_insert 函数插入数据,逻辑记录保存在 entry 中 。
page_cur_tuple_insert 函数中调用 rec_convert_dtuple_to_rec_new 函数将逻辑记录转换成物理记录 。
/*********************************************************//**Builds a new-style physical record out of a data tuple andstores it beginning from the start of the given buffer.@return pointer to the origin of physical record */staticrec_t*rec_convert_dtuple_to_rec_new(/*==========================*/ byte*buf, /*!< in: start address of the physical record */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t*dtuple) /*!< in: data tuple */{ ulint extra_size; ulint status; rec_t* rec; // 计算记录头大小,记录头大小用 extra_size 表示 rec_get_converted_size_comp(index, status, dtuple->fields, dtuple->n_fields, &extra_size); // 因为 buf 是用来存储整个记录的开始位置的,这里的 buf + extra_size 表示存储的第一个列的位置,即 rec 所指的位置 rec = buf + extra_size; // 真正转换格式 rec_convert_dtuple_to_rec_comp(rec, index, dtuple->fields, dtuple->n_fields, NULL,status, false);}其中:
  • 调用 rec_convert_dtuple_to_rec_comp 函数将 tuple 逻辑记录转换成物理记录 。
rec_convert_dtuple_to_rec_comp 函数中将每个列的值,以及 null 和 len 的信息存储到对应的位置 。
/* Store the data and the offsets */ // 将每个列的值,以及 null 和 len 的信息存储到对应的位置 for (i = 0; i < n_fields; i++) {const dict_field_t* ifield;dict_col_t*col = NULL;// 获取每个 field 的值field = &fields[i];// 计算 null 信息,因为这个标志是通过位来存储的,所以对每一个字节都需要做位处理// 计算列的大小和存储其长度字节数动态匹配的位置,比如判断变长列的长度占用1个字节或2个字节// 将元组列信息,写入到 compact 记录的对应列中 , len为其对应的存储长度memcpy(end, dfield_get_data(field), len);}最终调用 page_cur_insert_rec_low 函数将物理记录写入文件 。
// 真正插入记录rec = page_cur_insert_rec_low(cursor->rec,index, rec, *offsets, mtr);到这里就完成了一条数据的插入 。
总结insert 过程中用到了多个数据结构进行数据的传递 。
 
其中:
  • row_prebuilt_t结构体中保存有关某个表操作相关信息 , 其中包括ins_node_t;
  • ins_node_t结构体中保存插入操作相关的相关信息,其中包括dtuple_t;
  • dtuple_t结构体中保存逻辑记录,也就是索引元组,其中包括dfield_t;
  • dfield_t结构体中保存字段信息,dfield_t::data中保存真实列数据的指针;
  • btr_pcur_t结构体中包括btr_cur_t;
  • btr_cur_t结构体中包括page_cur_t;
  • page_cur_t结构体中保存查询得到的记录 rec,其中包括buf_block_t;
  • buf_block_t结构体中保存数据页指针 frame , 其中包括buf_page_t 。
insert 过程中行记录格式发生了多次转换 。
其中: