创建聚集索引

2023-11-16

一、ibuf_init_at_db_start

Creates the insert buffer data structure at a database startup and initializes the data structures for the insert buffer.

[cpp]  view plain  copy
  1. void  
  2. ibuf_init_at_db_start(void)  
  3. /*=======================*/  
  4. {  
  5.     page_t*     root;  
  6.     mtr_t       mtr;  
  7.     dict_table_t*   table;  
  8.     mem_heap_t* heap;  
  9.     dict_index_t*   index;  
  10.     ulint       n_used;  
  11.     page_t*     header_page;  
  12.     ulint       error;  
  13.   
  14.     ibuf = mem_alloc(sizeof(ibuf_t)); //The insert buffer control structure  
  15.   
  16.     memset(ibuf, 0, sizeof(*ibuf));  
  17.   
  18.     /* Note that also a pessimistic delete can sometimes make a B-tree 
  19.     grow in size, as the references on the upper levels of the tree can 
  20.     change */  
  21.   
  22.     ibuf->max_size = buf_pool_get_curr_size() / UNIV_PAGE_SIZE  
  23.         / IBUF_POOL_SIZE_PER_MAX_SIZE;  //此处为2,即设置最大为buffer pool的一半。  
  24.   
  25.     mutex_create(ibuf_pessimistic_insert_mutex_key,  
  26.              &ibuf_pessimistic_insert_mutex,  
  27.              SYNC_IBUF_PESS_INSERT_MUTEX);  
  28.   
  29.     mutex_create(ibuf_mutex_key,  
  30.              &ibuf_mutex, SYNC_IBUF_MUTEX);  
  31.   
  32.     mutex_create(ibuf_bitmap_mutex_key,  
  33.              &ibuf_bitmap_mutex, SYNC_IBUF_BITMAP_MUTEX);  
  34.   
  35.     mtr_start(&mtr);  
  36.   
  37.     mutex_enter(&ibuf_mutex);  
  38.   
  39.     mtr_x_lock(fil_space_get_latch(IBUF_SPACE_ID, NULL), &mtr);  
  40.   
  41.     header_page = ibuf_header_page_get(&mtr); //使用buf_page_get_gen()从space0:page3读取。  
  42.   
  43.     fseg_n_reserved_pages(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, //此处是fseg_header(ibuf_header中只有这一项),即指向root page的inode  
  44.                   &n_used, &mtr);  //Calculates the number of pages reserved by a segment, and how many pages are currently used.  
  45.     ibuf_enter(&mtr);  
  46.   
  47.     ut_ad(n_used >= 2); //n_used表示正在使用使用的page,此例正好为2  
  48.   
  49.     ibuf->seg_size = n_used;  
  50.   
  51.     {  
  52.         buf_block_t*    block;  
  53.   
  54.         block = buf_page_get(  
  55.             IBUF_SPACE_ID, 0, FSP_IBUF_TREE_ROOT_PAGE_NO, //page 4是insert buffer的root page  
  56.             RW_X_LATCH, &mtr);  
  57.         buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);   
  58.   
  59.         root = buf_block_get_frame(block);  
  60.     }  
  61.   
  62.     ibuf_size_update(root, &mtr); //此例更新ibuf->free_list_len=0,ibuf->height=1,ibuf->size=1(只有根页)  
  63.     mutex_exit(&ibuf_mutex);  
  64.   
  65.     ibuf->empty = (page_get_n_recs(root) == 0); //Gets the number of user records on page (the infimum and supremum records are not user records).  
  66.     ibuf_mtr_commit(&mtr);                      //此例ibuf->empty=1,确实为空。  
  67.   
  68.     heap = mem_heap_create(450);  
  69.   
  70.     /* Use old-style record format for the insert buffer. */  
  71.     table = dict_mem_table_create(IBUF_TABLE_NAME, IBUF_SPACE_ID, 1, 0); //IBUF_TABLE_NAME为"SYS_IBUF_TABLE",1表示表的列数  
  72.   
  73.     dict_mem_table_add_col(table, heap, "DUMMY_COLUMN", DATA_BINARY, 0, 0);  
  74.   
  75.     table->id = DICT_IBUF_ID_MIN + IBUF_SPACE_ID; //#define DICT_IBUF_ID_MIN 0xFFFFFFFF00000000ULL  
  76.   
  77.     dict_table_add_to_cache(table, heap);//加入数据字典,其中为table struct加了三个字段(见上一篇),并加入dict_sys的哈希表中。  
  78.     mem_heap_free(heap);  
  79.   
  80.     index = dict_mem_index_create(    //创建聚集索引结构体  
  81.         IBUF_TABLE_NAME, "CLUST_IND",  
  82.         IBUF_SPACE_ID, DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF, 1);  
  83.   
  84.     dict_mem_index_add_field(index, "DUMMY_COLUMN", 0);  
  85.   
  86.     index->id = DICT_IBUF_ID_MIN + IBUF_SPACE_ID;  
  87.   
  88.     error = dict_index_add_to_cache(table, index,  
  89.                     FSP_IBUF_TREE_ROOT_PAGE_NO, FALSE);//经过一番周折,将index链入到table->indexes  
  90.     ut_a(error == DB_SUCCESS);  
  91.   
  92.     ibuf->index = dict_table_get_first_index(table); 即table->indexes  
  93. }  

[cpp]  view plain  copy
  1. /** Insert buffer struct */  
  2. struct ibuf_struct{  
  3.     ulint       size;       /*!< current size of the ibuf index 
  4.                     tree, in pages */  
  5.     ulint       max_size;   /*!< recommended maximum size of the 
  6.                     ibuf index tree, in pages */  
  7.     ulint       seg_size;   /*!< allocated pages of the file 
  8.                     segment containing ibuf header and 
  9.                     tree */  
  10.     ibool       empty;      /*!< Protected by the page 
  11.                     latch of the root page of the 
  12.                     insert buffer tree 
  13.                     (FSP_IBUF_TREE_ROOT_PAGE_NO). TRUE 
  14.                     if and only if the insert 
  15.                     buffer tree is empty. */  
  16.     ulint       free_list_len;  /*!< length of the free list */  
  17.     ulint       height;     /*!< tree height */  
  18.     dict_index_t*   index;      /*!< insert buffer index */  
  19.   
  20.     ulint       n_merges;   /*!< number of pages merged */  
  21.     ulint       n_merged_ops[IBUF_OP_COUNT];  
  22.                     /*!< number of operations of each type 
  23.                     merged to index pages */  
  24.     ulint       n_discarded_ops[IBUF_OP_COUNT];  
  25.                     /*!< number of operations of each type 
  26.                     discarded without merging due to the 
  27.                     tablespace being deleted or the 
  28.                     index being dropped */  
  29. }  

这样,我们除了有一些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、准备工作

首先创建表:

[cpp]  view plain  copy
  1. CREATE TABLE `nkeys` ( `c1` int(11) NOT NULL, `c2` int(11) DEFAULT NULL, `c3` int(11) DEFAULT NULL, `c4` int(11) DEFAULT NULL, `c5` int(11) DEFAULT NULL, PRIMARY KEY (`c1`), UNIQUE KEY `c4` (`c4`), KEY `nkey1` (`c3`,`c5`) ) ENGINE=InnoDB;  

先向表中插入50000条数据,以保证索引有两层(不然根本不会使用insert buffer),使用以下方法;之后重启再插入一条数据(保证页不在buffer pool中)。 这样btr_cur_search_to_nth_level将会调用以下函数:ibuf_should_try、buf_page_get_gen、ibuf_insert

[cpp]  view plain  copy
  1. delimiter $$  
  2. drop procedure  if exists `test`.`insert_value_n` $$  
  3. create procedure insert_value_n(in n int) begin set @i=1; while @i<n do insert into nkeys values (@i,10,@i,@i,@i); set @i=@i+1; end while; end $$     //可以使用start transaction将整个插入过程作为一个事务,可加快插入,具体参考《Mysql技术内幕InnoDB存储引擎》7.8节。  
  4. delimiter ;  
  5. call insert_value_n(50000);  


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、其他。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

创建聚集索引 的相关文章

  • RabbitMQ --- 惰性队列、MQ集群

    一 惰性队列 1 1 消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度 就会导致队列中的消息堆积 直到队列存储消息达到上限 之后发送的消息就会成为死信 可能会被丢弃 这就是消息堆积问题 解决消息堆积有三种思路 增加更多消费者
  • Numpy中的转换成数组的array函数(更新中)

    今天给大家讲解一下图像处理和深度学习里面一个常用的函数array array的功能是接收一个多位置数 例如列表list 元 组tuple等 列表 list1 1 2 3 list2 1 2 3 list3 1 2 3 元组 tuple 1
  • 单片机串口时序与TTL电平

    串口是一个广义的概念 这是单讲单片机的串口UART 以及单片机的TTL电平 主要是记录一下自己忘了还能再看一下 1 TTL电平标准 输出 L lt 0 8V H gt 2 4V 输入 L lt 1 2V H gt 2 0V TTL器件输出低
  • VTK笔记-体绘制-vtkVolume

    体渲染 体渲染是一个用于描述3D数据渲染过程的术语 这里的3D数据是指其属性信息遍及3D空间 而不是一个在3D空间中的2D曲面 面渲染是对数据的表面或者一个抽取的轮廓进行渲染 是通过对面上的标量属性进行显示的 面渲染能显示其表面或者一个抽取
  • 常用数据结构与算法:二叉堆(binary heap)

    一 什么是二叉堆 二 二叉堆的实现 三 使用二叉堆的几个例子 一 什么是二叉堆 1 1 二叉堆简介 二叉堆故名思议是一种特殊的堆 二叉堆具有堆的性质 父节点的 键值 总是大于或等于 小于或等于 任何一个子节点的键值 二叉堆又具有二叉树的性质
  • vue 集成file-saver和xlsx 实现前端表格导出

    1 npm安装 npm install xlsx save npm install file saver save 2 创建export js import FileSaver from file saver import as XLSX
  • ntp时间同步软件_MES、SCADA项目中的时间同步—S7-1500和PC通过NTP进行时间同步

    写在面前 文中链接仅在微信公众号有效 大家好 我是小智 智能制造之家号主 前面我们在谈到MES SCADA项目的时候 更多的是从网络 从通讯协议 从数据采集方向去阐述 比如网络丢包 比如modbus TCP 比如S7 COMM 又比如网络冗
  • 踩坑之路(jpa的批量插入) Oracle

    今天做项目的一个需求 需要把其他地方的数据存储到本地数据里面 没想到一次踩了两个坑 因为需要保存的数据很大 而且每天都需要保存一次 第一次用的jpa的saveAll方法 测试了一遍 保存下来总共花了50分钟 老大看时间的太慢了 提醒了我一下
  • day10-Dom操作

    js Dom 概述 1 通过js获取页面上的元素 2 操作元素 2 1 操作元素的样式 2 2 操作元素的类名 2 3 操作元素属性 2 4 操作元素的内容 2 5 获取元素在dom中的一些信息 3 利用js来生成 添加 删除 修改 克隆元
  • 盘点一个Python自动化办公需求——获取文件夹下所以文件夹的名字,并存excel为一列

    01 前言 这个事情还得从前几天在Python最强王者群 东哥 问了一个Python自动化办公处理的问题 需求倒是不难 一起来看看吧 02 实现过程 这里 wangning 又给了一个答案 他自己之前整理的文章 不过需要自己稍微修改下才行
  • 几种硬盘IO性能测试工具

    dd工具 操作系统 ubuntu 12 04 测试工具 dd 版本 8 21 执行dd version来查看 工具说明 dd命令能粗略测试硬盘IO性能 不足 执行dd命令测试硬盘IO性能 对硬盘的损害很大 不建议多次或长时间尝试 测试命令
  • POSTMAN提取接口返回值

    记录一下 创建一个请求在Tests模块写入提取方法 如图 可点击右侧 Set a globals variable 直接生成写入全局变量语句 set token tk 中的 token为全局变量名称 切记后边的tk 变量名 不要加引号 写好

随机推荐