探究InnoDB可重复读

在RC(Read Committed)和RR(Repeatable Read)两种事务隔离级别下,InnoDB存在两种数据读取方式:

快照读(Snapshot Read)

故名思意,快照读读取的都是快照数据,快照怎么来,在InnoDB引擎下是基于undo log,那undo log又是什么?举例说明,假设有这样一个表:

-- 表结构
CREATE TABLE `innodb_test` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(100) NOT NULL DEFAULT '0',
    `age` INT(11) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    INDEX `idx_age` (`age`)
)
ENGINE=InnoDB;

-- 初始数据
INSERT INTO `innodb_test` (`id`, `name`, `age`) VALUES (1, '貂蝉', 100),(2, '庄周', 120),(3, '项羽', 130);
  • id=1的初始数据行
  • 事务A执行如下语句 UPDATE innodb_test SET name='嬴政', age=90 WHERE id=1; 此时innodb会做如下操作:
    1. 把该行修改前的值Copy到undo log(Copy on write);
    2. 修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行。
  • 事务B执行如下语句 UPDATE innodb_test SET name='甄姬', age=91 WHERE id=1; 此时undo log中有2条记录,它们通过回滚指针相连。

undo log的存在解决了两个问题,一是数据回滚,二是实现了MVCC (Multi-Version Concurrency Control) ,快照读读取的就是undo log中的数据,所以这种读取是不需要加锁的,避免了读写冲突。常见的快照读语句就是最常见的SELECT,比如:

SELECT * FROM innodb_test WHERE id=1;

快照读在RC和RR隔离级别下的表现却是不一样的,为了方便说明,现在将数据还原到初始数据,然后按照下表的顺序操作。

#

事务1

事务2

1

begin;

begin;

2

SELECT * FROM innodb_test WHERE id=1;// 输出1-貂蝉-100

3

UPDATE innodb_test SET name='嬴政', age=90 WHERE id=1;

4

commit;

5

SELECT * FROM innodb_test WHERE id=1;// 输出???

  • RC 输出的是最新提交的结果(1-嬴政-90),RC级别的快照读遵循以下规则:
    • 优先读取当前事务修改的数据,自己修改的,当然可以读到了;
    • 其次读取最新已提交数据。

    会出现前后读取结果不一样的情况,但读取的是最新数据。

  • RR 输出结果和第一次查询是一样的(1-貂蝉-100),RR级别的快照读遵循以下规则:
    • 优先读取当前事务修改的数据,和RC一样;
    • 其次读取小于当前事务id的最新一条已提交数据,此时数据版本已经确定了,后面的快照读取始终读取这个版本。

    通过这样的机制,保证了快照读的可重复读,但读取到的数据很可能已经过期了。

当前读(Current Read)

而当前读,读取的是最新已提交数据,并且都会加行锁,如下语句都会产生当前读:

SELECT balabala LOCK IN SHARE MODE;
SELECT balabala FOR UPDATE;
INSERT balabala;
UPDATE balabala;
DELETE balabala;

当前读需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,第一条语句,对读取记录加S锁(共享锁),其他的操作,都加的是X锁(排它锁)。当前读在RC和RR隔离级别下的表现也是不一样的,为了方便说明,现在将数据还原到初始数据,然后按照下表的顺序操作。

#

事务1

事务2

1

begin;

begin;

2

SELECT * FROM innodb_test WHERE age=120 FOR UPDATE;

3

insert into innodb_test(name, age) values('嬴政', 120);// 此时会发生什么?

  • RC 成功执行,但会造成事务1的幻读,前后两次读取结果不一样。
  • RR 会锁等待,在RR隔离级别下,事务1的sql不仅会对该记录加X锁,还会对上下两个数据间隙加间隙锁,以此确保在数据读取期间,其它事物不会在该间隙内增加数据,从而保证可重复读。

总结

RR隔离级别下,快照读通过undo log来保证可重复读,当前读通过X(S)锁+GAP锁来保证可重复读,但显然快照读和当前读之间无法保证可重复读。本文对可重复读的实现机制做了阐述,关于undo log、锁等知识仅仅从简描述,后面有时间再详细写一下。

版权声明 本博客所有的原创文章,作者皆保留版权。转载必须包含本声明,保持本文完整,并以超链接形式注明作者高爽。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Spark学习技巧

phoenix二级索引

二级索引 二级索引是从主键访问数据的正交方式。Hbase中有一个按照字典排序的主键Rowkey作为单一的索引。不按照Rowkey去读取记录都要遍历整张表,然后按...

4169
来自专栏跟着阿笨一起玩NET

.NET事务 隐式事务(TransactionScope) 显示事务(CommittableTransaction) WebService(TransactionOption)

如需运行分布式事务,需启动Distributed Transaction Coordinator服务,如启动该服务报错,输入CMD命令msdtc -resetl...

511
来自专栏蓝天

Oracle 系统表大全

数据字典dict总是属于Oracle用户sys的。   1、用户:    select username from dba_users;   改口令    al...

652
来自专栏性能与架构

MySQL Query Cache实现原理

MySQL的Query Cache实现原理实际上并不是特别复杂,简单来说就是将客户端请求的Query语句(仅限于SELECT类型的Query)通过一定的hash...

37311
来自专栏Java Edge

1 SQL查询优化1. 获取有性能问题SQL的方法2.慢查询日志介绍3. 实时获取3.SQL的解析预处理及生成执行计划4 对特定SQL的查询优化

3467
来自专栏乐沙弥的世界

PGA的设置与调整

    PGA,即程序全局区(Program Global Area),是Oracle体系机构的重要组成部分。Oracle 数据库对系统内存的总开销即是PGA+...

572
来自专栏企鹅号快讯

大数据入门基础系列之详谈Hive的索引

视图和索引的区别(简单地来谈谈) 视图是指计算机数据库中的视图,是一个虚拟表,即不是实实在在的,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行...

1785
来自专栏云霄雨霁

Java--JDBC总结

2015
来自专栏java一日一条

理解MySQL——架构与概念

写在前面:最早接触的MySQL是在三年前,那时候MySQL还是4.x版本,很多功能都不支持,比如,存储过程,视图,触发器,更别说分布式事务等复杂特性了。但从5....

452
来自专栏java架构师

SQL Server 高性能写入的一些总结

1.1.1 摘要 在开发过程中,我们不时会遇到系统性能瓶颈问题,而引起这一问题原因可以很多,有可能是代码不够高效、有可能是硬件或网络问题,也有可能是数据库设计的...

38316

扫码关注云+社区