MySQL 5.7 : 索引创建优化(Bulk Load)

先来看一组测试数据

使用sysbench,生成一张大约10,000,000行记录的表,数据全部在buffer pool中

创建索引(k,pad)

5.7.5

alter table sbtest1 add key(k,pad);

Query OK, 0 rows affected (44.14 sec)

Records: 0  Duplicates: 0  Warnings: 0

5.7.4

root@sbb 04:25:11>alter table sbtest1 add key(k,pad);

Query OK, 0 rows affected (1 min 2.03 sec)

Records: 0  Duplicates: 0  Warnings: 0

几轮测试的结果都差不多,5.7.5的索引创建速度总是优于5.7.4(同时也优于5.6).

OK,老规矩,我们来看主要对索引创建做了什么样的优化,在5.7.5的changelog entry如下:

InnoDB: Instead of inserting one index record at a time, InnoDB now performs a bulk load when creating or rebuilding indexes. This method of index creation is also known as a “sorted index build”. This enhancement, which improves the efficiency of index creation, also applies to full-text indexes. A new global configuration option, innodb_fill_factor, defines the percentage of space on each page that is filled with data during a sorted index build, with the remaining space reserved for future index growth. For more information, see Bulk Load for CREATE INDEX.

在之前的版本中,创建索引时总是一条记录一条记录的插入索引,当记录量比较大时,这种插入方式非常低效,因此引入了BULK LOAD的方式。采用自底向上的构建方式。大体思路为,总是按照从左向右,从底往上的方式来构建btree记录。

因此修改的点应该在获取了已经归并排序完成的索引记录之后,准备向新构建的BTREE中插入记录。老的实现方式有明显的缺点:1.每次插入BTREE都需要重定位cursor;2.自顶向下的构建索引可能导致大量的索引分裂。

0.background

新增源文件:btr/btr0bulk.cc

定义了两个类来辅助索引

PageBulk:用于处理页内操作

BtrBulk:用于处理btree操作,针对每层BTREE都有一个PageBulk对象

1.总体入口

我们以上述测试用的建索引语句为例

入口函数:row_merge_build_indexes

在完成对二级索引记录的排序文件后,进入插入阶段:

Step 1. 初始化BtrBulk

BtrBulk btr_bulk(sort_idx, trx->id);

btr_bulk.init();  初始化m_page_bulks,该vector存储的是PageBulk对象

Step 2.随后调用row_merge_insert_index_tuples进入元组插入阶段。

遍历元组,对于每条记录,进行转换后,调用 btr_bulk->insert(dtuple)->insert(tuple, 0)插入

Step 3.完成后调用btr_bulk.finish(error)完成插入操作。

显然我们的重点集中在btr_bulk->insert的逻辑上。

2.自底向上的索引记录插入

入口函数: BtrBulk::insert

画了个流程图来便于理解这个过程。

Step 1. 当前btree level没有对应的PageBulk,则创建,初始化,并加入到m_page_bulks中。

PageBulk::init() 会开启一个mtr (不记录redo log,MTR_LOG_NO_REDO),分配新的page(如果需要的话)

另外还需要计算page上保留的空闲空间数,用于索引完成后的DML操作,由新参数innodb_fill_factor控制。

        m_reserved_space =

                UNIV_PAGE_SIZE * (100 – innobase_fill_factor) / 100;

Step 2. 当前插入的tuple是非叶子节点的最左节点的最小记录,设置tuple标记REC_INFO_MIN_REC_FLAG

Step 3.判断是否需要外部存储,如果需要,转换记录格式(dtuple_convert_big_rec)

page_bulk->needExt(tuple, rec_size)。

Step 4. 检查当前page的空间是否足够插入记录(PageBulk::isSpaceAvailable)

如果PageBulk::isSpaceAvailable返回false,表示page空间不足,需要

#创建一个兄弟节点,及其对应的PageBulk,并进行初始化,分配新page

#提交当前PageBulk:err = pageCommit(page_bulk, sibling_page_bulk, true)

…将当前page指向下一个page;

…压缩表需要特殊处理,先进行压缩(PageBulk::compress()),如果压缩失败,则进行page分裂(BtrBulk::pageSplit), 在pageSplit里会先分配一个新的page用于容纳分裂后的数据,简单的说..

page_bulk1 —-> 空间不足

page_bulk1       (page_bulk2)

page_bulk1 —->page_bulk2

压缩page_bulk1—>failed—->split(page_bulk1, page_bulk3) —>(page_bulk1—>page_bulk3—>page_bulk2)

也就是说,这里可能产生BtrBulk::pageCommit的递归调用。

…构建父亲节点记录,并插入到父节点。

dtuple_t*       node_ptr = page_bulk->getNodePtr();

dberr_t         err = insert(node_ptr, page_bulk->getLevel()+1);

这里会递归调用BtrBulk::insert函数来完成自下而上的BTREE节点构建

#如果是叶子节点,还需要唤醒page cleaner线程,做必要的log_free_check()

Step 5.在完成上述必要的检查后,将tuple转会为rec,并插入到page中(PageBulk::insert)

page_bulk->insert(rec, offsets)

Step 6.处理外部存储记录(PageBulk::storeExt)

参考:

worklog

http://dev.mysql.com/worklog/task/?id=7277

对应补丁:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8357

官方文档:

http://dev.mysql.com/doc/refman/5.7/en/create-index-bulk-load.html

时间: 2025-01-20 09:35:26

MySQL 5.7 : 索引创建优化(Bulk Load)的相关文章

MySQL内核月报 2014.12-MySQL· 性能优化·Bulk Load for CREATE INDEX

背景 MySQL5.6以后的版本提供了多种优化手段用于create index,比如online方式,Bulk Load方式. Online提供了非阻塞写入的方式创建索引,为运维提供了很大的便利. Bulk Load提升了索引创建的效率,减少了阻塞的时间. 这篇介绍下MySQL 5.7.5 Bulk Load的细节,并从查找,排序,redo,undo,page split等维度比较一下传统方式和Bulk Load方式. 传统方式 MySQL 5.7.5版本之前,create index使用的是和

mysql中数据库索引与优化

一.索引的概念索引就是加快检索表中数据的方法.数据库的索引类似于书籍的索引.在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息.在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库. 二.索引的特点 1.索引可以加快数据库的检索速度 2.索引降低了数据库插入.修改.删除等维护任务的速度 3.索引创建在表上,不能创建在视图上 4.索引既可以直接创建,也可以间接创建 5.可以在优化隐藏中,使用索引 6.使用查询处理器执行SQL语句,在一个表上,一次只能使用一个索引

mysql 优化实例之索引创建

mysql 优化实例之索引创建 优化前: pt-query-degist分析结果: # Query 23: 0.00 QPS, 0.00x concurrency, ID 0x78761E301CC7EE47 at byte 394687 # This item is included in the report because it matches --limit. # Scores: V/M = 3.27 # Time range: 2016-09-29T11:46:22 to 2016-1

Mysql技巧之索引分析和优化

mysql|技巧|索引|优化     索引用来快速地寻找那有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表里面的记录数量越多,这个操作的代价就越高.如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置.如果表有1000个记录,通过索引查找记录至少要比顺序扫描记录快100倍. 假设我们创建了一个名为people的表: CREATE TABLE pe

理解MySQL——索引与优化

写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多).如果对之建立B-Tree索引,则只需要进行log100(

MySQL Order By索引优化方法_Mysql

尽管 ORDER BY 不是和索引的顺序准确匹配,索引还是可以被用到,只要不用的索引部分和所有的额外的 ORDER BY 字段在 WHERE 子句中都被包括了. 使用索引的MySQL Order By 下列的几个查询都会使用索引来解决 ORDER BY 或 GROUP BY 部分: 复制代码 代码如下: SELECT * FROM t1 ORDER BY key_part1,key_part2,... ; SELECT * FROM t1 WHERE key_part1=constant ORD

MySQL索引创建 删除 增加 修改命令

简单的唯一索引: 可以创建一个唯一索引的表.唯一的索引意味着两个行不能拥有相同的索引值.这里是语法来创建索引的表  代码如下 复制代码 CREATE UNIQUE INDEX index_name ON table_name ( column1, column2,...); 可以使用一个或多个列上创建索引.例如,我们可以创建一个索引的使用在表tutorials_tbl的tutorial_author列上  代码如下 复制代码 CREATE UNIQUE INDEX AUTHOR_INDEX ON

Mysql数据库之索引优化_Mysql

MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓"好马配好鞍",如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如"精通MySQL"."SQL语句优化"."了解数据库原理"等要求.我们知道一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,

高性能的MySQL(5)创建高性能的索引一哈希索引

哈希索引(hash index)基于哈希表实现,只有精确匹配索引的所有列的查询才有效,对于每一行数据,存储引擎都会对所有索引列计算一个哈希码,不同键值的行计算出来的哈希码也不一样,哈希码保存在哈希索引中,同时哈希表中保存指向每个数据的指针. 1.Memory引擎支持哈希索引,也支持B-Tree索引,而且支持非唯一的哈希索引,如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希条目,这个是和特别的. 举例说明: CREATE TABLE `testhash` ( `fname`