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. 视频播放-SurfaceView

    1.视图 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:too ...

  2. SQL 根据时间和打印状态抽取记录

    1.首先要是没有打印的记录. 2.其次是要按照时间,时间是要按照倒序排列. 下载文件的URL and order by 上传时间 desc *是否打印,使用字段bit,0是没有打印,1是已经打印

  3. Ch05 视图模型

    5.1  什么是视图模型 5.1.1  在线商店示例 5.1.2  建立视图模型 5.1.3  交付表现模型 5.1.4  ViewData.Model 5.2  表现用户输入 5.2.1  设计(输 ...

  4. Vim的常用命令笔记

    [简介] Vim是vi编辑器的改进版.vi是类UNIX系统里最常用的编辑器. [模式] 在shell中直接打vim filename就会进入普通模式.按i键进入编辑模式,此时Vim底部显示INSERT ...

  5. 用WebCollector制作一个爬取《知乎》并进行问题精准抽取的爬虫(JAVA)

    简单介绍: WebCollector是一个无须配置.便于二次开发的JAVA爬虫框架(内核),它提供精简的的API.仅仅需少量代码就可以实现一个功能强大的爬虫. 怎样将WebCollector导入项目请 ...

  6. Delphi中多标签页面的实现

    unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  7. jquery 提交数据

    目录 jquery ajax的应用 1 表单提交 2 ajax的提交(ajax post get) 普通的表单提交 <%@ page language="java" impo ...

  8. 树后台数据存储(採用webmethod)

    树后台数据存储 关于后台数据存储将集中在此篇解说 /* *作者:方浩然 *日期:2015-05-26 *版本号:1.0 */ using System; using System.Collection ...

  9. EXT2/EXT3文件系统(一)

    整理自<鸟哥的Linux私房菜>,整理者:华科小涛http://www.cnblogs.com/hust-ghtao/ 1.文件系统概念引入 文件系统是一种存储和组织计算机数据的方法,它使 ...

  10. Entity FramWork - 在VS里面直接创建表,并同步到数据库

    前面具体添加什么直接看: 1.Entity - 使用EF框架进行增删改查 - 模型先行 2.Entity - 使用EF框架进行增删改查 - 数据库先行 然后: 然后右键,可以添加[实体],也就是表.之 ...