innodb table/partition truncate 的坑

innodb做table truncate时候,要把属于这个table的表空间文件的所有的页刷盘并从buffer pool中去掉。这个过程是通过遍历每一个buffer pool instance的LRU

链表,然后检查链表中每个页是否属于目标表空间文件,属于的话就evict它,是脏页的话还要先flush它。清理完buffer pool中的页后,再truncate表空间文件。

这样,当buffer pool很大的时候,这个truncate操作会耗时很久,因为要遍历的页的列表太长了。曾经我们在TDSQL 分布式事务处理中,为了定期迅速地清理commit log,把commit log表巧妙地设计成每个小时写一个分区,这样,删掉几个小时之前的分区就简单地truncate那个partition即可,结果实际测试发现buffer pool较大时候,这个truncate非常慢,最终不得不使用了“笨”办法 --- 逐条删除commit log表中的过期行。

而最近,我们又在另外一个地方遇到了类似的问题:mariadb的gtid_slave_pos变量的值,目前是装在mysql.gtid_slave_pos表中的,这是一个innodb表。当用户执行 set global gtid_slave_pos='xxx'时候,mariadb要truncate mysql.gtid_slave_pos表,然后,再插入新值。所以,当用户使用了250GB的buffer pool后,这个过程就很慢了。最终我还是要改成用‘笨’办法,逐行删除mysql.gtid_slave_pos的数据。

至此,mariadb10.0/10.1的内核中,没有使用ha_truncate() 来truncate内部使用的数据表的做法了。 mysql-5.7 没有这么truncate 内部数据表,不过其innodb的truncate的这个问题也是有的。

Innodb的truncate table/partition应该怎么实现才能更快呢? 我想到了这个办法:使用buffer pool的hash table找页。具体操作如下,后面我会这样来改进tdsql的mariadb/mysql 内核:

buffer pool instance的LRU list里面的页,都是可以通过buffer pool instance的hash table 找到的,hash key 就是(table-space-id, page-no), page-no从0开始,直到文件末尾(lseek得到)。依次用目标表空间id与它的每个page-no 生成hash key,然后查找每个buffer pool instance,一定可以找到这个页,然后从所在的buffer pool instance 中删除

foreach page in target-tablespace tts:

hash-key= (tts.id, page.no)

foreach bufferpool-instance bpi:

pg-ptr= find-page-from-buffer-pool-instance-hash-table(bpi, hash-key);

if (pg-ptr)

evict-from-buffer-pool(bpi, pg-ptr);

break;

truncate 表空间必须同步清理buffer pool中的页,是因为这个表还可能重新增长,旧数据不能被使用到或者误当做新数据,因此无法通过异步化缩短时间。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180428G1EXRW00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券