MySQL 5.7: 为innodb引擎彻底移除thr_lock

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

在继MDL锁系统改为使用LOCK-FREE方式实现后,Server层又再5.7.5迎来重大改动:THR_LOCK被彻底移除,而是完全使用MDL锁来实现。

对应的change log entry:

Scalability for InnoDB tables was improved by avoiding THR_LOCK locks. As a result of this change, DML statements for InnoDB tables that previously waited for a THR_LOCK lock will wait for a metadata lock:

  • Explicitly or implicitly started transactions that update any table (transactional or nontransactional) will block and be blocked by LOCK TABLES ... READ for that table. This is similar to how LOCK TABLES ... WRITE works.
  • Tables that are implicitly locked by LOCK TABLES now will be locked using metadata locks rather than THR_LOCKlocks (for InnoDB tables), and locked using metadata locks in addition to THR_LOCK locks (for all other storage engines). Implicit locks occur for underlying tables of a locked view, tables used by triggers for a locked table, or tables used by stored programs called from such views and triggers.Multiple-table updates now will block and be blocked by concurrent LOCK TABLES ... READ statements on any table in the update, even if the table is used only for reading.
  • HANDLER ... READ for any storage engine will block and be blocked by a concurrent LOCK TABLES ... WRITE, but now using a metadata lock rather than aTHR_LOCK lock.

这两大改动,完全消除了Server层的MDL锁开销和THR LOCK锁开销,在PK-SELECT查询中,瓶颈居然压到了Lock_grant上…

这篇博客是我看代码时的记录,对细节不感兴趣的直接跳到最后,看看官方博客和对应worklog的描述,比我清楚多啦 :)

在此之前,先来了解下什么叫做thr lock. thr lock实际上是早期版本控制Server层表级并发的锁。

0. 主要数据结构

THR_LOCK_DATA   

THR_LOCK_INFO

MYSQL_LOCK

锁对象: THR LOCK锁对象具有比较特殊的结构

在“Understanding MySQL Internal”一书中也有一节专门讲Table Lock Manager:

table lock管理模块为每个表维护了四个队列:

Current read-lock queue (lock->read)

Pending read-lock queue(lock->read_wait)

Current write-lock queue(lock->write)

Pending write-lock queue(lock->write_wait)

当前正在等待的线程都会被加入到write_wait 和 read_wait队列中。而已经拥有了的则加入到read 和write队列。

1.加锁

Backtrace:

lock_tables->mysql_lock_tables->thr_multi_lock->thr_lock

根据SQL类型的不同,加锁的类型也不同,例如对于SELECT操作,类型为lock_type=TL_READ,而对于普通的DML操作,类型为TL_WRITE_ALLOW_WRITE

显然对于正常的DML/SELECT操作,这两种锁类型是不冲突的。

LOCK TABLE t1 READ

lock_type=TL_READ_NO_INSERT

LOCK TABLE t1 WRITE

lock_type=TL_WRITE

TRUNCATE TABLE t1

lock_type=TL_WRITE

更多的例子,不一一列举了,具体的描述见枚举类型thr_lock_type。

如果线程持有更高级别的锁在表上,就不会去加低级别的锁,例如我们先执行LOCK TABLE t1 WRITE 会执行加锁,随后执行TRUNCATE TABLE,就无需再次加锁了。

锁冲突检测,举个简单的例子:

Session 1: BEGIN; LOCK TABLE t1 READ;

Session 2: BEGIN; INSERT INTO t1 VALUES(1,2,3);

Session 2将被堵塞住,Backtrace为:

1 pthread_cond_timedwait,safe_cond_timedwait(thr_mutex.c:278),inline_mysql_cond_timedwait(mysql_thread.h:1188),wait_for_lock(thr_lock.c:462),thr_lock(thr_lock.c:787),thr_multi_lock(thr_lock.c:1060),mysql_lock_tables(lock.cc:321),lock_tables(sql_base.cc:5920),mysql_insert(sql_insert.cc:908)

如果使用LOCK TABLE t1 WRITE, 那么会发现Session 2堵在MDL这了。

key function:

check_locks : 检查锁是否冲突

“以下摘录自Understanding MySQL Internals一书”

关于读锁:

如果没有写锁或者在等待队列中的写锁,则可以执行,否则加入到read_wait队列中.

在等待队列中锁的优先级规则为:

#TL_WRITE的优先级总是比读锁的优先级要高,但TL_READ_HIGH_PRIORITY除外.

#TL_READ_HIGH_PRIORITY 优先级在任何的Pending 写锁之上

#所有在write_wait队列中的写锁,如果不是TL_WRITE,则其优先级低于读锁

当前高优先的写锁会导致其他锁请求suspend并进入condtion wait.但以下场景除外:

#通过THR_LOCK中的回调指针check_status,存储引擎层可能允许除TL_READ_NO_INSERT的读锁和一个TL_WRITE_CONCURRENT_INSERT 锁

#TL_WRITE_ALLOW_WRITE 允许除TL_WRITE_ONLY外的所有读锁和写锁

#TL_WRITE_ALLOW_READ 允许除TL_READ_NO_INSERT外的所有读锁

#TL_WRITE_DELAYED 允许除TL_READ_NO_INSERT外的所有读锁

#TL_WRITE_CONCURRENT_INSERT允许除TL_READ_NO_INSERT外的所有读锁

关于写锁

如果存在读锁,则读锁会阻塞写锁,但以下情况除外:

#请求的锁类型为TL_WRITE_DELAYED

#请求的锁类型为TL_WRITE_CONCURRENT_INSERT或者TL_WRITE_ALLOW_WRITE,并且在读锁队列中没有没有TL_READ_NO_INSERT。

2.解锁

SELECT

Backtrace:

mysql_execute_command->close_thread_tables->mysql_unlock_tables->thr_multi_unlock->thr_unlock

3.问题

实际上在Innodb场景下,大部分表级锁检测都已经被Metadata Lock覆盖了,INNODB 唯一严重依赖THR_LOCK的地方是LOCK TABLE READ, 以此来保证在LOCK TABLE READ 能够完全阻塞住.  对于DDL和DML的冲突,已有的逻辑已经能保证这一点了。

因此在新的逻辑中,只需要增加一种新的MDL锁类型来代替THR_LOCK在LOCK TABLE READ场景下的角色即可。THR LOCK的调用可以完全被避免掉。

不过这也引入了一点不兼容性:LOCK TABLE READ 将会堵塞住MULTI-UPDATE涉及到的表,即时这个表只是用来进行读操作。

0.如何避免使用thr_lock

需要注意,这个是引擎相关的,某些存储引擎可能依旧依赖于thr lock,因此Innodb对此有特殊处理。

一个新的存储引擎FLAG引入,以对不同的引擎做区分:

HA_NO_READ_LOCAL_LOCK: 存储引擎不支持 LOCK TABLE … READ LOCAL,并且不想在 handler::store_lock接口中将其升级为LOCK TABLE…READ

引入新的MDL锁类型:

MDL_SHARED_READ_ONLY (简称SRO):允许读,同时阻塞写入,用于代替LOCK TABLE READ锁使用的THR LOCK (TL_READ_NO_INSERT)

MDL_SHARED_WRITE_LOW_PRIO: 当DML存在LOW_PRIORITY子句时,使用该类型的MDL,其权限比SRO要低

针对LOCK TABLES操作,使用SNRW锁来替换TL_WRITE锁

lock_count用于返回引擎层对单个实例表需要的thr lock个数,类似Merge引擎或者partition表可能需要多个thr lock。而对于innodb,之前版本直接引用handler的虚函数,返回1。在5.7.5版本中,为innodb定义了对应接口:

uint

ha_innobase::lock_count(void) const

/*===============================*/

{

        return 0;

}

返回0到上层函数get_lock_data,那么对于Innodb表,将不会再为其创建thr lock锁。如此简单的避免了为Innodb表创建THR LOCK.

ha_innobase::store_lock :不再存储thr lock 类型

2346                                                 TL_IGNORE */

2347 @@ -13474,8 +13495,6 @@

2348                 lock.type = lock_type;

2349         }

2350

2351 –       *to++= &lock;

ha_innobase::store_lock() doesn’t try to store type of thr_lock.c lock

  in MYSQL_LOCK::locks[] array it gets as a parameter

关于如何协调新加MDL类型与已有类型之间的冲突检测和优先级关系,代码做了大量的修改,不细看了,感兴趣的直接翻worklog

worklog:

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

前置补丁:

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

 MDL_context::is_lock_owner 更名为MDL_context::owns_equal_or_stronger_lock

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8110  //单元测试修改

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

一些函数的返回值从bool修改为void,因为从未使用到.

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

相关:http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8633

官方博客:

http://mysqlserverteam.com/removing-scalability-bottlenecks-in-the-metadata-locking-and-thr_lock-subsystems-in-mysql-5-7/


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
存储 关系型数据库 MySQL
MySQL InnoDB数据存储结构
MySQL InnoDB数据存储结构
|
1月前
|
存储 缓存 关系型数据库
MySQL的varchar水真的太深了——InnoDB记录存储结构
varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。
832 6
MySQL的varchar水真的太深了——InnoDB记录存储结构
|
2月前
|
存储 缓存 关系型数据库
InnoDB 引擎底层存储和缓存原理
InnoDB 引擎底层存储和缓存原理
|
2月前
|
存储 缓存 关系型数据库
MySQL - 存储引擎MyISAM和Innodb
MySQL - 存储引擎MyISAM和Innodb
|
12天前
|
存储 关系型数据库 MySQL
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
28 0
|
2月前
|
存储 关系型数据库 MySQL
InnoDB 引擎底层事务的原理
InnoDB 引擎底层事务的原理
|
2月前
|
存储 SQL 关系型数据库
Mysql专栏 - mysql、innodb存储引擎、binlog的工作流程
Mysql专栏 - mysql、innodb存储引擎、binlog的工作流程
76 0
|
15天前
|
关系型数据库 MySQL 数据库
mysql卸载、下载、安装(window版本)
mysql卸载、下载、安装(window版本)
|
1月前
|
关系型数据库 MySQL 数据库
rds安装数据库客户端工具
安装阿里云RDS的数据库客户端涉及在本地安装对应类型(如MySQL、PostgreSQL)的客户端工具。对于MySQL,可选择MySQL Command-Line Client或图形化工具如Navicat,安装后输入RDS实例的连接参数进行连接。对于PostgreSQL,可以使用`psql`命令行工具或图形化客户端如PgAdmin。首先从阿里云控制台获取连接信息,然后按照官方文档安装客户端,最后配置客户端连接以确保遵循安全指引。
84 1
|
4天前
|
关系型数据库 MySQL 数据库
《MySQL 简易速速上手小册》第1章:MySQL 基础和安装(2024 最新版)
《MySQL 简易速速上手小册》第1章:MySQL 基础和安装(2024 最新版)
27 4