首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【腾讯云CDB】如何快速删除InnoDB中的大表

【腾讯云CDB】如何快速删除InnoDB中的大表

原创
作者头像
腾讯云数据库 TencentDB
修改2019-04-08 20:58:20
3.7K1
修改2019-04-08 20:58:20
举报

1. 背景

在使用MySQL时,如果有大表的存储引擎是InnoDB,并且系统参数innodb_file_per_table设置为1,即每个文件对应一个独立的表空间,当对这些大表进行DROP TABLE时,有时会发现整个数据库系统的性能会有显著下降,包括一些只涉及几行数据的简单SELECT查询和DML语句,而且这些语句和正在删除的大表没有关系。造成这种现象的原因是什么呢?通过什么方式能缓解和避免这个问题呢?

2. 已知的瓶颈

Percona曾经在MySQL官方5.5.23之前的版本中遇到过这个问题,并且提供了一种叫Percona Lazy Drop的补丁。简单来说,他们认为这个问题的瓶颈在CPU。在删除一个有独立表空间的大表时,需要对buffer pool中所有和这个表空间有关的数据页做清理工作,包括从AHI,flush list和LRU list上移除,而在这个清理过程中,会一直持有buffer pool的mutex。如果buffer pool配置特别大,比如500 GB大小,持有这个mutex的事件会较长,导致其他连接被阻塞住,从而导致系统性能的下降。Percona Lazy Drop就是在清理buffer pool这里做了优化,尽量短时间和小粒度的持有mutex。搜索关注“腾讯云数据库”官方微信立得10元腾讯云无门槛代金券,体验移动端一键管理数据库,学习更多数据库技术实战教程。搜索关注“腾讯云数据库”官方微信立得10元腾讯云无门槛代金券,体验移动端一键管理数据库,学习更多数据库技术实战教程。

MySQL官方在5.5.23版本中也实现了一个lazy drop的功能,但和Percona的实现方式不一样:在移除flush list时,会有一个条件判断,如果已经处理了超过一定数量的page,会强制释放当前持有的buffer pool mutex和flush list mutex,并且让出CPU,过一会儿再重新拿回锁继续清理flush list;对于LRU list,则不做处理,因为当这个表被删除后,这些数据页最终会在LRU算法调度下被回收。相关的函数调用图为:

buf_LRU_flush_or_remove_pages(BUF_REMOVE_FLUSH_NO_WRITE) --> buf_LRU_remove_pages --> buf_flush_dirty_pages --> buf_pool_mutex_enter
                                                                                                            |__ buf_flush_or_remove_pages --> buf_flush_list_mutex_enter
                                                                                                            |                             |__ buf_flush_try_yield
                                                                                                            |                             |__ buf_flush_list_mutex_exit
                                                                                                            |__ buf_pool_mutex_exit

Percona在后续版本中移植了MySQL官方5.5.23中的lazy drop,并且移除了自己之前实现的Percona Lazy Drop方案。

3. IO问题

尽管已经有了上述的buffer pool层面的优化,我们在使用MySQL 5.6或者5.7时依然发现删除大表对系统性能还是会产生显著的影响,说明DROP TABLE还有其他的性能瓶颈,尤其是对于这样一种业务场景:并发地删除多个大表。在这种场景下,数据库实例几乎处于不可服务的状态。通过抓取这种状态下的mysqld进程的堆栈,我们发现此时性能瓶颈不在上面介绍的buffer pool mutex上,而是在删除ibd文件的IO上。IO的瓶颈是怎样具体地导致系统性能下降的呢?通过阅读MySQL 5.7的源码可以看到,整个DROP TABLE过程可以简单地概括为:

  • 获取dict_sys->mutex这个数据字典锁
  • 启动一个innodb事务
  • 更新数据字典,包括内存中的数据和mysql库下的数据字典表
  • lazy drop逻辑,清理buffer pool的flush list,会多次持有和释放buffer pool mutex以及flush list mutex
  • 写入MLOG_FILE_DELETE类型的redo日志
  • unlink ibd文件
  • 提交innodb事务
  • 释放dict_sys->mutex

这个过程表明,删除ibd文件时是持有dict_sys->mutex的,如果文件大小很大,比如50GB以上,unlink操作会比较耗时,dict_sys->mutex会较长时间被当前连接持有,此时,并发的其他DROP TABLE会阻塞在dict_sys->mutex上,更糟糕的是,并发的被阻塞的DROP TABLE会持有所有的table cache lock,因为它是在清理table cache中和该表相关的数据时被dict_sys->mutex阻塞的,于是,会导致接下来的几乎所有语句都阻塞在table cache lock上,从show processlist结果上看,就表现为SELECT和DML语句状态是”Opening tables”,而DROP TABLE语句状态为”Checking permissions”。被阻塞的DROP TABLE语句(持有所有table cache lock)的相关函数调用为:

mysql_execute_command --> mysql_rm_table --> tdc_remove_table --> table_cache_manager.lock_all_and_tdc
                                                              |__ Table_cache_manager::free_table --> intern_close_table --> closefrm --> ha_innobase::close --> row_prebuilt_free --> dict_table_close --> mutex_enter(&dict_sys->mutex)
                                                              |__ table_cache_manager.unlock_all_and_tdc

DROP TABLE的源代码调用关系大致为:

row_drop_table_for_mysql --> row_mysql_lock_data_dictionary
                         |__ trx_start_for_ddl
                         |__ clean up data dictionary
                         |__ row_drop_table_from_cache
                         |__ row_drop_single_table_tablespace --> fil_delete_tablespace --> buf_LRU_flush_or_remove_pages
                         |                                                              |__ os_file_delete --> unlink
                         |__ row_mysql_unlock_data_dictionary

4. 解决方案

从上面介绍可以看到,DROP TABLE可能存在两个性能瓶颈,一个是buffer pool的清理,另一个是ibd文件的删除,怎么缓解或者解决这个问题呢?

4.1 不改动源码情况
  • 尽可能在低峰期进行删除表的操作;
  • 对于buffer pool问题,适当增大innodb_buffer_pool_instances参数,尽量减小持有buffer pool mutex对其他语句的影响;
  • 对于IO问题,删除表之前对ibd文件手动创建一个硬链接,让DROP TABLE可以快速执行结束,等到低峰期再真正从磁盘上删除文件;
4.2 改动源码方案
  • 考虑在unlink文件前释放dict_sys->mutex dict_sys->mutex是用来保护内存中的数据字典,以及mysql库下的数据字典表,从源码可以看到,在unlink ibd文件之前,所有的数据字典操作已经结束,其实是可以释放掉dict_sys->mutex的。 这个方案可以快速简单地解决dict_sys->mutex是性能瓶颈的问题,但是,这个方案是不安全的,因为提前释放dict_sys->mutex意味着需要提前提交innodb事务,于是上述的DROP TABLE过程变为:搜索关注“腾讯云数据库”官方微信立得10元腾讯云无门槛代金券,体验移动端一键管理数据库,学习更多数据库技术实战教程。

那么就会存在这样一个情况:如果系统crash发生在释放dict_sys->mutex后和unlink文件前,那么当数据库重新启动后,从InnoDB数据字典的角度看,DROP TABLE事务已经成功提交了,数据字典里已经没有这个表了,但是磁盘上还存在ibd文件;这个问题一方面会导致磁盘空间的浪费,另外一方面会导致CREATE TABLE失败,如果表名和之前删除的表名一样。可能有人会问,不是在提交事务前写入了一条MLOG_FILE_DELETE类型的redo日志吗,那么数据库启动后重做redo日志时难道不会删除磁盘上遗留的ibd文件?遗憾的是,现在crash recovery的逻辑中,对于MLOG_FILE_DELETE类型的日志,只把它当作是一条“告知”含义的日志记录,不会去删除本应被删除的文件(在UNIV_HOTBACKUP代码分支的crash recovery逻辑中会,但这个分支不在正常的server编译路径中)。 如果想要用这个方案,就需要修改MLOG_FILE_DELETE日志的语义,也就是说在crash recovery中,当执行这条日志时,如果磁盘上该文件还存在,则将该文件删除,同时,将DROP TABLE过程调整为:

  • 获取dict_sys->mutex这个数据字典锁
  • 启动一个innodb事务
  • 更新数据字典,包括内存中的数据和mysql库下的数据字典表
  • lazy drop逻辑,清理buffer pool的flush list,会多次持有和释放buffer pool mutex以及flush list mutex
  • 启动一个mini-transaction
  • 写入MLOG_FILE_DELETE类型的redo日志
  • 提交innodb事务
  • 释放dict_sys->mutex
  • 提交mini-transaction
  • unlink ibd文件
  • 获取dict_sys->mutex这个数据字典锁
  • 启动一个innodb事务
  • 更新数据字典,包括内存中的数据和mysql库下的数据字典表
  • lazy drop逻辑,清理buffer pool的flush list,会多次持有和释放buffer pool mutex以及flush list mutex
  • 写入MLOG_FILE_DELETE类型的redo日志
  • 提交innodb事务
  • 释放dict_sys->mutex
  • unlink ibd文件

  • 假设已经没有dict_sys->mutex瓶颈,单纯地unlink一个大文件也会影响系统其他的IO操作,因为需要涉及大量的文件系统日志操作(详见之前推送的文章”Linux删除文件过程解析”),所以另一个可以考虑的方案是将一个大文件的删除分成多次的truncate操作。具体来说,在DROP TABLE中,将之前的unlink调用替换为rename,将ibd文件加上一个后缀,比如.trash,然后将这个重命名后的文件加入到一个队列中,DROP TABLE就继续后面的提交事务释放锁。真正的文件删除可以在innodb master thread中进行,或者重新启动一条专门的线程负责在后台从队列中拿文件并小批量truncate。搜索关注“腾讯云数据库”官方微信立得10元腾讯云无门槛代金券,体验移动端一键管理数据库,学习更多数据库技术实战教程。 这个方案需要注意的一个问题是重命名的文件名不能重复,因为有dict_syc->mutex的保护,用一个简单的计数器就可以实现,但考虑到重启的情况,用时间戳会更好一些;另一个问题是,如果重命名的文件依然在之前的数据库目录下,那么在后台线程真正删除掉文件之前,DROP DATABASE会失败,一种方案是DBA可以手动删除掉trash文件,另一个方案是让用户指定一个其他的临时目录来存放rename后的文件,注意临时文件目录需要和数据目录在同一个挂载点下。

5. 总结

本文介绍了MySQL DROP TABLE可能存在的性能瓶颈,导致瓶颈的具体原因,以及相关的解决方案和思路。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 背景
  • 2. 已知的瓶颈
  • 3. IO问题
  • 4. 解决方案
    • 4.1 不改动源码情况
      • 4.2 改动源码方案
      • 5. 总结
      相关产品与服务
      云数据库 SQL Server
      腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档