1、知识点;
事务的四大特性 ACID ;
原子性(Atomic):事务是一个整体(无论在该事务中操作任何CRUD),要不全部执行,要不全部不执行。(数据库能够进行操作的最小的逻辑单元)
一致性(Consistent):组成一个事务的操作是CRUD,要么全部成功,要么全部失败(类似原子性的后续补充)。
隔离性(Insulation):事务之间的隔离(比如:AB两个事物,并发时,A事务在读取数据时,仅能读取B事务操作之间的数据,或是B事务操作之后的数据,无法读取操作中的数据)
持久性(Duration):事务完成后对数据的修改时永久的。
事务的四大隔离级别;
读未提交(read uncommitted):一个事务可以读到另一个事务未提交的结果(所有的问题均会出现(脏读、不可重复读、幻读))
读提交(read committed ):只有一个事务被提交后,其更新结果才能被其他事务读取到(出现问题(不可重复读、幻读),解决(脏读))
可重复读(repeatable read):在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。(解决(脏读、不可重复读、幻读)) (mysql默认级别)
序列化(serializable):事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
不同隔离级别可能导致的问题:脏读、不可重复读,幻读
并发问题汇总:
脏读:一个事务读取到另一个事务未提交的数据。
不可重复读:一个事务在不同时刻读取到不同的数据。
幻读:一个事务在读取数据后,(另一个事务执行添加操作后)。由于不可重复读的问题解决,所以该事务读取不到新添加的数据,所以在添加同样主键的数据时会报错,
2、实例数据及测试
备注:建议开始前先将会话的自动提交关闭:
命令:$>set session autocommit = 0; -- 关闭会话事务的自动提交
$>show session variables like "autocommited" ; -- 查看事务提交自动化状态。
1>读未提交:最低的隔离界别,三大问题均存在(脏读、不可重复读、幻读)
实现简述:A会话 (repeatable read )、B会话(read uncommitted) 。A会话中创建事务,然后修改数据,但不提交,B会话可以读到A未提交的数据: 以下按操作顺序依次执行。
A会话:
查看A的会话界别
使用数据库
开启事务
修改数据,但并未提交
B会话(A不关闭,重新启动一个会话);
查看当前隔离界别
不修改隔离界别,发现数据并未读取到未提交的数据。
设置隔离界别为读未提交
读取到未提交的数据。
2>读提交(存在问题:不可重复读)
实现简述:在1的基础上,修改B会话的隔离界别为read commited ,发现脏读会被解决(及读取不到A会话未提交的数据)。
B会话:
设置隔离界别为读已提交。脏读被解决。
问题:可重复读。
问题描述:A会话中,在B会话对数据进行第一次查询后,对该数据进行修改并提交 。B会话再对该数据进行查询,发现第二次和
第一次查询出来的数据不一致。
B会话:
设置隔离界别为读提交,开启事务,第一次查询数据。
A会话
查询数据、开启事务。修改数据。
提交数据。
查看数据已修改。
B会话
两次查询数据不一致。
3>可重复读
问题解决描述:在2的基础上,设置B会话的隔离界别为repeatable read,发现B会话再第一二次读取的数据一致。
B会话:
设置隔离界别为可重复读
A会话
修改数据
B会话
发现在同一个事物中两次读取数据一致
提交事务后,读取到修改后的数据。
问题描述:幻读,A会话,查询数据时,发现主键id的数值到1,然后B开启事务,添加一个主键id为2的数据,之后提交数据。然后A会话,提交主键是2的数据,发现报(主键id为2的数据已存在)。但A缺没读取到,出现了幻读。
A会话
B会话
创建数据
A会话
第二次查询数据(不可重复读)后,添加报错,已存在 A会话出现幻读。
4>序列化(串行化(悲观锁))(锁行) (读占写锁)
问题描述:设置A会话为串行化(serializable),然后创建事务,查询users表后,在B会话中创建事务,往users表中添加数据发现被塞住了,只要A会话中的事务提交了,B会话才可添加成功(同样,A创建事务后(不查询users表),B创建事务后,添加数据到users表中(可成功,不提交事务,然后A会话查询users表,发现被塞住了,只有B会话的事务提交后,才可提交成功!)。避免了幻读,但很影响性能、
A会话
设置事务为串行化
创建事务查询users表
B会话
创建事务,可查询users表,但添加数据时被塞住
A会话提交数据
B会话中的添加操作执行(由于本机操作时间慢了,所以超时,速度快点会执行,然后B会话提交事务即可)。