一种方法,大大提高了全表扫描的InnoDB速度

一种方法,大大提高了全表扫描的InnoDB速度
更快速的全表扫描在InnoDB

一般来说,大多数应用程序使用索引找到几行数据(在100行的主键查询或查询),但有时我们需要全表查询。典型的全表扫描是逻辑备份(mysqldump)和在线模式的变化(注:大表模式,网上操作也是脸谱网开源项目)(选择…为导出的文件)。

在脸谱网,我们使用mysqldump备份数据库。正如您所知道的,MySQL提供了两种备份方式,提供了物理备份和逻辑备份的命令工具。与物理备份相比,逻辑备份具有一些优点,如:

逻辑备份备份数据要小得多。对3x-10x大小差别并不少见。
解析备份数据库比较容易。物理备份,当发生严重问题,如校验失败。如果我们不能恢复数据库,它是知道InnoDB内部数据结构很难,或去修理损坏的地方。我们更多的是逻辑备份比物理备份。
逻辑备份的主要缺点是数据库的完全备份和完全还原要比物理备份慢得多。

慢速完全逻辑备份常常导致问题。如果数据库中有许多零碎的表,这可能需要很长时间。在脸谱网,我们面对的是就绩效问题,这导致我们无法完成基于硬盘和缓存在合理的时间内,一些服务器的一些逻辑备份。我们知道,我们是没有效率的全表扫描因为InnoDB实际上是不按顺序读,并在大多数情况下它是随机读取,这是个老问题了,已经知道很多年了。我们的数据库的存储容量不断增加。慢全扫描问题严重影响了我们。因此,我们决定提升为阅读InnoDB的速度。最后,我们的数据库工程师团队在InnoDB实现逻辑预读功能,使用逻辑预读在超负荷生产,全表扫描速度为15 ~快20倍甚至更快的速度。

对大的、零散的数据表的全表扫描

当所有的表扫描完成后,会将扫描的主要关键秩序的页和行。这应该用于所有InnoDB表,包括碎片表。如果表没有主键的页面片段(存储主密钥和行的页表),全表扫描是很快的,因为阅读顺序是接近物理存储顺序。这就像操作系统命令(DD /猫/等)阅读下面的文件。
复制代码如下:DD如果= / / / MySQL数据在large_table.ibd = / / / dev空BS = 16k iflag =直接

你可能会发现,即使在一个商业硬盘服务器,你可以达到的速度大于100 Mb / s乘以号驶入。1GB以上 / S并不少见。

不幸的是,在许多情况下,有重点的页表的碎片。例如,如果你需要管理user_id和object_id映射,主键将(user_id,object_id),插入排序是不是与user_id一致,和新的插入/更新往往导致分裂的网页。新的分割页将被分配离开当前页面,这意味着页面将支离破碎。

如果主键页支离破碎,全表扫描会变得极为缓慢。图1说明了这个问题。在InnoDB读取页# 3,它需要阅读网页# 5230,然后,阅读网页# 4.the页# 5230位置远离# 3页4页#,所以磁盘读操作的顺序开始变得几乎是随机的,不连续的,它是已知的随机读取硬盘比连续阅读要慢的多。提高随机读性能的一种有效的方法是使用SSD,SSD每GB价格比硬盘贵得多,所以它通常是不可能的使用SSD。
图1。全表扫描实际上不连续读取。

线性预取真的很有意义吗

InnoDB支持预读功能称为线性预读。它具有线性预取。如果N页可以按顺序访问(N可以通过innodb_read_ahead_threshold配置参数默认值是56),我们可以一次读一个程度(64个页面,如果不压缩页每1MB)。但是,在实践中,这并不意味着什么。一个程度(64页)是很小的。对于一个大的、分散的数据库表,下一页是不一定在同一程度上,上面的图1是一个很好的例子。阅读3页#后,InnoDB需要读5230页#。页3页# # 5230不在同一程度,所以线性预读技术不是很有用的在这里。这是大表的一个很常见的情况下,所以这也解释了为什么线性阅读不能有效提高全表扫描的性能。
物理阅读

如上所述,为慢扫描全表速度的主要原因是InnoDB主要是随机读。为了加快全表扫描,就要读InnoDB为顺序。第一种方式我认为是创建一个UDF(用户自定义函数)来读IBD文件(的InnoDB数据文件)。当UDF执行,对IBD文件页面应存放在InnoDB的缓存池,所以不需要读取随机在全表扫描。下面是一个例子的使用:

MySQL >选择buf_warmup(db1
MySQL >选择large_application_table内存中的SELECT * FROM; / * * /
buf_warmup()是一个用户定义的函数,读取数据库的表large_table整个IBD文件的数据库DB1。此功能需要时间从硬盘读取的ibd文件,但由于是按顺序读,这比随机读取的速度要快得多。在我的测试中,它比正常线性预读快约5倍。

这证明了IBD文件的顺序读取可以有效地提高吞吐量,但也存在一些缺点。

这种方法不能工作,如果表的大小超过了InnoDB缓冲池的大小。
在整个表扫描过程中,读取整个IBD文件不仅意味着读取主键页,还读取两级索引页和其他一些不必要的页,并将其保存在缓冲池中,尽管实际上需要主键页。如果您有大量的两级索引,则此方法不能有效地工作。
应用程序需要以调用UDF做出一些改变
这似乎是一个很好的解决方案,但我们的数据库设计团队想出了一个更好的解决方案,称为逻辑读吧,所以我们不选择UDF的方法。

逻辑阅读

逻辑预读取(LRA)工作流如下:

读取主键的页的某些分支。
计算页的个数
按照页码(大部分的顺序磁盘读取)的顺序,依次读取一些叶子页面(通过配置控件的数量)
按照主键的顺序读取行
整个过程如图2所示。
图2:逻辑预读
逻辑阅读解决物理问题进行prereading.lra InnoDB只读主键页(不需要阅读的两级索引页),和预读页面数量控制。此外,你不需要做任何改变的SQL语法。

为了使你的工作,我们需要添加两个会话变量:一个是innodb_lra_size ,是用来控制叶片的预读页面大小(页)。另一个是innodb_lra_sleep利用512MB到4096mb大小和50毫秒的休眠时间测试。到目前为止,我们还没有遇到任何严重的问题(如坍塌/阻塞/不一致)。这些会话变量的设置只有当全表是必要的。在我们的应用程序中,就和其他一些辅助脚本启用逻辑阅读。

提交多个异步I/O请求我一次

我们发现性能问题的另一个原因是,InnoDB只读取一页的时候,我 / O,即使预读技术打开。它真的是太小的顺序读一次读只有16KB,而且效率比低得多的阅读单元。

在5.6版本中,可以使用Linux的地方我 / O默认。如果连续多16kb读请求提交一次,Linux会合并这些请求里面,和读操作可以更有效地执行。不幸的是,我们只会向我/在时间的一个页面的请求。我提交的bug报告# 68659写的。错误报告,在1±0的环境现代硬盘RAID,如果我一次性提交连续64页的读请求,我可以得到比1000mb / S的硬盘读取速度;如果只提交一页的读请求,我们只能160mb /硬盘读取速度。

为了使LRA工作在我们的应用环境更好,我们修改这个问题。在我们的MySQL InnoDB会提交多个页面我 / O请求之前调用io_submit()。

标杆管理

我们使用的所有测试都是生产环境中的数据库表(分页表)。

1。纯硬盘环境全表扫描(基本基准,没有其他工作量)
2。繁重工作量下的在线模式转换
*转储时间,不计算数据加载时间

源代码

所有的增强修改我们可以得到在GitHub上。

逻辑预读实现:diff
-提交多个I/O请求一次:差异
-使mydqldump逻辑预读:diff

结论

全表扫描,InnoDB是低效的,因此我们必须做出一些改变。我做了两方面的改进,一是逻辑读取的实现;一是实现多个异步读我/ O请求。我们的数据库表在生产环境中,我们得到的8-15倍的性能提升,为减少备份时间是非常有用的,模式修改时间等。我想这些功能能够在InnoDB得到Oracle的官方支持,至少主要MySQL分支。

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部