14.3.4 Phantom Rows 幻影行
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 幻影行的更多相关文章
- 14.5.4 Phantom Rows 幻影行
14.5.4 Phantom Rows 幻影行 所谓的幻读问题发生在一个事务 当相同的查询产生不同的结果集在不同的时间. 例如,如果一个SELECT 是执行2次,但是第2次返回的时间不第一次返回不同, ...
- InnoDB 存储引擎的锁机制
测试环境隔离级别:REPEATABLE-READ 行级别的 - Share and Exclusive Locks 共享锁 S:允许持有S锁的事务对行进行读操作 排他锁 X: 允许持有X锁的事务对行进 ...
- 【原创】新说Mysql事务隔离级别
引言 大家在面试中一定碰到过 说说事务的隔离级别吧? 老实说,事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!因为他们对可重复读 ...
- [51CTO]新说MySQL事务隔离级别!
新说MySQL事务隔离级别! 事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!本文所讲大部分内容,皆有官网作为佐证,因此对本文内 ...
- 【转】新说Mysql事务隔离级别
作者:孤独烟 转自:https://www.cnblogs.com/rjzheng/p/9955395.html 引言 大家在面试中一定碰到过 说说事务的隔离级别吧? 老实说,事务隔离级别这个问题,无 ...
- Shared and Exclusive Locks 共享和排它锁
14.5 InnoDB Locking and Transaction Model InnoDB 锁和事务模型 14.5.1 InnoDB Locking 14.5.2 InnoDB Transact ...
- mysql 锁2
官网地址 https://dev.mysql.com/doc/refman/5.5/en/innodb-transaction-isolation-levels.html 这里主要是说事务隔离级别,以 ...
- [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变成默认 ...
- Mysql事务及行级锁的理解
在最近的开发中,碰到一个需求签到,每个用户每天只能签到一次,那么怎么去判断某个用户当天是否签到呢?因为当属表设计的时候,每个用户签到一次,即向表中插入一条记录,根据记录的数量和时间来判断用户当天是否签 ...
随机推荐
- 视频播放-SurfaceView
1.视图 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:too ...
- SQL 根据时间和打印状态抽取记录
1.首先要是没有打印的记录. 2.其次是要按照时间,时间是要按照倒序排列. 下载文件的URL and order by 上传时间 desc *是否打印,使用字段bit,0是没有打印,1是已经打印
- Ch05 视图模型
5.1 什么是视图模型 5.1.1 在线商店示例 5.1.2 建立视图模型 5.1.3 交付表现模型 5.1.4 ViewData.Model 5.2 表现用户输入 5.2.1 设计(输 ...
- Vim的常用命令笔记
[简介] Vim是vi编辑器的改进版.vi是类UNIX系统里最常用的编辑器. [模式] 在shell中直接打vim filename就会进入普通模式.按i键进入编辑模式,此时Vim底部显示INSERT ...
- 用WebCollector制作一个爬取《知乎》并进行问题精准抽取的爬虫(JAVA)
简单介绍: WebCollector是一个无须配置.便于二次开发的JAVA爬虫框架(内核),它提供精简的的API.仅仅需少量代码就可以实现一个功能强大的爬虫. 怎样将WebCollector导入项目请 ...
- Delphi中多标签页面的实现
unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...
- jquery 提交数据
目录 jquery ajax的应用 1 表单提交 2 ajax的提交(ajax post get) 普通的表单提交 <%@ page language="java" impo ...
- 树后台数据存储(採用webmethod)
树后台数据存储 关于后台数据存储将集中在此篇解说 /* *作者:方浩然 *日期:2015-05-26 *版本号:1.0 */ using System; using System.Collection ...
- EXT2/EXT3文件系统(一)
整理自<鸟哥的Linux私房菜>,整理者:华科小涛http://www.cnblogs.com/hust-ghtao/ 1.文件系统概念引入 文件系统是一种存储和组织计算机数据的方法,它使 ...
- Entity FramWork - 在VS里面直接创建表,并同步到数据库
前面具体添加什么直接看: 1.Entity - 使用EF框架进行增删改查 - 模型先行 2.Entity - 使用EF框架进行增删改查 - 数据库先行 然后: 然后右键,可以添加[实体],也就是表.之 ...