[译]MySQL不加锁实现一致性读的机制分析

原文直通车:Consistent Nonlocking Reads

 

MySQL的一致性读的机制是是这样实现的:InnoDB引擎为一个事务Tx提供一个在时间T1的版本快照(T1就是在本

事务中首次执行查询语句的时间点)。事务Tx中可以查询到时间点T1之前提交的数据,时间点T1之后提交的数据在

Tx中是看不到的。唯一的例外Ex是在事务Tx中可以看到在本事务中提交的数据(即便是在T1时间点还没有提交的数据)。

 

先建一个表,边理论边实践,具体看下MySQL是如何工作的。

mysql> create table mvcc(
    ->  id int primary key,
    ->  name varchar(20),
    ->  city varchar(20)
    -> ) engine innodb default charset utf8
    -> ;
Query OK, 0 rows affected (0.01 sec)

新建数据库连接,假定为session1,在session1中做如下操作:

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql> set autocommit=off;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into mvcc(id,name,city) values(1,‘name1‘,‘city1‘);
Query OK, 1 row affected (0.00 sec)

mysql> select * from mvcc;                                             T1
+----+-------+-------+
| id | name  | city  |
+----+-------+-------+
|  1 | name1 | city1 |
+----+-------+-------+
1 row in set (0.00 sec)

在上面的实验中可以看到,本事务Tx中T1时间前插入的还未提交的数据,在T1时间新建的快照里面是可以看到的。

这里说是快照,其实不是严格意义上的快照(其实是在T1点做个标记,T1之后其他事务提交的数据,利用undo log回滚,得出旧数据)

这个时候,其实事务Tx,还有其他事务,都可以更新T1时间点的快照数据。

这个例外Ex会导致如下的异常:如果你在Tx中更新了表的数据,在Tx中的Select语句会看到数据这些更新的数据,但是此时Select还可能会看到

某些行的比较老的数据(T1后有其他事务进行了数据的更新,但是在事务Tx中是看不到这些更新的,即便是其他事务提交了,所有上面这些都是在Repeatable read隔离级别的情况)

这个异常异味着在事务Tx的select看到的状态不是数据库真是的状态。

 

如果数据库的隔离级别是Repeatable Read隔离级别的(MySQL的默认级别),在事务Tx中所有的select语句所看到的快照都是跟Tx中第一条select

看到的数据是一样的。如果你先给在tx中得到新的快照,只能先commit本事务,在新开事务

 

如果数据库的隔离级别是READ COMMITTED级别的话,在Tx中的每次select操作都会重新得到一份最新的快照。

 

一致性读在Innodb引擎的在READ COMMITTED和REPEATABLE READ隔离级别下执行SELECT语句的时候的默认行为。一致性读不会在所读的表上添加

锁,所以其他session也可以同时修改这个表的数据。

 

如果你的MySQL数据库运行在默认的REPEATABLE READ的隔离级别的话。当你发出一个一致性读(也就是普通的SELECT语句)。Innodb会给你的事务

赋予一个时间点T,这个时间点T就是你在事务中执行select语句的时间点。如果数据库服务器为你当前事务赋予了一个时间点之后其他事务删除了数据,在你事务中是看不到这条数据被删除了。其他事务执行Insert你当前事务也看不到新增的数据,其他事务执行update你当前事务也看到更新的数据。

注意:
上述这种数据库快照一般使用了在一个事务中做数据读取操作(select).不太使用于DML(insert,update,delete)语句。
考虑如下场景:
如果你在一个事务中修改或者插入了某条记录并且提交了该事务。另外一个Repeatable read的transaction TX2是看不到他们的。虽然看不到这些更改或者新增的数据,但是TX2
可以删除刚才新增的数据(unbeliveable),实验说明一切。
琢磨下如下实验:

在repeatable read隔离级别的session1的transaction中建立快照(执行select语句)

步骤1

mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| REPEATABLE-READ        |
+------------------------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mvcc;
+----+-----------------+-------+
| id | name            | city  |
+----+-----------------+-------+
|  1 | name1-not-new-1 | city1 |
|  2 | name2-not-new   | city2 |
|  3 | name3           | city3 |
+----+-----------------+-------+
3 rows in set (0.00 sec)

在另一个session2的transaction中新增一条数据

步骤2

mysql> select * from mvcc;
+----+-----------------+-------+
| id | name            | city  |
+----+-----------------+-------+
|  1 | name1-not-new-1 | city1 |
|  2 | name2-not-new   | city2 |
|  3 | name3           | city3 |
+----+-----------------+-------+
3 rows in set (0.00 sec)

mysql> insert into mvcc(id,name,city) values(4,‘name4‘,‘city4‘);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mvcc;
+----+-----------------+-------+
| id | name            | city  |
+----+-----------------+-------+
|  1 | name1-not-new-1 | city1 |
|  2 | name2-not-new   | city2 |
|  3 | name3           | city3 |
|  4 | name4           | city4 |
+----+-----------------+-------+
4 rows in set (0.00 sec)

在session1中查看刚才的数据,发现查看不到,但是删除能成功。

步骤3

mysql> select * from mvcc;
+----+-----------------+-------+
| id | name            | city  |
+----+-----------------+-------+
|  1 | name1-not-new-1 | city1 |
|  2 | name2-not-new   | city2 |
|  3 | name3           | city3 |
+----+-----------------+-------+
3 rows in set (0.00 sec)

mysql> delete from mvcc where id=4;
Query OK, 1 row affected (0.00 sec)

 

这就是multi-versioned concurrency control(简称mvcc)

 

在下面的例子中,session A只有在sessionB commit并且session A commit之后才能看到b提交的数据。sessionB提交跟sessionA提交的这段间隔内sessionA就”错过了“sessionB的数据

           Session A              Session B

           SET autocommit=0;      SET autocommit=0;
time
|          SELECT * FROM t;
|          empty set
|                                 INSERT INTO t VALUES (1, 2);
|
v          SELECT * FROM t;
           empty set
                                  COMMIT;

           SELECT * FROM t;
           empty set

           COMMIT;

           SELECT * FROM t;
           ---------------------
           |    1    |    2    |
           ---------------------
           1 row in set

如果想要在在sessionA看到sessionB commit到session A commit这段时间内的数据,可以使用如下语法

SELECT * FROM t LOCK IN SHARE MODE;

[译]MySQL不加锁实现一致性读的机制分析

时间: 2024-08-25 10:17:24

[译]MySQL不加锁实现一致性读的机制分析的相关文章

mysql查询更新时的锁表机制分析(只介绍了MYISAM)

为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. 一.概述 MySQL有三种锁的级别:页级.表级.行级.MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking):BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁:InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁. MySQL这3种锁的特性可大致归纳如下: 表级锁:开销小,加

mysql查询更新时的锁表机制分析

为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. 一.概述 MySQL有三种锁的级别:页级.表级.行级.MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking):BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁:InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁. MySQL这3种锁的特性可大致归纳如下: 表级锁:开销小,加

MySQL半一致性读原理解析-从源码角度解析

1.什么是半一致性读 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一致性读原来是有条件的

众所周知,在设定了隔离等级为Repeatable Read及以上时,InnoDB 可以实现数据的一致性读.换句话来说,就是事务执行的任意时刻,读取到的数据是同一个快照,不会受到其他事务的更新影响. 以前一直以为在事务内读到的数据不会受其他事务影响,后来发现只有普通的select语句才是一致性读.如果是update, delete, select for update, select in share mode等语句是当前读,读的是数据库最新数据, 下面是两个例子. 加锁读 创建一个测试用的表,

MySQL 一致性读 深入研究 digdeep博客学习

http://www.cnblogs.com/digdeep/p/4947694.html 一致性读,又称为快照读.使用的是MVCC机制读取undo中的已经提交的数据.所以它的读取是非阻塞的. 相关文档:http://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html A consistent read means that InnoDB uses multi-versioning to present to a query a

数据库的一致性读,赃读,多线程与赃读,ACID,UNDO

赃读 对于对象额同步异步方法,我们在设计自己的程序的时候,一定要考虑的问题整体,不然会出现数据不一致的错误,很经典的就是赃读(dityread) 示例: ? package com.nbkj.thread; public class DityRead { private String username = "hsj179540"; private String password = "123"; public synchronized void setValue(S

ORACLE 物理读 逻辑读 一致性读 当前模式读总结浅析

在ORACLE数据库中有物理读(Physical Reads).逻辑读(Logical Reads).一致性读(Consistant Get).当前模式读(DB Block Gets)等诸多概念,如果不理解或混淆这些概念的话,对你深入理解一些知识无疑是一个障碍,但是这些概念确实挺让让人犯晕的.下面我们总结.学习一下这方面的知识点.捋一捋他们的关系和特点,希望对你有所帮助. 物理读(Physical Reads) 从磁盘读取数据块到内存的操作叫物理读,当SGA里的高速缓存(Cache Buffer

oracle构建一致性读

对于实际的业务系统,通常有一些热点的表,insert和delete的量非常大,这个时候就会发现一些查询语句的逻辑读比较偏高,这时可能就是oracle在构建一致性块的进行的consistent read.下面做一个测试看下:第一步准备数据: create table test( col1 varchar2(12) col2 number ext varchar2(4000) ); create index test_ind on test(user_id, col2); create sequen

oracle一致性读

sql语句执行时,产生一致性读. 什么是逻辑读? cpu在内存中读这些block的过程就叫做逻辑读(consistent get),在读的过程中产生的IO就是逻辑IO.逻辑读的过程中,是非常消耗cpu资源的.因此,执行sql的逻辑读越少越好.sql调优必须调整buffer get很大的sql语句 logical reads= consistent gets + db block gets,逻辑读其实是DB BLOCK GETS 和 consistents Gets 之和 DB block get