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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介:

额,这个标题有点大,实际上只是我在测试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


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3天前
|
存储 关系型数据库 MySQL
MySQL的优化利器⭐️Multi Range Read与Covering Index是如何优化回表的?
本文以小白的视角使用通俗易懂的流程图深入浅出分析Multi Range Read与Covering Index是如何优化回表
|
4天前
|
运维 负载均衡 关系型数据库
MySQL高可用解决方案演进:从主从复制到InnoDB Cluster架构
MySQL高可用解决方案演进:从主从复制到InnoDB Cluster架构
|
4天前
|
存储 SQL 关系型数据库
mysql中MyISAM和InnoDB的区别是什么
mysql中MyISAM和InnoDB的区别是什么
12 0
|
10天前
|
存储 关系型数据库 MySQL
【MySQL系列笔记】InnoDB引擎-数据存储结构
InnoDB 存储引擎是MySQL的默认存储引擎,是事务安全的MySQL存储引擎。该存储引擎是第一个完整ACID事务的MySQL存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和 CPU。因此很有必要学习下InnoDB存储引擎,它的很多架构设计思路都可以应用到我们的应用系统设计中。
159 4
|
15天前
|
存储 监控 关系型数据库
【MySQL】InnoDB 什么情况下会产生死锁
【MySQL】InnoDB 什么情况下会产生死锁
|
17天前
|
SQL 关系型数据库 MySQL
|
17天前
|
存储 缓存 关系型数据库
|
2天前
|
关系型数据库 MySQL 数据库
docker MySQL删除数据库时的错误(errno: 39)
docker MySQL删除数据库时的错误(errno: 39)
|
2天前
|
关系型数据库 MySQL 数据库连接
用Navicat备份Mysql演示系统数据库的时候出:Too Many Connections
用Navicat备份Mysql演示系统数据库的时候出:Too Many Connections
|
3天前
|
存储 Oracle 关系型数据库
oracle 数据库 迁移 mysql数据库
将 Oracle 数据库迁移到 MySQL 是一项复杂的任务,因为这两种数据库管理系统具有不同的架构、语法和功能。
15 0