一、ibuf_init_at_db_start
Creates the insert buffer data structure at a database startup and initializes the data structures for the insert buffer.
这样,我们除了有一些SYS_*表(参考上一篇,它们也由dict_sys管理),还有一个dict_sys,它的哈希表中有很多dict_table_t结构体,这些结构体的index链接着表的索引。对应insert buffer,就是dict_sys---> dict_table_t ----->indexes.
二、流程
参考:《MySQL Innodb Insert Buffer/Checkpoint/Aio 实现分析》by 何登成;例子使用和该文中同样的例子。
row_insert_for_mysql--->row_ins_step--->row_ins--->row_ins_index_entry_step(ins_node_t* node, thr)---->row_ins_index_entry(node->index, ...)---->row_ins_index_entry_low(index, ...)---->btr_cur_search_to_nth_level(index, ...)
需要注意的是,row_ins_index_entry_step中对表的index作了遍历,对每个index,都调用了>row_ins_index_entry。所以,row_ins_index_entry_low(index, ...)的参数index可能是聚集索引,也可能是辅助索引,是辅助索引的时候才涉及到insert buffer(因为insert buffer在索引不唯一时才可以使用。)
在row_ins_index_entry_low中,先调用btr_cur_search_to_nth_level,之后对于聚集索引,调用btr_cur_optimistic_insert()真正的在页中插入数据;而对于非唯一辅助索引,则直接退出,插入过程交给merge线程。
1、准备工作
首先创建表:
先向表中插入50000条数据,以保证索引有两层(不然根本不会使用insert buffer),使用以下方法;之后重启再插入一条数据(保证页不在buffer pool中)。 这样btr_cur_search_to_nth_level将会调用以下函数:ibuf_should_try、buf_page_get_gen、ibuf_insert 。
2、ibuf_should_try用于判断是否可以使用insert buffer。它由btr_cur_search_to_nth_level调用。如果可以使用insert buffer,则设置buf_mode = btr_op == BTR_DELETE_OP ? BUF_GET_IF_IN_POOL_OR_WATCH : BUF_GET_IF_IN_POOL,对于插入操作,即设置buf_mod为BUF_GET_IF_IN_POOL。
3、buf_page_get_gen
由于页不在buffer pool中,且buf_mod为BUF_GET_IF_IN_POOL,所以直接返回block = NULL。
4、ibuf_insert--->ibuf_insert_low
If a thread attempts to buffer an insert on a page while a purge is in progress on the same page, the purge must not be buffered, because it could remove a record that was re-inserted later. For simplicity, we block the buffering of all operations on a page that has a purge pending.
三、ibuf_insert_low
1、ibuf_entry_build()创建dtupl_t,即entry to insert into an ibuf index tree. 实际就是在原有dtupl_t上加了四个字段:space_id、marker、page number、type info。
2、btr_pcur_open(ibuf->index, ibuf_entry, PAGE_CUR_LE, mode, &pcur, &mtr)
ibuf->index为insert buffer的聚集索引,最终找到当前记录应该操作的 insert buffer 页面,操作位置记录在btr_pcur_t* pcur中,pcur->pos = BTR_PCUR_IS_POSITIONED,表示The persistent cursor is positioned by index search。其内部会调用btr_cur_search_to_nth_level()。
本例中首次使用insert buffer时,pcur中会指向page 4(保留页)中的首个记录(infimum),因为此时insert buffer中还没有数据。
3、ibuf_get_volume_buffered
Find out the volume of already buffered inserts for the same index page.
4、bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, &bitmap_mtr);
返回bitmap page,此例page_no(注意page_no是数据插入页,而不是ibuf中的页)较小,所以为第1页(保留页)。(问题:一个bitmap page,能表示多少个页的状态?16384)每个辅助索引页在bitmap页中占有4位:IBUF_BITMAP_FREE(2):0表示为可用空间,1表示剩余空间大于1/32页,2表示大于1/16,3表示大于1/8;IBUF_BITPAM_BUFFERED:1表示该索引页有记录被缓存在ibuf中;IBUF_BITMAP_IBUF:1表示该页为insert buffer的索引页。
5、根据 bitmap,计算索引页面中的空余空间,是否足够存放当前记录,并且不引起页面分裂。之后更新bitmap。
6、 btr_cur_optimistic_insert
其中btr_cur_ins_lock_and_undo() Check locks and write to the undo log, if specified(不过对于insert buffer,不会写undo log,此处只有聚集索引才会写undo log);page_cur_tuple_insert() Inserts a record next to page cursor, returns pointer to inserted record if succeed.
四、merge
概括地说,Merge Insert Buffer的操作可能发生在以下几种情况下: 辅助索引页被读取到缓冲池时; Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时; Master Thread。
第一种情况为当辅助索引页被读取到缓冲池中时,例如这在执行正常的SELECT查询操作,这时需要检查Insert Buffer Bitmap页,然后确认该辅助索引页是否有记录存放于Insert Buffer B+树中。若有,则将Insert Buffer B+树中该页的记录插入到该辅助索引页中。可以看到对该页多次的记录操作通过一次操作合并到了原有的辅助索引页中,因此性能会有大幅提高。
Insert Buffer Bitmap页用来追踪每个辅助索引页的可用空间,并至少有1/32页的空间。若插入辅助索引记录时检测到插入记录后可用空间会小于1/32页,则会强制进行一个合并操作,即强制读取辅助索引页,将Insert Buffer B+树中该页的记录及待插入的记录插入到辅助索引页中。这就是上述所说的第二种情况。
还有一种情况,在Master Thread线程中每秒或每10秒会进行一次Merge Insert Buffer的操作,不同之处在于每次进行merge操作的页的数量不同。
以下从另一个角度来看merge:
1、master thread merge——主动merge
(1)先进行异步IO:ibuf_contract_for_n_pages(10个索引页)->ibuf_contract_ext,随机定位一个insert buffer 的页面,使用fil_io将该页面涉及的索引页读出(异步IO);
(2)再进行merge:io_handler_thread-->fil_aio_wait->buf_page_io_complete--->ibuf_merge_or_delete_for_page,判断当前页面是否存在insert buffer项(应当存在),在insert buffer中查找与该页相关的第一条记录,将这些记录插入的该索引页中,并删除在ibuf中的记录,之后设置ibuf bitmap。
以下为被动merge:
2、insert操作导致页面空间不足,或update导致页面空间不足,或purge导致页面为空,都会导致被动merge,因为insert buffer只能针对单页面,不能进行page split。
3、进行insert buffer操作时,发现insert buffer已经太大,ibuf_insert_low-->ibuf_contract(sync=true)(操作同步IO,不允许insert操作进行)。同样是随机定位一个insert buffer页面,将该页面中的所有更新合并到索引页中。
4、其他。