MySQL+InnoDB semi-consitent read原理及实现分析(转)
add by zhj: 主要讲的是在MySQL在Repeatable Read和Read Committed级别下,加锁时的不同,在Read Committed隔离级别下,只对where
中满足条件的并且已经存在的记录加锁,一般来说,这是我们期望的。而在Repeatable Read下,如果update/delete中的查询使用非唯一索引时,
对满足条件但并不存在的记录也会加锁,这称为间隙锁;如果update/delete查询不能使用索引,那会对表中的所有记录加锁。
原文:http://hedengcheng.com/?p=220
semi-consistent简介
对于熟悉MySQL,或者是看过InnoDB源码的朋友们来说,可能会听说过一个新鲜的名词:semi-consistent read 。 何谓semi-consistent read?以下一段文字,摘于semi-consistent read一文:
A type of read operation used for UPDATE statements, that is a combination of read committed and consistent read. When an UPDATE statement examines a row that is already locked, InnoDB returns the latest committed version to MySQL so that MySQL can determine whether the row matches the WHERE condition of the UPDATE. If the row matches (must be updated), MySQL reads the row again, and this time InnoDB either locks it or waits for a lock on it. This type of read operation can only happen when the transaction has the read committed isolation level, or when the innodb_locks_unsafe_for_binlog option is enabled.
简单来说,semi-consistent read是read committed与consistent read两者的结合。一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。
semi-consistent read只会发生在read committed隔离级别下,或者是参数innodb_locks_unsafe_for_binlog被设置为true。
MySQL server与InnoDB引擎是如何进行交互?InnoDB引擎如何实现semi-consistent read?请见下面的详细分析。
semi-consistent实现
MySQL Server层
从上面的描述中可以看出,semi-consistent read仅仅针对于update操作,因此在sql_update.cc的mysql_update方法中,有如下调用:
sql_update.cc::mysql_update()
// 通知底层引擎,尝试进行semi consistent read
// 是否真正进行semi consistent read,由底层引擎决定
table->file->try_semi_consistent_read(1);
// InnoDB引擎决定当前update是否可以进行semi-consistent read
// 具体的处理方法,在下节中分析
ha_innodb.cc::try_semi_consistent_read(bool yes);
// 进行update的读与更新操作
…
// update操作完成之后,关闭semi-consistent read
table->file->try_semi_consistent_read(0);
MySQL Server层处理semi-consistent较为简单,接下来看看InnoDB Engine的处理方式。
InnoDB Engine层
InnoDB Engine层面,对于semi-consistent read的处理,包括两方面的逻辑:
- 判断当前语句是否可以支持semi-consistent read
- fetch next时,对于semi-consistent read的特殊处理
是否采用semi-consistent read
前面提到,MySQL Server在update时,会调用引擎的try_semi_consistent_read方法,来尝试进行semi-consistent read,而是否进行semi-consistent read,则交由底层处理。
ha_innodb.cc::try_semi_consistent_read()
if (yes &&
(srv_locks_unsafe_for_binlog
|| prebuilt->trx->isolation_level <= TRX_ISO_READ_COMMITTED))
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
简单分析下,当用户设置系统参数innodb_locks_unsafe_for_binlog为true,或者是采用的事务隔离级别为read committed(或以下)时,设置prebuilt->row_read_type参数,标识当前update语句使用semi-consistent read,fetch next时需要有针对性的做特殊处理。
Fetch Next特殊处理逻辑
InnoDB fetch next的主函数入口是row_search_for_mysql,此函数如何针对性的处理semi-consistent read呢?
row0sel.c::row_search_for_mysql()
…
// 尝试对于定位到的记录加锁
err = sel_set_rec_lock();
…
case DB_LOCK_WAIT:
// 如果加锁需要等待,则判断是否可以进行semi-consistent read
// 判断条件为:
// 1. prebuilt->row_read_type必须设置为ROW_READ_TRY_SEMI_CONSISTEN
// 2. 当前scan必须是range scan或者是全表扫描,而非unique scan
// 3. 当前索引必须是聚簇索引
// 4. 不满足以上三个条件,就不能进行semi-consistent read,进行加锁等待
// 注意:若不需要加锁等待,那么也不需要进行semi-consistent read,直接
// 读取记录的最新版本即可,没有加锁等待的开销。
if ((prebuilt->row_read_type != ROW_READ_TRY_SEMI_CONSISTENT)
|| unique_search
|| index != clust_index)
goto lock_wait_or_error;
// 可以进行semi-consistent read,根据记录的当前版本,构造最新的commit版本
// 若没有commit版本,当前版本为最新版本,则直接读取下一条记录
// 若存在commit版本,则设置did_semi_consistent_read为TRUE
row_sel_build_committed_vers_for_mysql();
if (old_vers == NULL)
goto next_rec;
did_semi_consistent_read = TRUE;
…
// 若本次update scan,由于加锁等待,使用了semi-consistent,则设置相应的参数
// 该参数,在下一小节提到的MySQL针对semi-consistent优化中有用
if (did_semi_consistent_read)
prebuilt->row_read_type = ROW_READ_DID_SEMI_CONSISTENT;
else
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
// 至此,InnoDB的fetch next针对semi-consistent read的处理完毕
优化:Unlock unmatched row
上面提到的是semi-consistent read的功能实现,除此之外,MySQL针对semi-consistent read,还做了优化措施:对于update scan返回的不满足条件的记录,提前放锁。
MySQL Server层流程:
sql_update.cc::mysql_update()
// 判断当前scan返回的记录,是否满足update的where条件
// 若满足,则进行update操作
if (!(select && select->skip_record())
…
// 若不满足update的where条件,则选择将当前记录上的行锁提前释放
else
table->file->unlock_row();
InnoDB Engine层流程:
ha_innobd.cc::unlock_row();
switch (prebuilt->row_read_type)
// 若系统未设置参数innodb_locks_unsafe_for_binlog,同时隔离级别大于
// TRX_ISO_READ_COMMITTED,则不可提前释放不满足条件的行锁
// 否则可以提前释放不满足条件的行锁
case ROW_READ_WITH_LOCKS:
if (!srv_locks_unsafe_for_binlog &&
prebuilt->trx->isolation_level > TRX_ISO_READ_COMMITTED)
break;
// 若当前系统已采用SEMI_CONSISTENT read,但是没有锁等待,加锁直接成功
// 那么此时直接释放不满足条件的行锁
case ROW_READ_TRY_SEMI_CONSISTENT:
row_unlock_for_mysql();
// 若当前系统已采用SEMI_CONSISTENT read,并且有锁等待,构造了commit版本
// 没有在commit版本上加锁,因此也无锁可放,直接返回即可
case ROW_READ_DID_SEMI_CONSISTENT:
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
break;
semi-consistent优缺点分析
优点
- 减少了更新同一行记录时的冲突,减少锁等待。
无并发冲突,读记录最新版本并加锁;有并发冲突,读事务最新的commit版本,不加锁,无需锁等待。
- 可以提前放锁,进一步减少并发冲突概率。
对于不满足update更新条件的记录,可以提前放锁,减少并发冲突的概率。
- 在理解了semi-consistent read原理及实现方案的基础上,可以酌情考虑使用semi-consistent read,提高系统的并发性能。
缺点
- 非冲突串行化策略,因此对于binlog来说,是不安全的
两条语句,根据执行顺序与提交顺序的不同,通过binlog复制到备库后的结果也会不同。不是完全的冲突串行化结果。
因此只能在事务的隔离级别为read committed(或以下),或者设置了innodb_locks_unsafe_for_binlog参数的情况下才能够使用。
测试用例
构造semi-consistent read
set binlog_format=mixed;
set session transaction isolation level repeatable read;
create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1;
insert into t1 values (1),(2),(3),(4),(5),(6),(7);
session 1: session 2:
set autocommit=0;
update t1 set a = a + 10;
set binlog_format=mixed;
set session transaction isolation level read committed;
update t1 set a = a + 100 where a > 10;
此时,session 2不需要等待session 1,虽然session 1的更新后项满足session 2的条件,但是由于session 2进行了semi-consistent read,读取到的记录的前项为(1-7),不满足session 2的更新where条件,因此session 2直接返回。
session 2直接返回,0 rows affected。
构造unlock unmatched row
set binlog_format=mixed;
set session transaction isolation level repeatable read;
create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1;
insert into t1 values (1),(2),(3),(4),(5),(6),(7);
session 1: session 2:
set autocommit=0;
update t1 set a = a + 10;
commit;
set binlog_format=mixed;
set session transaction isolation level repeatable read;
set autocommit = 0;
update t1 set a = a + 100 where a < 10;
select * from t1 lock in share mode;
session 1在session 2开始前已经提交,session 2可以进行semi-consistent read。并且读到的都是session 1的更新后项,完成加锁。但是由于更新后项均不满足session 2的where条件,session 2会释放所有行上的锁(由MySQL Server层判断并调用unlock_row方法释放行锁)。
此时,session 1再次执行select * from t1 lock in share mode语句,直接成功。因为session 2已经将所有的行锁提前释放。
朋友们可以试试将session 2的隔离级别改为repeatable read,那么此时session 1就会等待session 2提交。
MySQL+InnoDB semi-consitent read原理及实现分析(转)的更多相关文章
- 浅析Mysql InnoDB存储引擎事务原理
浅析Mysql InnoDB存储引擎事务原理 大神:http://blog.csdn.net/tangkund3218/article/details/47904021
- MySQL InnoDB 实现高并发原理
MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...
- 搞懂MySQL InnoDB事务ACID实现原理
前言 说到数据库事务,想到的就是要么都做修改,要么都不做.或者是ACID的概念.其实事务的本质就是锁和并发和重做日志的结合体.那么,这一篇主要讲一下InnoDB中的事务到底是如何实现ACID的. 原子 ...
- mysql innodb引擎 一次线上死锁分析排查步骤
我们的线上erp系统一天使用人员反映部分数据死活保存不上而且页面操作很慢.开始以为操作数据量大的原因, 后来查看了我们线上的glowroot系统,发现slowtrace中有超长时间的访问,点开查看详情 ...
- 一文快速搞懂MySQL InnoDB事务ACID实现原理(转)
这一篇主要讲一下 InnoDB 中的事务到底是如何实现 ACID 的: 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 隔 ...
- MySQL InnoDB MVCC
MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...
- MYSQL索引结构原理、性能分析与优化
[转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...
- MySQL主从同步的延迟原理
1. MySQL数据库主从同步延迟原理. 答:谈到MySQL数据库主从同步延迟原理,得从mysql的数据库主从复制原理说起,mysql的主从复制都是单线程的操作,主库对所有DDL和DML产生binlo ...
- MYSQL主从不同步延迟原理
1. MySQL数据库主从同步延迟原理. 要说延时原理,得从mysql的数据库主从复制原理说起,mysql的主从复制都是单线程的操作, 主库对所有DDL和DML产生binlog,binlog是 ...
- 浅析MySQL InnoDB的隔离级别
MySQL InnoDB存储引擎中事务的隔离级别有哪些?对应隔离级别的实现机制是什么? 本文就将对上面这两个问题进行解答,分析事务的隔离级别以及相关锁机制. 隔离性简介 隔离性主要是指数据库系统提供一 ...
随机推荐
- 【css】css 中文字体 unicode 对照表
css 中文字体可以用 unicode 格式来表示,比如“宋体”可以用 \5B8B\4F53 来表示.具体参考下表: 中文名 英文名 unicode 宋体 SimSun \5B8B\4F53 黑体 S ...
- Ubuntu下搜狗输入法只显示黑框,不显示输入的汉字选项
1. cd ~/.config 2.删除三个文件夹: SogouPY, SogouPY.users, sogou-qimpanel 然后重启输入法
- linux mysql卸载
卸载mysql 1.查找以前是否装有mysql 命令:rpm -qa|grep -i mysql 可以看到mysql的包: mysql-libs-5.1.71-1.el6.x86_64 2.删除mys ...
- [转]搞个这样的 APP 要多久
我有些尴尬地拿着水杯,正对面坐着来访的王总,他是在别处打拼的人,这几年据说收获颇丰,见移动互联网如火如荼,自然也想着要进来干一场,尽管王总从事的行当也算跟IT沾边,但毕竟太长时间不接触技术,有些东西不 ...
- nginx 配置信息
主配置文件: cat /etc/nginx/nginx.conf# For more information on configuration, see:# * Official English Do ...
- IE11不能使用F12功能DOM资源管理器无法使用
https://www.ludou.org/win7-ie-11-f12-bug.html 32位win7下载补丁:http://www.microsoft.com/zh-CN/download/de ...
- jexl2 执行字符串Java代码
一,引入jar包, <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-jexl --><depen ...
- 怎样利用Heartbeat与Floating IP在Ubuntu 14.04上创建高可用性设置
提供 ZStack社区 内容简单介绍 Heartbeat是一款开源程序,负责将集群基础设施容量--包括集群成员与消息收发--交付至客户server. Hearbeat在高可用性server基础设施其中 ...
- python使用requests发送multipart/form-data请求数据
def client_post_mutipart_formdata_requests(request_url,requestdict): #功能说明:发送以多部分表单数据格式(它要求post的消息体分 ...
- iOS - 让WKWebView 支持 NSURLProtocol
iOS8以后,苹果推出了新框架Webkit,提供了替换UIWebView的组件WKWebView.各种UIWebView的问题没有了,速度更快了,占用内存少了,一句话,WKWebView是App内部加 ...