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. jqGrid笔记@简单实现

    jqGrid在MVC中的版本是已经通过 HtmlHelper 的扩展方法封装后的产物,webForm版本貌似也将其封装成了服务器端空间,所以我推荐下载原生的jqGrid版本的地址为:http://ww ...

  2. 技术不牛如何才拿到国内IT巨头的Offer(转)

    不久前,byvoid面阿里星计划的面试结果截图泄漏,引起无数IT屌丝的羡慕敬仰.看看这些牛人,NOI金牌,开源社区名人,三年级开始写Basic...在跪拜之余我们不禁要想,和这些牛人比,作为绝大部分技 ...

  3. Ubuntu中查看硬盘分区UUID的方法(所有Linux目录的解释)

    在Ubuntu中UUID的两种获取方法,至于UUID是什么,你可以大概理解为分区的标识符,像条形码那样. 在终端中输入下面的命令就可心查看到分区UUID了.命令1.sudo blkid 命令2.ls ...

  4. Android播放音乐时跳动的屏谱demo

    Android实现播放音频时的跳动频谱,并实现可以调节的均衡器. Main.java package com.wjq.audiofx; import android.app.Activity; imp ...

  5. 增强SEO的div+css命名规则

    页头:header登录条:loginBar标志:logo侧栏:sideBar广告:banner导航:nav子导航:subNav菜单:menu子菜单:subMenu搜索:search滚动:scroll页 ...

  6. 移动端 前端框架 amaze ui

    移动端 前端框架 amaze ui http://amazeui.org/?_ver=2.x

  7. Godiva_百度百科

    Godiva_百度百科 北京 三里屯 北京市朝阳区三里屯路19号院10号楼一层S10-13单元及二层S10-22单元 100027 北京朝阳大悦城北京市朝阳区朝阳北路101号朝阳大悦城1号商业楼1F- ...

  8. SRM 583 Div Level Two:IDNumberVerification

    题目来源:http://community.topcoder.com/stat?c=problem_statement&pm=12610 这道题比较有意思,估计是中国人出的吧,以前都不知道身份 ...

  9. js之checkbox的代码全选/全不选,使用id获取元素,而不是name

    每当有多个选项的时候,都会有一种想法是:全选,全不选,如果子选项有被选,父选项也得被选. 注意:这里是根据id来获取元素的,但是不能直接用getElementById,因为那只能返回一个,而不是集合. ...

  10. 找工作笔试面试那些事儿(16)---linux相关知识点(1)

    linux这部分的知识倒不是笔试面试必考的内容,不过现在很多公司开发环境都在linux系统下,一些简单的知识还是需要了解一下的,笔试面试中万一碰到了,也不会措手不及.作为菜硕的我,又因为读研期间的项目 ...