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. 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .

    双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后 ...

  2. mysql备份数据库几种方法

    方法一 cmd 到mysql bin文件夹下用 例如以下命令 mysqldump --opt -h192.168.0.156 -uusername -ppassword --skip-lock-tab ...

  3. c语言,数组和字符串

    1. “数组名代表了数组的存储首地址,是一个地址常量”. 对于char *p1 = "A String."; 和 char p2[] = "Another String. ...

  4. Blog 转移

    Blog 转移至 http://blog.rapcoder.com CSDN :  http://blog.csdn.net/a542551042 欢迎一起交流 学习 ,谢谢!!!

  5. Java 异常解决之java.lang.IllegalArgumentException: Comparison method violates its general contract!

    Java 异常解决 在你的代码前加一句 System.setProperty("java.util.Arrays.useLegacyMergeSort", "true&q ...

  6. Eclipse用法和技巧六:自动生成get和set方法1

    java的类中,除了常量声明为静态且公有的,一般的对象数据作用域,都是声明为私有的.这样做能保护对象的属性不会被随意改变,调试的时候也会方便很多:在类的公有方法中大一个调用栈就能看到哪里改了属性值.声 ...

  7. xhr的send方法以及node如何处理get和post数据

    起因:看了阮一峰老师的关于上传文件的文章,进行测试,在使用xhr对象的send方法时遇到问题. 遇到的问题是使用send方法传送过去的数据,在node后台无法接收,经过很多次测试,怀疑是不是send与 ...

  8. codility上的问题(18) Rho 2012

    从正整数1开始,产生一个数列,数列中的每个数是之前出现过的任意两个数的和(可以相等),问产生正整数A,需要的数列长度至少是多少?返回这样一个最短的序列. 例如A=42 可以这样[1, 2, 3, 6, ...

  9. tar打包过滤某个文件及文件夹

    ip=ip add|grep eth0|grep -i inet|awk '{print $2}'|cut -d '/' -f 1 cd /data tar -zvcf `echo $ip`_`dat ...

  10. 简单的javascript抽奖程序

    <html>  <head>   <title>手机号码抽奖程序</title>   <script>    //声明一个数组装住号码,可根 ...