Mysql事务隔离级别以及不同隔离级别的并发事务问题

一、事务四大特性(ACID)

1、原子性(Atomicity)

     事务开始后所有操作,要么全部做完,要么全部不做。事务是一个不可分割的整体。事务在执行过程中出错,会回滚到事务开始之前的状态,以此来保证事务的完整性。

2、一致性(Consistency)

    事务在开始和结束后,能保证数据库完整性约束的正确性即数据的完整性。转账为例,A向B转账,我们必须保证A扣了钱,B一定能收到钱。

3、隔离性(Isolation)

     事务之间的完全隔离,两个事务互不影响。如A向一张银行卡转账,避免在同一时间过多的操作导致账户金额的缺损,所以在A转入结束之前是不允许其他事务对此卡操作。

4、持久性(Durability)

     事务的对数据的修改是永久性的。通俗的解释为事务完成后,对数据的操作都要进行落盘(持久化)。事务一旦完成就是不可逆的,数据库表现为事务一旦完成就无法回滚。

二、并发导致的事务问题

1、脏读

      一个事务读取另外一个事务还没有提交的数据叫脏读。

     例如:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

扫描二维码关注公众号,回复: 6339259 查看本文章

2、不可重复读

      同一个事务中,多次读出的同一数据是不一致的。

      例如:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。

3、幻读

      同一个事务中,按照同一条件读取出的数据量(也就是条数)不一致。

     例如:事务 A 多次读取满足条件(比如age>=10)的数据,事务 B 在事务A多次读取的过程中,对数据作了新增或删除,导致事务A多次读取同一条件的数据时,数据量不一致。

     这儿讲一下不可重复读和幻读的区别:

  • 不可重复读:主要是说多次读取一条记录, 发现该记录中某些列值被修改过。
  • 幻读:主要是说多次读取一个范围内的记录(包括直接查询所有记录结果或者做聚合统计), 发现结果不一致(标准档案一般指记录增多, 记录的减少应该也算是幻读)

     另外需要提一句,mysql的innodb的mvcc已经帮我们解决了幻读的问题,后续我在给大家详细解释下mvcc。

三、不同隔离级别会产生的事物问题

 mysql默认隔离级别为:可重复读(repeatable-read)

隔离级别 脏读        不可重复度 幻读     
读未提交(read-uncommitted)
读已提交(read-committed)
可重复读(repeatable-read)
可串行化(serializable)

四、隔离级别详细举例

1、读未提交(read-uncommitted)-- 脏读

#首先,修改隔离级别
set tx_isolation='READ-UNCOMMITTED';
select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+

#事务A:启动一个事务
start transaction;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事务B:也启动一个事务(那么两个事务交叉了)
       在事务B中执行更新语句,且不提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事务A:那么这时候事务A能看到这个更新了的数据吗?
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |   --->可以看到!说明我们读到了事务B还没有提交的数据
|    2 |    2 |
|    3 |    3 |
+------+------+

#事务B:事务B回滚,仍然未提交
rollback;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事务A:在事务A里面看到的也是B没有提交的数据
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |      --->脏读意味着我在这个事务中(A中),事务B虽然没有提交,但它任何一条数据变化,我都可以看到!
|    2 |    2 |
|    3 |    3 |
+------+------+

2、读已提交(read-committed)-- 不可重复读

#首先修改隔离级别
set tx_isolation='read-committed';
select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

#事务A:启动一个事务
start transaction;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事务B:也启动一个事务(那么两个事务交叉了)
       在这事务中更新数据,且未提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事务A:这个时候我们在事务A中能看到数据的变化吗?
select * from tx;
+------+------+                
| id   | num  |                
+------+------+                
|    1 |    1 |--->并不能看到!  
|    2 |    2 |                
|    3 |    3 |                
+------+------+                
                               
#事务B:如果提交了事务B呢?         
commit;                        
                               
#事务A:                         
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |----------------->相同的select语句,两次查询结果不一样
|    2 |    2 |
|    3 |    3 |
+------+------+

3、可重复读repeatable-read)-- 幻读

#首先,更改隔离级别
set tx_isolation='repeatable-read'; select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:开启一个新事务(那么这两个事务交叉了)
       在事务B中更新数据,并提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ commit; #事务A:这时候即使事务B已经提交了,但A能不能看到数据变化? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->还是看不到的!(这个级别2不一样,也说明级别3解决了不可重复读问题) | 2 | 2 | | 3 | 3 | +------+------+ #事务A:只有当事务A也提交了,它才能够看到数据变化 commit; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+

4、可串行化(serializable)

 #首先修改隔离界别
set tx_isolation='serializable';
select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+

#事务A:开启一个新事务
start transaction;

#事务B:在A没有commit之前,这个交叉事务是不能更改数据的
start transaction;
insert tx values('4','4');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
update tx set num=10 where id=1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

参考文章

    https://www.cnblogs.com/snsdzjlz320/p/5761387.html

猜你喜欢

转载自www.cnblogs.com/kukudexin/p/10965646.html