4.MySQL--事务隔离
2. 事务的隔离级别
事务的隔离性是多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其他事务的操作所干扰,多个事务之间要相互隔离。
在并发下事务会容易出现一些问题:
- 数据更新丢失:两个事务同时操作一条数据,一个事务因为异常导致数据更新丢失\
- 脏读:一个事务开始读取某一行数据,另外一个事务已经更新了此条数据但是没有及时提交,这是相当危险的,因为所有的操作都被回滚。
- 不可重复读:一个事务对同一条数据读取两次,但是结果却不一样。例如,在读取的过程中,另外的一个事务进行的修改,并提交。
- 幻读:事务在操作过程中进行两次查询,第二次查询的结果在第一次查询中未出现的数据(查询的语句不一定一样),这是因为在读取过程中有另一个事务进行了插入。\
MySQL中存在(Innodb)存在4种隔离级别,不同的隔离级别对事务的处理不同。\
- 未授权读取(未提交读取 Read Uncommitted):如果一个事务已经开始写数据,则另外一个数据则不会允许同时进行写操作,但允许其他事务读此条数据;READ-UNCOMMITTED | 0:存在脏读,不可重复读,幻读的问题。,隔离级别是可以通过“排它锁”实现。
- 授权读取(已读提交 Read Commited)::读取数据的事务允许其他事务继续访问该数据,但是未提交的写事务将会禁止其他事务访问该数;READ-COMMITTED |1:解决脏读、幻读的问题,存在不可重复读、幻读的问题。
- 可重复读取(Repeatable Read):读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他的事务;REPEATABKLE-READ | 2:解决脏读、不可重复读的问题,存在幻读的问题,默认的隔离级别。可以通过 “排他锁“,“共享锁”实现。
- 序列化(Serializable):提供严格的事务隔离,它要求事务序列化执行,事务只能一个挨着一个地执行,不能并发执行;SERIALIZABLE | 3 解决脏读、不可重复读、幻读,它保证事务安全,但完全串行执行,性能最低;如果仅仅通过“行级锁”是无法实现事务的序列化的,必须通过其他的机制保证新插入的数据不会被刚执行查询操作的事务访问到。
事务的隔离级别
隔离级别 | 读数据一致性 | 脏读 | 不可重复读的问题 | 幻读 |
---|---|---|---|---|
未提交读 Read Uncommitted | 最低级别,只能保证不读取物理上损坏的数据 | 是 | 是 | 是 |
已读提交 Read committed | 语句级 | 否 | 是 | 是 |
可重复读取 Repeatable Read | 事务隔离级别 | 否 | 否 | 是 |
序列化 Serializable | 最高级别,事务级 | 否 | 否 | 否 |
可以通过 “show variables like ‘%iso’; ” 查看当前的隔离级别
SELECT @@global.tx_isolation, @@tx_isolation; --查看
+-----------------------+------------------+
| @@global.tx_isolation | @@tx_isolation |
+-----------------------+------------------+
| REPEATABLE-READ | READ-UNCOMMITTED |
+-----------------------+------------------+
-- 设定全局的隔离级别 设定会话 global 替换为 session 即可
常用的set语法
-- SET [GLOABL] config_name = 'foobar';
-- SET @@[session|global].config_name = 'foobar';
-- SELECT @@[global.]config_name;
SET @@gloabl.tx_isolation = 'READ-UNCOMMITTED';
SET @@gloabl.tx_isolation = 'READ-COMMITTED';
SET @@gloabl.tx_isolation = 'REPEATABLE-READ';
SET @@gloabl.tx_isolation = 'SERIALIZABLE';
2.1 事务异常现象展示与解决
2.1.1 级联回滚
隔离级别:任意
在程序的实际运行中,我们的事务往往是会并发运行的,可能在某个时间段是运行几个事务;这就出现了级联回滚的现象。
级联回滚:就是在一段时间同时有两个以上的事务在运行,由于最先运行的事务出现了 “异常” 导致事务不得不回滚的情况。
演示:下面的表格每一行可理解为执行时间
session1 | session2 | session3 |
---|---|---|
begin; | begin; | begin; |
select * from count where prefix = ‘dz10021’ | ||
update count set count = 1 where prefix = ‘dz10021’; | ||
select * from count where prefix = ‘dz10021’ | ||
update count set count = 2 where prefix = ‘dz10021’; | select * from count where prefix = ‘dz10021’ | |
update count set count = 3 where prefix = ‘dz10021’; | ||
这里添加一条错误的信息 | ||
insert into count values(‘dz10021’, 0, 2); | ||
这里因为异常回滚 | 因为session1异常回滚 | 因为session1异常回滚 |
[Err] 1062 - Duplicate entry ‘dz10021’ for key ‘PRIMARY’ | [Err] 1213 - Deadlock found when trying to get lock; try restarting transaction | [Err] 1213 - Deadlock found when trying to get lock; try restarting transaction |
解释:如上的情况就是因为与session1发生了异常,但是因为session2与session3依赖于session1实现某一些业务,为了保证数据的一致性的话,是会对于相互依赖操作的数据进行级联的回滚这是很有必要的,但是对于程序来说体验不好,事情快要做好了,突然因为别人的一个岔子打断,会很不舒服。
解决:可以再 select * from count where prefix =’dz10021’ 加上 for update
2.1.2 幻读
隔离级别:RR
session1 | session2 |
---|---|
start transaction | start transaction |
select * from count where prefix = ‘dz1’ | |
没有发现这条数据准备新增 | select * from count where prefix = ‘dz1’ |
这也没发现这条数据新增 | |
这里因为其他事情耽搁了 | insert into count values(‘dz1’, 1, 1) |
commit | |
处理之后新增 | |
insert into count values(‘dz1’, 0, 1) | |
出现主键冲突,如果没有设置主键就是数据重复提交 |
T1事务:查询数据库中是否存在prefix(主键)为dd的数据,如果没有就新增一条
T2事务:干扰的情况就是直接添加数据吗。
在程序的执行过程中,然后T1执行完了判断操作进行新增数据,因为t2在t1延时的时候执行了新增操作;这个时候t1再执行新增的时候失败了,然后再去读数据发现并没有这条数据,这个时候t1就出现了所谓的 ‘幻读’
解决:
- select * from count where prefix = ‘dz1’ for update;
- SET @@gloabl.tx_isolation = ‘SERIALIZABLE’;
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: