一个幻读模型引出的记录可见性判断

Ⅰ、看一个幻读的模型

表a中有4条记录1,3,5,7,开两个session

session1:begin;    delete <= 7 ;
session2:begin;    insert a values(2);    commit;
session1:commit;

这个模型在rc的情况下,这是没问题的,只是加记录锁,不会锁范围,插入2是可以的。最后a上就剩2这个记录

那binlog里面记录的内容就有讲究了,假设是statment格式的binlog

insert 2;
delete <= 7;

这时候数据库做同步,从库上就gg了,一个事务所做的修改对另一个事务不可见,好似串行

这就是不符合隔离性的要求,并行执行和串行执行的结果不一样

row格式的binlog是下面这样记录的:

insert 2;
delete 1,delete 3, delete 5, delete 7

这样2就还在

所以,一定要用row,row记录的是一条一条记录,而不是简单的sql.所以说rc的情况下设置为row,主从还是可以保持一致的

5.1版本才支持row,主从复制从3.23就开始支持,中间差了4.0,4.1,5.0三个版本,最早为什么innodb要支持这样的锁,也有部分原因是当时的MySQL的复制不用Next-key Lock,就是根本不可用的,因为innodb可以并行的,有并发问题,不像其他存储引擎有表锁

tips:

  • binlog_format这个参数5.7之前默认statement,之后默认row
  • 其实不用我们担心,在事务隔离级别为rc的情况下,binlog_format随便你怎么设MySQL都会给你记为row的,不信可以试试

Ⅱ、如何判断一条记录的可见性

先讲两句题外话,这个判断的是根据事务id来做的,每条记录有rowid、txid、rowpointer,后面才接用户的列

txid是6个字节的,其实每个事务分配的,而且是全局自增的,在共享表空间的某个位置存放着当前max的txid,所以每开启一个事务都会分配一个txid

2.1 具体怎么判断?

①当前活跃事务列表,里面记录着当前正在执行未提交的事务

begin;
xxx    随便什么语句

这时候产生一个read_view的内存对象,现在在mysql里面完全看不到,能看到的就是下面这个,有多少个read_view开着,我们通过这个read_view来判断记录的可见性

--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=23137, Main thread ID=139830599059200, state: sleeping
Number of rows inserted 116, updated 27, deleted 0, read 130
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s

这个read_view就是把事务开始的时候事务活跃列表拷贝一份出来,这时候会拿到很多个事务id,只要你的记录对应的事务id在这个活跃列表中,意味着这个记录不可见,因为这条记录在select这个事务开启的时候还没提交,所以该记录的事务id对新产生的事务是不可见的

②read_view在rc和rr中的区别

rr中read_view只产生一次,rc中每执行一条sql语句就会创建一个read_view

原因:rc可以读到已经提交的事务的记录,第二次执行还要检查事务活跃列表,如果提交了这条记录就可见,而rr实现了可重复读

如果一个事务执行时间很长,它插入了一条记录,是否可见?就看创建read_view的时候这个tx_id有没有在活跃列表中,如果不在就意味着不可见,那就不会删掉了。

rr的好处,read_view只创建一次,rc要创建n次

如果事务活跃列表很长的话,每次拷贝的时候要锁住所有活跃事务(latch,5.5版本叫kernel_mutex,这个锁很大),需要时间还是挺长的,但5.6,5.7都开始做优化了(把大锁拆分了)

所以,一个事务中有很多条操作,而且全是select操作(随机主键值查询),这时候rr性能更好,没有insert所以不会有gap锁导致的并发插入影响,这种情况太少了,所以我们还是选择rc

2.2 read_view什么时候分配?

begin的时候还是执行第一条sql的时候?

测一把便知

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> desc l;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | NO   | PRI | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

mysql> select * from l;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)

测试一:
session1:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

session2:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into l values (4);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from l;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
| 4 |
+---+
4 rows in set (0.00 sec)

session1:
mysql> select * from l;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
| 4 |
+---+
4 rows in set (0.00 sec)

测试二:
session1:
mysql>
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from l;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)

session2:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into l values (4);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from l;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
| 4 |
+---+
4 rows in set (0.00 sec)

session1:
mysql> select * from l;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)

按道理如果事务隔离级别为rr,那一个事务提交了,对另一个事务不可见,解决不可重复读,这样看测试二是合理的,

那为什么,测试二session1一开始select了一把,session2里面事务提交了,session1就不可见,而测试一session1一开始没有select,后面再select就可见了?这是重复读的体现吗?

原因:rr隔离级别的时,事务中有select时会创建一个read_view,而且一个事务只创建一次,所以测试一的时候,最后session1 select的时候才创建了read_view,发现session2的事务中相关记录已经commit了,不在事务活跃列表中,所以读到了这条记录,而测试二,session1 开启事务,第一个select的时候就创建了read_view,这时候session2里面的事务还没开启,第二个select的时候用的还是原来的rv,这样就不可见了

tips:

如果希望begin的时候就创建read_view

必须用start transaction with consistent snapshot; 结合rr用,因为rc时候,事务中每执行一个sql就会创建read_view

session1:
start transaction with consistent snapshot;    创建了rv

session2:
begin;
insert aaa;
commit;

session1:
select aaa;查不到,创建rv的时候,session2中的事务还不存在

如果rc的话能读到aaa,因为第三步session1里执行select又会创建一个rv,会发现aaa这个记录已经提交了,就能看到了

原文地址:https://www.cnblogs.com/---wunian/p/9181095.html

时间: 2024-10-08 10:19:43

一个幻读模型引出的记录可见性判断的相关文章

何为脏读、不可重复读、幻读

2.0 前言 事务的隔离性是指多个事务并发执行的时候相互之间不受到彼此的干扰的特性,隔离性是事务ACID特性中的I,根据隔离程度从低到高分为Read Uncommitted(读未提交),Read Committed(读已提交),Repeatable Read(可重复读),Serializable(串行化)四种隔离级别.在具体介绍事务隔离性前先介绍几个名词,以便说明数据库在并发操作时候可能存在的问题,以便展开来探讨这四种隔离级别对应存在哪些问题,哪些隔离级别解决了哪些问题. 2.1 何为脏读.不可

幻读是什么?幻读有什么问题

一:CREATE TABLE `t` (  `id` int(11) NOT NULL,  `c` int(11) DEFAULT NULL,  `d` int(11) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `c` (`c`)) ENGINE=InnoDB; insert into t values(0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25); begin;select * fro

20 幻读是什么,幻读有什么问题?

例子: CREATE TABLE `t20` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, `d` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `c` (`c`) ) ENGINE=InnoDB; insert into t20 values(0,0,0),(5,5,5), (10,10,10),(15,15,15),(20,20,20),(25,25,25); select * from t

Mysql事务,并发问题,锁机制-- 幻读、不可重复读(转)

1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约束没有被破坏 持久性:事务的提交结果,将持久保存在数据库中 2.事务并发会产生什么问题 1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了. 例如: 张三的工资为5000,事务A中获取工资为5000,事务B获取工资为5

Next-key locking是如何解决幻读问题的

Next-key locking是如何解决幻读问题的 首先什么是幻读呢? 举个例子,两个男孩同时在追求一个女生的故事 A问:你有男朋友吗?女孩对他说没有.A追求女孩的事件还没有提交,就是继续追求哈. 就在A追求的同时,B也在追求,并且直接让女孩做他的女朋友,女孩答应了,B的追求事件结束. A又问:你有男朋友吗? 女孩对他说我已经有男朋友了! 呜呜呜 !刚才你还没有的,怎么现在就有了呢? 女孩说,你也没说过你追我的时候不让别人追我啊!... ... A哭着走了. 幻读 Phantom Proble

关于脏读、幻读和不可重复读

1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据. e.g.         1.Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)         2.Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!         3.而财务发现操作有误,回滚了事务,Mary的工资又变为了1000           像这样,Mary记取的工资数800

脏读 幻读 不可重复读

转自:http://www.blogjava.net/hitlang/archive/2009/04/13/265256.html 1, 脏读 一个事务读到另一个事务,尚未提交的修改,就是脏读.这里所谓的修改,除了Update操作,不要忘了,还包括Insert和Delete操作. 脏读的后果:如果后一个事务回滚,那么它所做的修改,统统都会被撤销.前一个事务读到的数据,就是垃圾数据. 举个例子:预订房间.有一张Reservation表,往表中插入一条记录,来订购一个房间. 事务1:在Reserva

脏读、不可重复读、幻读

1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据. 2. 不可重复读 :是指在一个事务内,多次读同一数据.在这个事务还没有结束时,另外一个事务也访问该同一数据.那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的.这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读.例如,一个编辑人员两次读取同一文档,但在两次读取之间,作

数据库事务隔离级别-- 脏读、幻读、不可重复读

一.数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted .Read committed .Repeatable read .Serializable ,这四个级别可以逐个解决脏读 .不可重复读 .幻读 这几类问题. √: 可能出现    ×: 不会出现   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 注意