MySQL事务(transaction)的四大特性ACID、隔离性问题及隔离级别
1.事务的四大特性(ACID)
原子性(atomicity):一个事务是不可分割的工作单位,事务中的操作要么一起发生,要么一起不发生
一致性(consistency):事务前后的数据必须保持一致
隔离性(isolation):多个事务之间数据要相互隔离
持久性(durability):事务一旦被提交,改变是永久性的
2.隔离性问题
2.1隔离性分析
- 隔离性问题本质上就是多线程的并发安全问题
- 如果两个线程并发修改,必然会产生多线程安全问题,必须隔离
- 如果两个线程并发查询,必然没有问题,不需要隔离
- 如果一个线程修改,一个线程查询,在不同的应用场景下可能有问题,也可能没问题
2.2隔离性可能造成的问题
脏读(dirty reads):一个事务读取到另一个事务未提交的数据
实现脏读过程:
1.打开两个窗口,因为SESSION表示的是当前窗口有效,所以窗口1和窗口2都要先执行下面语句,将隔离级别设置为最低
set session transaction isolation level read uncommitted;
2. 执行窗口1中圈住的语句
3.执行完窗口1中的语句后回到窗口2中开启事务并执行查询,此时窗口2中的数据查询a用户已经被扣款100,b用户账户已经被增加100(因为窗口1还未完成提交,但是允许查询,故出现了脏读)
4.执行窗口1的回滚操作
5.回到窗口2中继续查询数据,此时展示的值就是窗口一回滚后的值
2.不可重复度(non-repeatable reads) :一个事务多次读取同一条数据,多次查询结果不同
测试方法类似于脏读(此处不再赘述)
窗口1:
start transaction;
select * from user where name='a';
窗口2:
start transaction;
update user set money=money-100 where name='a';
commit;
窗口1:
/#两次查询结果不同
select * from user where name='a';
commit;
3.虚读/幻读(phantom reads ):出现概率很低,一个事务多次查询整表数据,查询到的结果不同
测试方法类似于脏读(此处不再赘述)
窗口1:
start transaction;
select sum(money) from user;
窗口2:
start transaction;
insert into user values(3,'c',5000);
commit;
窗口1:
#两次查询结果不同
select sum(money) from user;
commit;
3.隔离级别
数据库针对不同场景需求设计了不同的隔离级别
- read uncommitted:不做任何隔离,可能造成脏读、不可重复读、虚读(幻读)问题
- read committed:可以防止脏读,但是不能防止不可重复读、虚读(幻读)问题
- repeatable read:可以防止脏读、不可重复读,但是不能防止虚读(幻读)问题
- serializable:可以防止所有隔离性问题,但是数据库被设计为串行化的数据库,性能很低
安全性:serializable>repeatable read>read committed>read uncommitted
效率:read uncommitted>read committed>repeatable read>serializable
MySQL默认隔离级别:repeatable read
Oracle默认隔离级别:read committed
操作隔离级别
查询数据库的隔离级别
select @@tx_isolation;
修改隔离级别
set [session/global] transaction isolation level xxx;
不写默认为session,表示只修改当前客户端的隔离级别,不影响其他客户端
如果设置为global,表示修改数据库默认的隔离级别,但是当前窗口无效
例如:set session transaction isolation level serializable;