Mysql加锁过程详解(2)-关于mysql 幻读理解出现了幻读,那么不是说mysql的重复读解决了幻读的么?

那么,InnoDB指出的可以避免幻读是怎么回事呢?

http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html

By default, InnoDB operates in REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlog system variable disabled. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows (see Section 13.6.8.5, “Avoiding the Phantom Problem Using Next-Key Locking”).

准备的理解是,当隔离级别是可重复读,且禁用innodb_locks_unsafe_for_binlog的情况下,在搜索和扫描index的时候使用的next-key locks可以避免幻读。

关键点在于,是InnoDB默认对一个普通的查询也会加next-key locks,还是说需要应用自己来加锁呢?如果单看这一句,可能会以为InnoDB对普通的查询也加了锁,如果是,那和序列化(SERIALIZABLE)的区别又在哪里呢?

MySQL manual里还有一段:

13.2.8.5. Avoiding the Phantom Problem Using Next-Key Locking (http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html)

To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking.

You can use next-key locking to implement a uniqueness check in your application: If you read your data in share mode and do not see a duplicate for a row you are going to insert, then you can safely insert your row and know that the next-key lock set on the successor of your row during the read prevents anyone meanwhile inserting a duplicate for your row. Thus, the next-key locking enables you to “lock” the nonexistence of something in your table.

我的理解是说,InnoDB提供了next-key locks,但需要应用程序自己去加锁。manual里提供一个例子:

SELECT * FROM child WHERE id > 100 FOR UPDATE;

这样,InnoDB会给id大于100的行(假如child表里有一行id为102),以及100-102,102+的gap都加上锁。

可以使用show innodb status来查看是否给表加上了锁。

下面看列子

例子1

a

b

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET AUTOCOMMIT=0;

BEGIN

BEGIN

SELECT * FROM test WHERE a='1' FOR UPDATE;

SELECT * FROM test

 

INSERT test VALUES(1,1);

锁住了

INSERT test VALUES(1,1);

成功

COMMIT

 

COMMIT

避免幻读可以select锁住,再insert

例子2

a

b

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET AUTOCOMMIT=0;

BEGIN

BEGIN

SELECT * FROM test WHERE a='1' FOR UPDATE;

SELECT * FROM test

 

INSERT test VALUES(2,2);

连2也被锁住了?

INSERT test VALUES(1,1);

成功

COMMIT

这次提交成功

COMMIT

其他尝试,这种情况无论插入2还是5都被锁住等等

例子3

a

b

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET AUTOCOMMIT=0;

BEGIN

BEGIN

SELECT * FROM test

SELECT * FROM test

 

 

SELECT * FROM test WHERE a='1' FOR UPDATE;

SELECT * FROM test

 

INSERT test VALUES(2,2);

COMMIT

COMMIT

成功

COMMIT

COMMIT

例子 4

a

b

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET AUTOCOMMIT=0;

BEGIN

BEGIN

SELECT * FROM test

SELECT * FROM test

 

 

SELECT * FROM test WHERE a='2' FOR UPDATE;

 

SELECT * FROM test

 

INSERT test VALUES(2,2);

INSERT test VALUES(5,5);

 

COMMIT

COMMIT

例子 5

a

b

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET AUTOCOMMIT=0;

BEGIN

BEGIN

SELECT * FROM test

SELECT * FROM test

 

 

SELECT * FROM test WHERE a='1' FOR UPDATE;

INSERT test VALUES(5,5);

插入5成功了

UPDATE test SET b=33 WHERE a='3'

 

INSERT test VALUES(2,2);

2也可以

UPDATE test SET b=11 WHERE a='1'

1锁住了

COMMIT

 

COMMIT

SELECT * FROM test

SELECT * FROM test

 

 

以上例子说明,forupdate时候,id为主键,RR策略时候,锁住了的条件符合的行,但是如果条件找不到任何列,锁住的是整个表,(主键,唯一索引,非唯一索引,(insert,update对于gab锁不通),参考第一章,第七章,第九章)

------------------------------------------------------------------

再来看大神的解释 :链接: http://blog.bitfly.cn/post/mysql-innodb-phantom-read/

再看一个实验,要注意,表t_bitfly里的id为主键字段。实验三:

t Session A                 Session B

|

| START
TRANSACTION;       
START TRANSACTION;

|

| SELECT * FROM t_bitfly

| WHERE id<=1

| FOR UPDATE;

| +------+-------+

| | id   | value |

| +------+-------+

| |    1 |
a    
|

| +------+-------+

|                          
INSERT INTO t_bitfly

|                          
VALUES (2, 'b');

|                          
Query OK, 1 row affected

|

| SELECT * FROM t_bitfly;

| +------+-------+

| | id   | value |

| +------+-------+

| |    1 |
a    
|

| +------+-------+

|                          
INSERT INTO t_bitfly

|                          
VALUES (0, '0');

|                          
(waiting for lock ...

|                          
then timeout)

|                          
ERROR 1205 (HY000):

|                          
Lock wait timeout exceeded;

|                          
try restarting transaction

|

| SELECT * FROM t_bitfly;

| +------+-------+

| | id   | value |

| +------+-------+

| |    1 |
a    
|

| +------+-------+

|                          
COMMIT;

|

| SELECT * FROM t_bitfly;

| +------+-------+

| | id   | value |

| +------+-------+

| |    1 |
a    
|

| +------+-------+

v

可以看到,用id<=1加的锁,只锁住了id<=1的范围,可以成功添加id为2的记录,添加id为0的记录时就会等待锁的释放。

MySQL manual里对可重复读里的锁的详细解释:

http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read

For locking reads (SELECT with FOR
UPDATE
 or LOCK
IN SHARE MODE
),UPDATE,
and DELETE statements,
locking depends on whether the statement uses a unique index with a
unique search condition, or a range-type search condition. For a
unique index with a unique search
condition, InnoDB locks
only the index record found, not the gap before it. For other
search conditions, InnoDB locks
the index range scanned, using gap locks or next-key (gap plus
index-record) locks to block insertions by other sessions into the
gaps covered by the range.

------

一致性读和提交读,先看实验,实验四:

t Session
A                     
Session B

|

| START
TRANSACTION;            
START TRANSACTION;

|

| SELECT * FROM t_bitfly;

| +----+-------+

| | id | value |

| +----+-------+

| |  1 |
a    
|

| +----+-------+

|                               
INSERT INTO t_bitfly

|                               
VALUES (2, 'b');

|                               
COMMIT;

|

| SELECT * FROM t_bitfly;

| +----+-------+

| | id | value |

| +----+-------+

| |  1 |
a    
|

| +----+-------+

|

| SELECT * FROM t_bitfly LOCK IN SHARE MODE;

| +----+-------+

| | id | value |

| +----+-------+

| |  1 |
a    
|

| |  2 |
b    
|

| +----+-------+

|

| SELECT * FROM t_bitfly FOR UPDATE;

| +----+-------+

| | id | value |

| +----+-------+

| |  1 |
a    
|

| |  2 |
b    
|

| +----+-------+

|

| SELECT * FROM t_bitfly;

| +----+-------+

| | id | value |

| +----+-------+

| |  1 |
a    
|

| +----+-------+

v

如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。

本身,可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。


可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据。

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

If you want to see the “freshest” state of the database, you should
use either the READ COMMITTED isolation level or a locking
read:

SELECT * FROM t_bitfly LOCK IN SHARE MODE;

结论:MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key
locks。

结论:mysql 的重复读解决了幻读的现象,但是需要 加上 select for update/lock in share mode 变成当前读避免幻读,普通读select存在幻读

Mysql加锁过程详解(3)-关于mysql 幻读理解的更多相关文章

  1. Mysql加锁过程详解(8)-理解innodb的锁(record,gap,Next-Key lock)

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  2. Mysql加锁过程详解(9)-innodb下的记录锁,间隙锁,next-key锁

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  3. Mysql加锁过程详解(1)-基本知识

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  4. Mysql加锁过程详解(2)-关于mysql 幻读理解

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  5. Mysql加锁过程详解(4)-select for update/lock in share mode 对事务并发性影响

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  6. Mysql加锁过程详解(5)-innodb 多版本并发控制原理详解

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  7. Mysql加锁过程详解(6)-数据库隔离级别(1)

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  8. Mysql加锁过程详解(6)-数据库隔离级别(2)-通过例子理解事务的4种隔离级别

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  9. Mysql加锁过程详解(7)-初步理解MySQL的gap锁

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

随机推荐

  1. Spring RestTemplate get post 请求 携带 headers

    RestTemplate 1.我用RestTemplate请求时 我把他注入到容器里  这样可以 什么用什么时候拿 2.也可以new出来 不过我不喜欢 所以就没有用new的 下面我自己的方法   先注 ...

  2. 工程无法正常调试运行unknown failure at android.os.Binder.execTransact

    同事正常使用的工程,放到另电脑上,开后可以正常编译,但是无法安装调试到手机上,始终提示错误 新建一个工程正常. 最后通过把开发工具升级到最新版本解决.

  3. 数据结构C语言顺序表

    #include <stdio.h> #include <stdlib.h> typedef int EmenType; typedef struct Node { int d ...

  4. shell传递参数

    简单介绍python的脚本传参 我们知道python脚本传递参数,有一个很方便的方式-sys.argv.它将脚本本身名字和后面的各项参数都放入一个列表. 使用的时候,索引这个列表就可以了.例如: py ...

  5. restful状态码常用

    在进行后端接口API封装的过程中,需要考虑各种错误信息的输出.一般情况下,根据相应问题输出适合的HTTP状态码,可以方便前端快速定位错误,减少沟通成本. HTTP状态码有很多,每个都有对应的含义,下面 ...

  6. azure cosmos db (mongo DB)

    使用.net mongo的操作类操作azure(微软云)cosmosdb时,发现在做delete的操作的时候可以传一个文档对象,但是最后这个文档会解析成具体的sql语句,而当文档特别大时这样就出先了转 ...

  7. entity framework 上下文对象跟踪相关

    entity framework 上下文对于对象的跟踪有2中方式进行控制,第一种从数据库查询但不加载到上下文. 这里可以用到.AsNoTracing()方法. 这里用到的是实体(entity)在上下文 ...

  8. SpringBoot 基础01

    SpringBoot 基础 pom.xml <!-- Spring Boot 依赖版本控制 --> <parent> <groupId>org.springfram ...

  9. RabbitMQ Routing 消息路由

    上篇文章中,我们构建了一个简单的日志系统.接下来,我们将丰富它:能够使用不同的severity来监听不同等级的log.比如我们希望只有error的log才保存到磁盘上. 1. Bindings绑定 上 ...

  10. 录音--获取语音流(pyAudio)

    这是学习时的笔记,包含相关资料链接,有的当时没有细看,记录下来在需要的时候回顾. 有些较混乱的部分,后续会再更新. 欢迎感兴趣的小伙伴一起讨论,跪求大神指点~ 录音-语音流(pyAudio) tags ...