MySQL 5.7: Innodb 事务子系统优化

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

MySQL5.7 : Innodb 事务子系统优化

之前写了篇博客介绍了Percona Server对Read View的优化,顺带简单提到了MySQL5.7的事务子系统优化,详细见http://mysqllover.com/?p=834 。 另外一篇博客http://mysqllover.com/?p=1087  也有所涉及。

本文总体介绍了几个和事务子系统相关的worklog以及其代码实现。这部分代码值得细读,因为他们是5.7 Innodb比较核心的改动,极大的提升了只读场景下的性能。

WL#6047

这个worklog包含几点变化:

第一,无需显示的开启只读事务,所有的事务开始默认为只读事务,当遇到读写SQL时,自动加入读写列表。

第二,只读事务不为其分配事务ID,因此如果SHOW ENGINE INNODB STATUS时看到大量事务的ID表现的很怪异时(非常大的整数值),不要觉得奇怪。

该改进带来的最大的好处是你无需修改你的业务SQL。其实这才是用户能接受的特性,如果没有量级别的提升,谁会愿意去改代码呢?

不过显而易见的,这个优化也带了某些 运维的‘退化’,例如你再也无法从SHOW ENGINE INNODB STATUS中发现一个活跃的长时间不提交的只读事务(例如:BEGIN;SELECT;SELECT…),你需要去查询INNODB_TRX表来获得这些信息。

我们以一个典型的例子来开启这个话题,首先准备一个简单的表。隔离级别为READ-COMMIT

CREATE TABLE t1 (a INT PRIMARY KEY, b INT);

INSERT INTO t1 VALUES (1,RAND()*100),(2,RAND()*100);

BEGIN;

以BEGIN显式开启一个事务;

b) SELECT * FROM t1;

分配一个事务句柄:

ha_innobase::open

ha_innobase::info_low

update_thd

check_trx_exists

innobase_trx_allocate

trx_allocate_for_mysql

新分配的事务句柄会加入到trx_sys->mysql_trx_list,并重复使用。

开始一个只读事务,开启的事务,不分配事务ID, 不分配回滚段

row_search_mvcc->trx_start_if_not_started->trx_start_low

Assign Read View

row_search_mvcc

trx_assign_read_view

MVCC::view_open

分配的read view会拷贝当前的活跃事务ID,设置最高和最低可见事务ID,然后加入到活跃事务的read view链表上(MVCC::m_views)

c) UPDATE t1 SET b=b+1 WHERE a=2;

row_search_mvcc

lock_table

trx_set_rw_mode

将事务转换成读写事务模式,分配回滚段,分配事务ID。加入读写事务链表(trx_sys->rw_trx_ids, trx_sys->rw_trx_set, trx_sys->rw_trx_list)。

d) COMMIT; 事务提交

代码:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5209

WL#6578

在该worklog种优化了read view的创建,对MVCC控制视图部分的代码进行了重构。

具体包括以下几个方面:

在之前版本中,read view的创建的复杂度为O(N),因为需要扫描读写事务链表;

现在创建一个read view 需要以下几步:

Step 1:

                view->prepare(trx->id);

拷贝事务ID(不包含自己的事务ID),相当于做一个当前活跃读写事务的快照存放在视图中,直接使用memcpy的方式 (copy_trx_ids(trx_sys->rw_trx_ids)),这一点和percona的优化是一样的。

设置m_low_limit_no ,m_low_limit_id

Step 2:

                view->complete();

设置视图的m_up_limit_id,表示所有小于这个值的修改都可见

Step 3:

                UT_LIST_ADD_FIRST(m_views, view);

将视图加入到活跃视图链表中。

b)  在之前版本中是在持有trx_sys mutex时创建的read view。

为了降低分配/释放read view的开销,维护了两个read view链表,一个用于放当前活跃的视图链表,一个用于放空闲的、可分配的视图链表。

当系统启动时,会初始化一定数量的read view放到空闲链表上。

Percona实现了类似的方案,不同的是Percona的read view在事务完成后不是放到空闲链表,而是下次继续重用(但从活跃链表移除,不管是否是读写事务)

c) 对于autocommit的只读事务,即时当前没有活跃事务,也可能因为创建read view ,而大量别的线程在释放read view,导致trx_sys mutex冲突。

针对该问题,实际上我已经在博文http://mysqllover.com/?p=1087中描述过了,对于自动提交的查询,在关闭read view时是不从视图链表上移除的,在再次开启事务重用该read view时,如果这期间没有读写事务,都无需重新初始化read view,直接使用即可。 因此如果一台实例上的都是自动提交的只读事务,完全可以避免trx_sys mutex的开销。

d) 需要持有trx_sys mutex来遍历rw_trx_list,以判断更改是否可见,或者根据事务id获取事务对象trx_t

由于已经保存了事务ID的快照,因此直接根据二分查找查找有序数组即可,无需遍历读写事务链表

参考函数ReadView::changes_visible

trx_sys_t新增成员rw_trx_set,用于维护从trx_id到trx_t的映射。这样可以根据事务ID快速找到对应的事务对象而无需扫描事务链表。参考函数trx_get_rw_trx_by_id

主要更改:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6203

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6204

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6205

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6224

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6236

在完成上述修改后,无需再维持ro_trx_list了,因为所有事务默认都被当做只读事务,这个链表开销完全可以忽略掉的。

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/6788

WL#6899

该worklog主要实现了隐式锁向现式锁转换的一个优化点。在获取活跃事务对象时,无需持有lock_sys mutex锁。

更详细的,参考我之前写的这篇博客:http://mysqllover.com/?p=1035

WL#6906

在连续内存中预分配事务对象,保持内存的连续性有利于编译器或者CPU做出某些优化,例如内存预取之类的(不是很了解这一块,不展开叙述)

为了实现事务对象内存分配,回收等,在底层分装了一些pool类,事务对象实际上被管理在一个池结构中。

后面我再单独写一篇博客来介绍新加的这些底层结构。

在启动时,初始化trx_pools (trx_pool_init),初始化时分配4M内存,在shutdown时释放(trx_pool_close())

为了管理事务对象池,设计了三个类:TrxFactory,TrxPoolLock,TrxPoolManagerLock

获取事务对象:trx_create_low —-> trx_pools->get()

释放事务对象:trx_free —->  trx_pools->free(trx)

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

相关代码:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5744

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5750

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5753

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5756

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/5786


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
17天前
|
存储 缓存 关系型数据库
MySQL底层概述—9.ACID与事务
本文介绍了数据库事务的ACID特性(原子性、一致性、隔离性、持久性),以及事务控制的演进过程,包括排队、排它锁、读写锁和MVCC(多版本并发控制)。文章详细解释了每个特性的含义及其在MySQL中的实现方式,并探讨了事务隔离级别的类型及其实现机制。重点内容包括:ACID特性(原子性、持久性、隔离性和一致性的定义及其实现方式)、事务控制演进(从简单的全局排队到复杂的MVCC,逐步提升并发性能)、MVCC机制(通过undo log多版本链和Read View实现高效并发控制)、事务隔离级别(析了四种隔离级别(读未提交、读已提交、可重复读、可串行化)的特点及适用场景)、隔离级别与锁的关系。
|
17天前
|
SQL 关系型数据库 MySQL
MySQL底层概述—10.InnoDB锁机制
本文介绍了:锁概述、锁分类、全局锁实战、表级锁(偏读)实战、行级锁升级表级锁实战、间隙锁实战、临键锁实战、幻读演示和解决、行级锁(偏写)优化建议、乐观锁实战、行锁原理分析、死锁与解决方案
MySQL底层概述—10.InnoDB锁机制
|
19天前
|
存储 缓存 关系型数据库
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
MySQL底层概述—5.InnoDB参数优化
|
20天前
|
存储 SQL 关系型数据库
MySQL底层概述—4.InnoDB数据文件
本文介绍了InnoDB表空间文件结构及其组成部分,包括表空间、段、区、页和行。表空间是最高逻辑层,包含多个段;段由若干个区组成,每个区包含64个连续的页,页用于存储多条行记录。文章还详细解析了Page结构,分为通用部分(文件头与文件尾)、数据记录部分和页目录部分。此外,文中探讨了行记录格式,包括四种行格式(Redundant、Compact、Dynamic和Compressed),重点介绍了Compact行记录格式及其溢出机制。最后,文章解释了不同行格式的特点及应用场景,帮助理解InnoDB存储引擎的工作原理。
MySQL底层概述—4.InnoDB数据文件
|
2月前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决
|
25天前
|
关系型数据库 MySQL 数据库
Docker Compose V2 安装常用数据库MySQL+Mongo
以上内容涵盖了使用 Docker Compose 安装和管理 MySQL 和 MongoDB 的详细步骤,希望对您有所帮助。
133 42
|
16天前
|
关系型数据库 MySQL 网络安全
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
86 25
|
3天前
|
监控 关系型数据库 MySQL
云数据库:从零到一,构建高可用MySQL集群
在互联网时代,数据成为企业核心资产,传统单机数据库难以满足高并发、高可用需求。云数据库通过弹性扩展、分布式架构等优势解决了这些问题,但也面临数据安全和性能优化挑战。本文介绍了如何从零开始构建高可用MySQL集群,涵盖选择云服务提供商、创建实例、配置高可用架构、数据备份恢复及性能优化等内容,并通过电商平台案例展示了具体应用。
|
11天前
|
SQL 关系型数据库 MySQL
数据库数据恢复——MySQL简介和数据恢复案例
MySQL数据库数据恢复环境&故障: 本地服务器,安装的windows server操作系统。 操作系统上部署MySQL单实例,引擎类型为innodb,表空间类型为独立表空间。该MySQL数据库没有备份,未开启binlog。 人为误操作,在用Delete命令删除数据时未添加where子句进行筛选导致全表数据被删除,删除后未对该表进行任何操作。
|
2月前
|
缓存 关系型数据库 MySQL
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
406 0