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

在继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/

时间: 2024-09-12 00:43:45

MySQL 5.7: 为innodb引擎彻底移除thr_lock的相关文章

MySQL · 源码分析 · Innodb 引擎Redo日志存储格式简介

MySQL有多种日志.不同种类.不同目的的日志会记录在不同的日志文件中,它们可以帮助你找出mysqld内部发生的事情.比如错误日志:用来记录启动.运行或停止mysqld进程时出现的问题:查询日志:记录建立的客户端连接和执行的语句:二进制日志:记录所有更改数据的语句,主要用于逻辑复制:慢日志:记录所有执行时间超过long_query_time秒的所有查询或不使用索引的查询.而对MySQL中最常用的事务引擎innodb,redo日志是保证事务一致性非常重要的.本文结合MySQL版本5.6为分析源码介

MySQL中MyISAM引擎与InnoDB引擎性能简单测试

[硬件配置] CPU : AMD2500+ (1.8G) 内存: 1G/现代 硬盘: 80G/IDE [软件配置] OS : Windows XP SP2 SE : PHP5.2.1 DB : MySQL5.0.37 Web: IIS6 [MySQL表结构] CREATE TABLE `myisam` ( `id` int(11) NOT NULL auto_increment, `name` varchar(100) default NULL, `content` text, PRIMARY

巧用MySQL InnoDB引擎锁机制解决死锁问题

最近,在项目开发过程中,碰到了数据库死锁问题,在解决问题的过程中,笔者对MySQL InnoDB引擎锁机制的理解逐步加深. 案例如下: 在使用Show innodb status检查引擎状态时,发现了死锁问题: *** (1) TRANSACTION: TRANSACTION 0 677833455, ACTIVE 0 sec, process no 11393, OS thread id 278546 starting index read mysql tables in use 1, loc

MySQL从MyISAM引擎转换到InnoDB引擎需要注意的地方

  分析 当了解完两种引擎的不同之处,很轻松的就能知道有哪些关键点了. 总的来说,从MyISAM转向InnoDB的注意事项有: 1.MyISAM的主键索引中,可以在非第一列(非第一个字段)使用自增列,而InnoDB的主键索引中包含自增列时,必须在最前面;这个特性在discuz论坛中,被设计用于"抢楼"功能,因此,若有类似的业务,则无法将该表从MyISAM转成InnoDB,需要自行变通实现(我们则是将其改到Redis中实现); 2.不带条件频繁统计全表总记录数时(SELECT COUNT

MySQL innodb引擎详解

innodb是事物安全的MySQL存储引擎 是oltp应用中核心表的首选存储引擎 MySQL第一个支持事物的存储引擎是BDB MySQL第一个完整支持ACID事物是innodb innodb的特点 行锁设计 支持MVCC 支持外键 提供一致性非锁定读 同时被设计用来最有效的利用以及使用内存和cpu 版本 功能 老版本innodb 支持ACID 行锁设计 MVCC innodb 1.0.x 增加了compress和dynamic页格式 innodb 1.1.x 增加了Linux AIO 多回滚段

《MySQL技术内幕:InnoDB存储引擎第2版》——1.3 MySQL存储引擎

1.3 MySQL存储引擎 通过1.2节大致了解了MySQL数据库独有的插件式体系结构,并了解到存储引擎是MySQL区别于其他数据库的一个最重要特性.存储引擎的好处是,每个存储引擎都有各自的特点,能够根据具体的应用建立不同存储引擎表.对于开发人员来说,存储引擎对其是透明的,但了解各种存储引擎的区别对于开发人员来说也是有好处的.对于DBA来说,他们应该深刻地认识到MySQL数据库的核心在于存储引擎. 由于MySQL数据库的开源特性,用户可以根据MySQL预定义的存储引擎接口编写自己的存储引擎.若用

《MySQL技术内幕:InnoDB存储引擎第2版》——2.8 小结

2.8 小结 本章对InnoDB存储引擎及其体系结构进行了概述,先给出了InnoDB存储引擎的历史.InnoDB存储引擎的体系结构(包括后台线程和内存结构):之后又详细介绍了InnoDB存储引擎的关键特性,这些特性使InnoDB存储引擎变得更具"魅力":最后介绍了启动和关闭MySQL时一些配置文件参数对InnoDB存储引擎的影响. 通过本章的铺垫,读者在学习后面的内容时就会对InnoDB引擎理解得更深入和更全面.第3章开始介绍MySQL的文件,包括MySQL本身的文件和与InnoDB存

MySQL数据库InnoDB引擎下服务器断电数据恢复方法_Mysql

说明: 线上的一台MySQL数据库服务器突然断电,造成系统故障无法启动,重新安装系统后,找到之前的MySQL数据库文件夹. 问题: 通过复制文件的方式对之前的MySQL数据库进行恢复,发现在程序调用时找不到数据库中的表,造成网站无法正常访问. 分析: 1.MySQL数据库,使用拷贝文件方式来恢复数据库,只支持MyISAM引擎: 2.如果有数据库或数据表使用了InnoDB引擎,恢复的时候,必须连同MySQL数据库目录下的ibdata1文件一起拷贝过来. 解决办法: 1.停止MySQL服务 serv

MySQL的InnoDB引擎入门学习教程_Mysql

MySQL发展到今天,InnoDB引擎已经作为绝对的主力,除了像大数据量分析等比较特殊领域需求外,它适用于众多场景.然而,仍有不少开发者还在"执迷不悟"的使用MyISAM引擎,觉得对InnoDB无法把握好,还是MyISAM简单省事,还能支持快速COUNT(*).本文是由于最近几天帮忙处理discuz论坛有感而发,希望能对广大开发者有帮助. 1. 快速认识InnoDBInnoDB是MySQL下使用最广泛的引擎,它是基于MySQL的高可扩展性和高性能存储引擎,从5.5版本开始,它已经成为了