MySQL 5.7: Innodb read view在只读场景的优化

本文涉及的产品
RDS Agent(兼容OpenClaw),2核4GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介:

额,这个标题有点大,实际上只是我在测试5.7性能过程中遇到的一个问题的解惑.不包含5.7的全部read view优化

———–

最近在测试MySQL5.7的只读性能时,和5.6版本对比,发现一个有趣的现象,即在我们的内部版本5.6里,trx_sys->mutex排名第一,而5.7版本则几乎完全看不到该mutex,测试的负载也比较简单,sysbench,使用auto-commit的pk查询

先来看看performance schema的输出:

MySQL5.6.16( heavily patched)

root@performance_schema 11:45:34>SELECT COUNT_STAR, SUM_TIMER_WAIT, AVG_TIMER_WAIT, EVENT_NAME FROM events_waits_summary_global_by_event_name where COUNT_STAR > 0 and EVENT_NAME like ‘wait/synch/%’ order by SUM_TIMER_WAIT desc limit 20;

+————+——————+—————-+—————————————————+

| COUNT_STAR | SUM_TIMER_WAIT   | AVG_TIMER_WAIT | EVENT_NAME                                        |

+————+——————+—————-+—————————————————+

|   67106658 | 2191100230566684 |       32650732 | wait/synch/mutex/innodb/trx_sys_mutex             |

|   33431350 |  116859287127016 |        3495412 | wait/synch/rwlock/sql/LOCK_grant                  |

|   67160439 |   89180859682708 |        1327620 | wait/synch/rwlock/sql/MDL_lock::rwlock            |

|   63988674 |   67704502627896 |        1057736 | wait/synch/mutex/sql/MDL_map::mutex               |

|   67087915 |   47565299538868 |         708936 | wait/synch/mutex/sql/LOCK_table_cache             |

 253945845 |   24152737750000 |          95048 | wait/synch/mutex/sql/THD::LOCK_thd_data           |

|   67076196 |   21521813780432 |         320460 | wait/synch/mutex/mysys/THR_LOCK::mutex            |

 101588861 |   19607984308264 |         192712 | wait/synch/rwlock/innodb/hash_table_locks        |

 181625203 |   14086715074644 |          77172 | wait/synch/mutex/innodb/trx_mutex                 |

|   90361030 |    9755600019708 |         107692 | wait/synch/mutex/innodb/trx_undo_mutex            |

|   33554506 |    5639702314236 |         167860 | wait/synch/rwlock/innodb/index_tree_rw_lock       |

|     397299 |      97044675080 |         244160 | wait/synch/mutex/innodb/os_mutex                  |

|         43 |      37606650696 |      874573272 | wait/synch/mutex/sql/LOG::LOCK_log                |

|      20152 |       7952989672 |         394580 | wait/synch/mutex/sql/LOCK_open                    |

|      19316 |       4096687392 |         211896 | wait/synch/mutex/innodb/innobase_share_mutex      |

|      28983 |       3267197392 |         112488 | wait/synch/mutex/innodb/dict_sys_mutex            |

|      10848 |       1396710304 |         128620 | wait/synch/mutex/innodb/buf_pool_mutex            |

|       8791 |        820647920 |          93304 | wait/synch/mutex/sql/LOCK_global_system_variables |

|       4579 |        765776448 |         166988 | wait/synch/mutex/sql/LOCK_plugin                  |

|       9659 |        522640176 |          54064 | wait/synch/mutex/innodb/file_format_max_mutex    |

+————+——————+—————-+—————————————————+

MySQL5.7.5

root@performance_schema 03:01:45>SELECT COUNT_STAR, SUM_TIMER_WAIT, AVG_TIMER_WAIT, EVENT_NAME FROM events_waits_summary_global_by_event_name where COUNT_STAR > 0 and EVENT_NAME like ‘wait/synch/%’ order by SUM_TIMER_WAIT desc limit 20;

+————+—————-+—————-+—————————————————+

| COUNT_STAR | SUM_TIMER_WAIT | AVG_TIMER_WAIT | EVENT_NAME                                        |

+————+—————-+—————-+—————————————————+

|    6205753 |  5513113478224 |         888132 | wait/synch/rwlock/sql/LOCK_grant                  |

|   12428382 |  4463207301384 |         358828 | wait/synch/mutex/sql/LOCK_table_cache            |

|   34298703 |  4162953914096 |         121208 | wait/synch/mutex/sql/THD::LOCK_query_plan         |

|   18881544 |  4085456883944 |         216256 | wait/synch/sxlock/innodb/hash_table_locks         |

|   20546361 |  2594608664108 |         126004 | wait/synch/mutex/sql/THD::LOCK_thd_data           |

|    6232362 |  1653423256736 |         265088 | wait/synch/sxlock/innodb/index_tree_rw_lock       |

|   13699272 |  1590037239708 |         115976 | wait/synch/mutex/sql/THD::LOCK_thd_query          |

|      29688 |     2876063536 |          96792 | wait/synch/mutex/innodb/fil_system_mutex          |

|      16923 |     2151605936 |         126876 | wait/synch/mutex/innodb/buf_pool_mutex            |

|       5214 |      559759472 |         107256 | wait/synch/mutex/innodb/log_sys_mutex             |

|       1664 |      355917264 |         213640 | wait/synch/mutex/innodb/innobase_share_mutex     |

|       2614 |      291241024 |         111180 | wait/synch/mutex/innodb/dict_sys_mutex            |

|       1683 |      251709776 |         149548 | wait/synch/mutex/sql/LOCK_open                    |

|        945 |      148695184 |         156960 | wait/synch/mutex/innodb/file_format_max_mutex     |

|       1683 |      132639920 |          78480 | wait/synch/mutex/sql/LOCK_global_system_variables |

|        896 |       83248096 |          92868 | wait/synch/mutex/innodb/flush_list_mutex          |

|        234 |       69119952 |         295172 | wait/synch/mutex/sql/LOCK_connection_count        |

|        598 |       58556544 |          97664 | wait/synch/mutex/sql/LOCK_plugin                  |

|        117 |       46728736 |         399376 | wait/synch/mutex/sql/LOCK_transaction_cache       |

|        286 |       44845216 |         156524 | wait/synch/mutex/sql/LOCK_thd_list                |

+————+—————-+—————-+—————————————————+

20 rows in set (0.54 sec)

很奇怪,在5.7里几乎完全看不到trx_sys mutex,而在我们优化版本里,却非常的明显,直接排在第一位了。

简单看了下代码,在5.6里read_view_remove  需要持有try sys mutex锁。而在5.7里,对应MVCC::view_close 对于只读事务无需持有trx_sys->mutex

另外一个原因是,在分配read view时,5.7是有针对性的对纯读场景下的视图做了优化,可以直接重用。

也就是说,对于只读场景,无论是read view的分配还是释放,都无需获取trx_sys->mutex.

以下简单的介绍下相关逻辑。

0.新的结构体MVCC,用来管理所有的read view

MVCC类的对象挂在trx_sys->mvcc上,在初始化事务子系统时创建

trx_sys->mvcc = UT_NEW_NOKEY(MVCC(1024));

(5.7里使用统一的接口来分配<UT_NEW, UT_NEW_NOKEY> 和释放内存<UT_DELETE> ,主要是为了能监控内存的使用状况

1024表示默认默认初始缓存的readview个数

)

MVCC:

在5.7里采用缓存rearview的方式避免过多的分配/释放readview内存,因此创建了两个链表:

m_free : 表示当前空闲的read view

m_views:表示当前活跃的read view

初始化时所有的read view 都加入到空闲链表上。

1.赋予readview

row_search_mvcc->trx_assign_read_view->MVCC::view_open

两种情况,一种是重用已经为当前事务分配了readview (事务结束后依然属于该事务对象,而不是直接回收),另外一种情况是从空闲链表上新分配read view.

对于前者,如果当前会话只读事务 并且没有活跃读写事务,那么该read view可以被重用。如果当前存在读写事务,则需要将该read view从m_views移除,走正常分配流程.

在分配readview对象时,如果m_free上没有空闲的,那么就重新malloc一个新的rearview对象.

对于重用的场景,理论上应该可以做更近一步的优化.这里重用的逻辑还是太简单粗暴了,必须在只读场景下才能直接重用readview.

2.关闭readview

trx_commit_in_memory->MVCC::view_close

这里也分两种情况:

第一种情况,在auto-commit-read-only场景下,无需持有trx_sys mutex,直接将read view设置为关闭

ptr->m_closed = true;

而对于读写事务,则需要从MVCC::m_views链表中移除并加入到MVCC::m_free链表上。这需要持有trx_sys->mutex

另外一个关闭readview的trace:

in ha_innobase::external_lock->MVCC::view_close, 也就是read-commit级别,如果显式的开启事务,不管是不是只读的,都需要关闭事务,并在下一条SQL再次打开read view.这种情况下的read view是不可重用的。

3.purge线程

purge线程总是需要根据最老的视图clone一个readview,以确定哪些数据可以被purge掉。

trx_sys->mvcc->clone_oldest_view(&purge_sys->view)

这里会逆序遍历m_views,找到一个状态不是被closed的read view,然后继续。

4.问题

在关闭read view时,总是要对read view指针 (p & ~1),也就是最低位置0,如果需要重用read view时,就将read view 低位置1 (reinterpret_cast<ReadView*>(p | 0x1))

而在重用read view时,又将read view的低位恢复为0,这种修改指针的方式看起来是安全的,因为内存总是在偶数位上开始分配

不过没想通这么干的含义,除了标示这个read view被close了并且可能被事务重用外,没看到别的用途.

后面再仔细看看.

5.TODO

根据上述,我们可以针对性的改造5.6的MVCC子系统。

当前我们的内部分支已经合并了percona对事务系统的优化, readview对象被缓存到prebuilt_view中。

更具体的,参考官方worklog:

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


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
存储 网络协议 关系型数据库
MySQL8.4创建keyring给InnoDB表进行静态数据加密
MySQL8.4创建keyring给InnoDB表进行静态数据加密
518 1
|
9月前
|
存储 关系型数据库 MySQL
介绍MySQL的InnoDB引擎特性
总结而言 , Inno DB 引搞 是 MySQL 中 高 性 能 , 高 可靠 的 存 储选项 , 宽泛 应用于要求强 复杂交易处理场景 。
382 15
|
12月前
|
关系型数据库 MySQL 分布式数据库
Super MySQL|揭秘PolarDB全异步执行架构,高并发场景性能利器
阿里云瑶池旗下的云原生数据库PolarDB MySQL版设计了基于协程的全异步执行架构,实现鉴权、事务提交、锁等待等核心逻辑的异步化执行,这是业界首个真正意义上实现全异步执行架构的MySQL数据库产品,显著提升了PolarDB MySQL的高并发处理能力,其中通用写入性能提升超过70%,长尾延迟降低60%以上。
|
存储 缓存 关系型数据库
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
MySQL的存储引擎是其核心组件之一,负责数据的存储、索引和检索。不同的存储引擎具有不同的功能和特性,可以根据业务需求 选择合适的引擎。本文详细介绍了MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案。
2423 57
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
|
SQL 缓存 关系型数据库
使用温InnoDB缓冲池启动MySQL测试
使用温InnoDB缓冲池启动MySQL测试
257 0
|
存储 关系型数据库 MySQL
MySQL存储引擎详述:InnoDB为何胜出?
MySQL 是最流行的开源关系型数据库之一,其存储引擎设计是其高效灵活的关键。InnoDB 作为默认存储引擎,支持事务、行级锁和外键约束,适用于高并发读写和数据完整性要求高的场景;而 MyISAM 不支持事务,适合读密集且对事务要求不高的应用。根据不同需求选择合适的存储引擎至关重要,官方推荐大多数场景使用 InnoDB。
826 7
|
存储 关系型数据库 MySQL
Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引
通过本文的介绍,希望您能深入理解InnoDB聚集索引与MyISAM非聚集索引的概念、结构和应用场景,从而在实际工作中灵活运用这些知识,优化数据库性能。
745 7
|
存储 关系型数据库 MySQL
MySQL引擎InnoDB和MyISAM的区别?
InnoDB是MySQL默认的事务型存储引擎,支持事务、行级锁、MVCC、在线热备份等特性,主索引为聚簇索引,适用于高并发、高可靠性的场景。MyISAM设计简单,支持压缩表、空间索引,但不支持事务和行级锁,适合读多写少、不要求事务的场景。
397 9
|
9月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
548 158
|
9月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。

推荐镜像

更多