MySQL · 特性分析 · drop table的优化

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 背景 系统为了加速对象的访问,通常都会增加一层缓存,以缓解下一层IO的瓶颈,OS的page cache和数据库的buffer pool都基于此。 但对象的删除,如果同步清理对象的缓存的话,不仅大大增加了延时,同时可能因为缓存过大导致IO blooding。所以针对缓存的清理,都会采用lazy d

背景

系统为了加速对象的访问,通常都会增加一层缓存,以缓解下一层IO的瓶颈,OS的page cache和数据库的buffer pool都基于此。

但对象的删除,如果同步清理对象的缓存的话,不仅大大增加了延时,同时可能因为缓存过大导致IO blooding。所以针对缓存的清理,都会采用lazy drop的优化,下面我们就来对比下percona和官方针对drop table的lazy drop 优化。

假设使用innodb_file_per_table为表创建独立的tablespace,在业务处理过程中,有删除表的动作,会发现drop table操作不仅仅持续比较长,而且在删除过程中,实例的QPS也有所降低,主要是因为在清理buffer pool过程中,持有buffer pool的mutex导致,percona server在5.1版本开始引入 lazy drop table来消除drop table过程中带来的影响,但也并没有完全消除,MySQL 官方在5.5.23以后也引入了 lazy drop table 来优化drop 操作,下面我们就来对比一下这两种方式的差异。

值得一提的是:在日常的运维中,drop的操作并非核心需求,我们也都建议DBA在 off-peak 时间去做这样的操作。

同步模式

在讨论lazy模式之前,我们先看看MySQL在5.5.23版本之前的处理方式即同步模式:
当要drop table的时候,会在整个操作过程中持有buffer pool的mutex,然后扫描两次LRU链表,把属于这个table的page失效掉,buffer pool中page的个数越多,持有mutex时间就会越长,对在线业务的影响也就越明显。

简短看下核心处理代码:

fil_delete_tablespace
buf_LRU_invalidate_tablespace(
     ulint     id)     /*!< in: space id */
{
     ulint     i;()
     for (i = 0; i < srv_buf_pool_instances; i++) {
          buf_pool_t*     buf_pool;

          buf_pool = buf_pool_from_array(i);
          buf_LRU_drop_page_hash_for_tablespace(buf_pool, id);
          buf_LRU_invalidate_tablespace_buf_pool_instance(buf_pool, id);
     }
}
  1. buf_LRU_drop_page_hash_for_tablespace会扫描一次LRU list,需要从adaptive hash中删除对要删除的表的page的引用;
  2. buf_LRU_invalidate_tablespace_buf_pool_instance会扫描一次LRU list:
    如果是dirty block,需要从flush list remove掉,然后从page hash中删除,最后从LRU list中删除。

可以看到,这种同步清理掉内存结构的操作,在业务高峰期,对系统的吞吐能力会产生不小的波动。

Percona lazy模式

percona实现了一个lazy drop table模式,使用参数控制:

mysql> show global variables like '%lazy%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_lazy_drop_table | 0     |
+------------------------+-------+

其处理drop table的过程如下:

  1. 持有buffer pool的lru list mutex锁;
  2. 开始扫描LRU list中的page;
    1. 如果这个page属于要删除的table的,就设置一个flag,表示这个page所在的表正在被删除
  3. 释放lru list mutex锁;
  4. 持有一个adaptive hash index的shared latch;
  5. 开始扫描buffer pool中的block;
  6. 如果这个page被AHI索引;
    1. 释放AHI 锁
    2. 持有page的exclusive lock
    3. 删除AHI中索引这个page的entries
    4. 释放page锁
    5. 持有AHI的shared lock进行下一个page的判断

相比较同步模式,Percona的lazy drop table在扫描lru list过程中,只set了一个flag,随后在lru正常的淘汰过程中或者flush dirty block的时候如果碰到这中block,直接就做删除处理了,这也就是lazy的核心。

其核心代码如下:

buf_LRU_mark_space_was_deleted(
     ulint     id)     /*!< in: space id */
{
     ulint     i;

/* 这一部分代码就是持有lru链表mutex,进行第一步,第二步操作。*/
     for (i = 0; i < srv_buf_pool_instances; i++) {
          mutex_enter(&buf_pool->LRU_list_mutex);
          while (bpage != NULL) {
               if (buf_page_get_space(bpage) == id)
                    bpage->space_was_being_deleted = TRUE;
          }
          mutex_exit(&buf_pool->LRU_list_mutex);

/* 这里扫描的是buf_pool中的chunk,也就是启动的时候,根据buffer pool的大小预分配好的blocks,不能更改,
   所以并不需要持有buffer pool mutex,或者lru list mutex。
*/
          btr_search_s_lock_all();
          chunk = buf_pool->chunks;
          for (j = buf_pool->n_chunks; j--; chunk++) {
               buf_block_t*     block     = chunk->blocks;
               for (k = chunk->size; k--; block++) {
                    if (buf_block_get_state(block)
                        != BUF_BLOCK_FILE_PAGE
                        || !block->index
                        || buf_page_get_space(&block->page) != id) {
                         continue;
                    }
/* 这里把AHI的锁释放掉了,但在btr_search_drop_page_hash_index中会持有AHI的lock对AHI结构进行变更。*/
                    btr_search_s_unlock_all();
                    rw_lock_x_lock(&block->lock);
                    btr_search_drop_page_hash_index(block, NULL);
                    rw_lock_x_unlock(&block->lock);

                    btr_search_s_lock_all();
               }
          }
          btr_search_s_unlock_all();
     }
}

MySQL lazy模式

在MySQL 5.5.23以后的版本,也实现了一个lazy drop table的方式,和percona的方式有所区别,下面来看一下具体的过程:

  1. 持有buffer pool mutex
  2. 持有buffer pool中的flush list mutex
  3. 开始扫描flush list;
    1. 如果dirty page属于drop table,那么就直接从flush list中remove掉;
    2. 如果删除的page个数超过了#define BUF_LRU_DROP_SEARCH_SIZE 1024 这个数目的话,释放buffer pool mutexflush list mutex,释放cpu资源;
      • 释放flush list mutex
      • 释放buffer pool mutex
      • 强制通过pthread_yield进行一次OS context switch,释放剩余的cpu时间片;
    3. 重新持有buffer pool mutex
    4. 重新持有flush list mutext
  4. 释放flush list mutex
  5. 释放buffer pool mutex

相比较percona的lazy方式,这里扫描的是dirty block,在LRU list中进行淘汰的时候,就不再判断当前fil_space是否存在的问题了,因为不牵涉到写入。

这里边有两个相关的bug,bug#51325bug#64284,有兴趣可以参考一下。

其核心的代码如下:

buf_LRU_flush_or_remove_pages(id, BUF_REMOVE_FLUSH_NO_WRITE, 0);

buf_pool_mutex_enter(buf_pool);

err = buf_flush_or_remove_pages(buf_pool, id, flush, trx);
......
buf_pool_mutex_exit(buf_pool);

/* BUF_REMOVE_FLUSH_NO_WRITE:意思表示,只对dirty block进行remove操作,不做写入。

对比

从上面的percona和oracle的MySQL版本比较来看,percona是持有了LRU list mutexAHI lock,而MySQL官方版本是持有了buffer pool mutexflush list mutex,从锁的保护范围来看,buffer pool mutex直观上瓶颈会比较明显,但具体还要跟表的大小、dirty block的比例来看,如果dirty block比较少的话,官方版本并不扫描LRU list,所以可能持有的时间并不会太久。

Percona的开发人员还针对这两个不同版本进行了Benchmarks, 大家可以看下他们测试出来的结果:

这个图是 MySQL 官方版本测试在系统压力下,进行频繁drop table的系统抖动:

MySQL官方

这个图是 Percona 版本测试在系统压力下,进行频繁drop table的系统抖动:

Percona版本

但对于这样的测试,小编想说,哪个DBA/开发人员这么变态,要这么频繁的drop table -_-||

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
5天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:百万级数据统计优化实践
【10月更文挑战第21天】 在处理大规模数据集时,传统的单体数据库解决方案往往力不从心。MySQL和Redis的组合提供了一种高效的解决方案,通过将数据库操作与高速缓存相结合,可以显著提升数据处理的性能。本文将分享一次实际的优化案例,探讨如何利用MySQL和Redis共同实现百万级数据统计的优化。
31 9
|
5天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
24 5
|
8天前
|
存储 关系型数据库 MySQL
基于案例分析 MySQL 权限认证中的具体优先原则
【10月更文挑战第26天】本文通过具体案例分析了MySQL权限认证中的优先原则,包括全局权限、数据库级别权限和表级别权限的设置与优先级。全局权限优先于数据库级别权限,后者又优先于表级别权限。在权限冲突时,更严格的权限将被优先执行,确保数据库的安全性与资源合理分配。
|
10天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
16 1
|
10天前
|
缓存 关系型数据库 MySQL
一文彻底弄懂MySQL优化之深度分页
【10月更文挑战第24天】本文深入探讨了 MySQL 深度分页的原理、常见问题及优化策略。首先解释了深度分页的概念及其带来的性能和资源问题。接着介绍了基于偏移量(OFFSET)和限制(LIMIT)以及基于游标的分页方法,并分析了它们的优缺点。最后,提出了多种优化策略,包括合理创建索引、优化查询语句和使用数据缓存,帮助提升分页查询的性能和系统稳定性。
|
13天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:百万数据量的优化实录
【10月更文挑战第6天】 在现代互联网应用中,随着用户量的增加和业务逻辑的复杂化,数据量级迅速增长,这对后端数据库系统提出了严峻的挑战。尤其是当数据量达到百万级别时,传统的数据库解决方案往往会遇到性能瓶颈。本文将分享一次使用MySQL与Redis协同优化大规模数据统计的实战经验。
56 3
|
13天前
|
NoSQL 关系型数据库 BI
记录一次MySQL+Redis实现优化百万数据统计的方式
【10月更文挑战第13天】 在处理百万级数据的统计时,传统的单体数据库往往力不从心,这时结合使用MySQL和Redis可以显著提升性能。以下是一次实际优化案例的详细记录。
67 1
|
9天前
|
存储 监控 关系型数据库
MySQL并发控制与管理:优化数据库性能的关键
【10月更文挑战第17天】MySQL并发控制与管理:优化数据库性能的关键
31 0
|
11天前
|
监控 关系型数据库 MySQL
mysql8索引优化
综上所述,深入理解和有效实施这些索引优化策略,是解锁MySQL 8.0数据库高性能查询的关键。
24 0
|
18天前
|
关系型数据库 MySQL 数据库
深入浅出MySQL索引优化:提升数据库性能的关键
在这个数据驱动的时代,数据库性能的优劣直接关系到应用的响应速度和用户体验。MySQL作为广泛使用的数据库之一,其索引优化是提升查询性能的关键。本文将带你一探MySQL索引的内部机制,分析索引的类型及其适用场景,并通过实际案例演示如何诊断和优化索引,以实现数据库性能的飞跃。

相关产品

  • 云数据库 RDS MySQL 版