14.3.4 Phantom Rows   幻影行

所谓的幻读为发生在一个事务 当相同的查询产生不同的结果集在不同的时间。

比如,如果一个SELECT被执行2次, 但是第2次返回的记录不是第一次返回的记录,

行是幻行

假设在child 表的id列上有一个索引 ,你需要读取和锁定所有的行 值大于100的,

以便更新选择行的一些列:

SELECT * FROM child WHERE id > 100 FOR UPDATE;

查询扫描索引从第一个记录id大于100开始, 让表包含记录90和102.

如果锁设置在index records 在扫描的范围 不堵塞插入 在区间上(在这个例子里,区间是90和102)

另外一个会话可以插入新值到表 id值为101.

如果你执行相同的查询 在相同的session,你会看到新的值id=101(一个幻读)

在查询返回的结果。 如果我们将一组行作为数据项, 新的幻读的child 会违背事务隔离原理,

一个事务应该在事务期间所读取的数据不会改变。

mysql> show variables like '%tx_isolation%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.01 sec) CREATE TABLE `child` (
`sn` int(11) NOT NULL AUTO_INCREMENT,
`id` int(11) DEFAULT NULL,
`info` varchar(40) DEFAULT NULL,
PRIMARY KEY (`sn`)
); mysql> show variables like '%tx_isolation%';
+---------------+----------------+
| Variable_name | Value |
+---------------+----------------+
| tx_isolation | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec) Session 1:
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
Empty set (0.00 sec) Session 2:
mysql> select * from child;
+----+------+------+
| sn | id | info |
+----+------+------+
| 1 | 1 | a1 |
| 2 | 99 | a99 |
+----+------+------+
2 rows in set (0.00 sec) mysql> insert into child(id,info) values(101,'a101');
Query OK, 1 row affected (0.00 sec) mysql> commit;
Query OK, 0 rows affected (0.01 sec) 继续测试: Session 1:
mysql> show variables like '%tx_isolation%';
+---------------+----------------+
| Variable_name | Value |
+---------------+----------------+
| tx_isolation | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec) mysql> SELECT * FROM child;
+----+------+------+
| sn | id | info |
+----+------+------+
| 1 | 1 | a1 |
| 2 | 99 | a99 |
| 3 | 101 | a101 |
+----+------+------+
3 rows in set (0.00 sec) mysql> insert into child(id,info) values(110,'a110');
Query OK, 1 row affected (0.00 sec) mysql> commit;
Query OK, 0 rows affected (0.01 sec) mysql> select * from child;
+----+------+------+
| sn | id | info |
+----+------+------+
| 1 | 1 | a1 |
| 2 | 99 | a99 |
| 3 | 101 | a101 |
| 4 | 110 | a110 |
+----+------+------+
4 rows in set (0.00 sec) mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+----+------+------+
| sn | id | info |
+----+------+------+
| 3 | 101 | a101 |
| 4 | 110 | a110 |
+----+------+------+
2 rows in set (0.00 sec) Session 2: mysql> select * from child; +----+------+------+
| sn | id | info |
+----+------+------+
| 1 | 1 | a1 |
| 2 | 99 | a99 |
| 3 | 101 | a101 |
| 4 | 110 | a110 |
+----+------+------+
4 rows in set (0.00 sec) mysql> insert into child (id,info) values(105,'a105');
Query OK, 1 row affected (0.00 sec) mysql> commit;
Query OK, 0 rows affected (0.01 sec) mysql> select * from child;
+----+------+------+
| sn | id | info |
+----+------+------+
| 1 | 1 | a1 |
| 2 | 99 | a99 |
| 3 | 101 | a101 |
| 4 | 110 | a110 |
| 5 | 105 | a105 |
+----+------+------+
5 rows in set (0.00 sec) 为了防止幻读,InnoDB使用一个算法叫做 next-key locking 由 index-row locking和gap locking 组成。 InnoDB 执行 row-level locking 以这种方式 当它搜索或者扫描表的索引, 它设置共享锁或者排它锁 在它遇到的index records上. 因此, row-level locks 实际上是 index-record locks. 此外, a next-key lock 在一个Index record 也影响了 “gap” before that index record. 也就是说,一个next-key lock 是一个index record lock 加上一个gap lock 在index record 之前的区间。 如果一个会话有一个共享锁或者排它锁在记录R上 在一个index里, 另外的会话不能立即插入到一个新的index record 在一个gap (在记录R之前) 当InnoDB 扫描一个Index, 它也可以lock gap 在最后一条记录后面 在index里, 比如之前的例子,防止任何插入到表 id值大于100的数据, InnoDB设置锁包含一个lock区间 在id=102以后的区间 测试:
Session 1: mysql> show variables like '%tx_isolation%';
+---------------+-----------------+
| Variable_name | Value |
S+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec) Database changed
mysql> select * from child;
+----+------+------+
| sn | id | info |
+----+------+------+
| 1 | 90 | a90 |
| 2 | 102 | a102 |
+----+------+------+
2 rows in set (0.00 sec) mysql> select * from child where id>100 for update;
+----+------+------+
| sn | id | info |
+----+------+------+
| 2 | 102 | a102 |
+----+------+------+
1 row in set (0.00 sec) mysql> show index from child;
+-------+------------+------------+--------------+-------------+-----------+------------- +----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+------------- +----------+--------+------+------------+---------+---------------+
| child | 0 | PRIMARY | 1 | sn | A | 4 | NULL | NULL | | BTREE | | |
| child | 0 | child_idx1 | 1 | id | A | 4 | NULL | NULL | YES | BTREE | | |
+-------+------------+------------+--------------+-------------+-----------+------------- +----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec) Session 2:
mysql> insert into child(id,info) values(91,'a91');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(90,'a90');
ERROR 1062 (23000): Duplicate entry '90' for key 'child_idx1'
mysql> insert into child(id,info) values(89,'a89');
Query OK, 1 row affected (0.00 sec) mysql> rollback;
Query OK, 0 rows affected (0.01 sec) mysql> insert into child(id,info) values(88,'a88');
Query OK, 1 row affected (0.00 sec) mysql> rollback;
Query OK, 0 rows affected (0.00 sec) mysql> insert into child(id,info) values(92,'a92');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(93,'a93');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(94,'a94');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(95,'a95');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(96,'a96');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(97,'a97');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(98,'a98');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(99,'a99');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(100,'a100');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(101,'a101');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(102,'a102');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(103,'a103');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into child(id,info) values(104,'a104'); x∈[3,4]表示3≤x≤4 因为两端有等号,所以叫闭区间
x∈(3,4)表示3<x<4 因为两端没等号,所以叫开区间 锁了(90,无穷) 你可以使用 next-key locking 来实现一个唯一性检查 在你的应用里: 如果你读取你的数据 在共享模式,没有看到重复的 对于一条记录准备被插入, 然后你可以安全的插入你的记录,知道 next-key lock 设置在你的行的继承者 在读取阻止任何 同时插入一个重复的记录。因此,the next-key locking 让你可以lock 你表中不存在的东西

14.3.4 Phantom Rows 幻影行的更多相关文章

  1. 14.5.4 Phantom Rows 幻影行

    14.5.4 Phantom Rows 幻影行 所谓的幻读问题发生在一个事务 当相同的查询产生不同的结果集在不同的时间. 例如,如果一个SELECT 是执行2次,但是第2次返回的时间不第一次返回不同, ...

  2. InnoDB 存储引擎的锁机制

    测试环境隔离级别:REPEATABLE-READ 行级别的 - Share and Exclusive Locks 共享锁 S:允许持有S锁的事务对行进行读操作 排他锁 X: 允许持有X锁的事务对行进 ...

  3. 【原创】新说Mysql事务隔离级别

    引言 大家在面试中一定碰到过 说说事务的隔离级别吧? 老实说,事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!因为他们对可重复读 ...

  4. [51CTO]新说MySQL事务隔离级别!

    新说MySQL事务隔离级别! 事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!本文所讲大部分内容,皆有官网作为佐证,因此对本文内 ...

  5. 【转】新说Mysql事务隔离级别

    作者:孤独烟 转自:https://www.cnblogs.com/rjzheng/p/9955395.html 引言 大家在面试中一定碰到过 说说事务的隔离级别吧? 老实说,事务隔离级别这个问题,无 ...

  6. Shared and Exclusive Locks 共享和排它锁

    14.5 InnoDB Locking and Transaction Model InnoDB 锁和事务模型 14.5.1 InnoDB Locking 14.5.2 InnoDB Transact ...

  7. mysql 锁2

    官网地址 https://dev.mysql.com/doc/refman/5.5/en/innodb-transaction-isolation-levels.html 这里主要是说事务隔离级别,以 ...

  8. [MySQL Reference Manual]14 InnoDB存储引擎

    14 InnoDB存储引擎 14 InnoDB存储引擎 14.1 InnoDB说明 14.1.1 InnoDB作为默认存储引擎 14.1.1.1 存储引擎的趋势 14.1.1.2 InnoDB变成默认 ...

  9. Mysql事务及行级锁的理解

    在最近的开发中,碰到一个需求签到,每个用户每天只能签到一次,那么怎么去判断某个用户当天是否签到呢?因为当属表设计的时候,每个用户签到一次,即向表中插入一条记录,根据记录的数量和时间来判断用户当天是否签 ...

随机推荐

  1. linux下执行sh文件报错:oswatcher_restart.sh: line 13: ./startOSW.sh: Permission denied

    1 查看执行sh文件的内容 [root@xxxdb0402 dbscripts]# more oswatcher_restart.sh  #!/usr/bin/ksh #export oswdir=` ...

  2. java--函数练习

    /* 1,定义一个功能,用于打印矩形 1,定义一个打印九九乘法表功能的函数 */ class FunctionTest { public static void main(String[] args) ...

  3. PHP脚本监控程序

    #!/bin/sh # Find ip IP=`/sbin/ifconfig eth1 | grep 'inet addr' | awk '{ print substr($2, index($2, & ...

  4. 基于visual Studio2013解决面试题之1307二分查找

     题目

  5. 基于visual Studio2013解决面试题之1109全排列

     题目

  6. HDU 4882 ZCC Loves Codefires(贪心)

     ZCC Loves Codefires Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/O ...

  7. HDU 3966 Aragorn's Story(树链剖分)

    HDU Aragorn's Story 题目链接 树抛入门裸题,这题是区间改动单点查询,于是套树状数组就OK了 代码: #include <cstdio> #include <cst ...

  8. 细节!重点!易错点!--面试java基础篇(二)

    今天来给大家分享一下java的重点易错点第二部分,也是各位同学面试需要准备的,欢迎大家交流指正. 1.字符串创建与存储机制:当创建一个字符串时,首先会在常量池中查找是否已经有相同的字符串被定义,其判断 ...

  9. TODO管理工具TaskWarrior (跨平台C++代码)

    Taskwarrior 是一个基于命令行的 TODO 列表管理工具.主要功能包括:标签.彩色表格输出.报表和图形.大量的命令.底层API.多用户文件锁等功能. http://www.oschina.n ...

  10. 从零开始学C++之异常(一):C语言错误处理方法、C++异常处理方法(throw, try, catch)简介

    一.C语言错误处理方法 1.返回值(if … else语句判断错误) 2.errno(linux 系统调用) 3.goto语句(函数内局部跳转) 4.setjmp.longjmp(Do not use ...