首先,我们下面说的数据库事务,都是使用INNODB引擎的结果。
MYISAM是没有事务的,也就没有下面这些说法。
1.数据库事务的四大特性:
- 原子性:事务包含的所有数据库操作要么全部成功,要不全部失败回滚
- 一致性:一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是100块,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是100块,这就是事务的一致性。
- 隔离性:一个事务未提交的业务结果是否对于其它事务可见。级别一般有:read_uncommit,read_commit,read_repeatable,Serializable。
- 持久性:一个事务一旦被提交了,那么对数据库中数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
了解上面的数据库事务四大特性后,我们就知道,数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable。
2.这四个隔离级别,分别可以解决下面这几类问题。
设置事务隔离级别为 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | 会出现 | 会出现 | 会出现 |
Read committed | 不会出现 | 会出现 | 会出现 |
Repeatable read | 不会出现 | 不会出现 | 会出现 |
Serializable | 不会出现 | 不会出现 | 不会出现 |
- Serializable就是强制串行化,相当于让数据库只能单线程访问,所以上面这些问题都不会出现,但效率实在太低,所以一般不会考虑将数据库隔离级别设置成Serializable。
- Read uncommitted又对事务放的太宽了,如果将数据库的隔离级别设置成Read uncommitted,那么各个事务之间就会各种乱搞乱读,那么基本上程序就会出现各种大bug,所以一般也不会将数据库隔离级别设置成Read uncommitted。
3.常见的数据库默认的事务隔离级别:
- oracle的默认隔离级别Read committed,所以默认下可能会出现不可重复读和幻读的问题。
- mysql的默认隔离级别是Repeatable read,所以默认下可能出现幻读读问题。
4.解释下常见的这几类问题
- 脏读
事务一对一条数据做了修改,但并未提交或者回滚的时候,事务二读到了这个事务一已经修改的数据,但是由于这条数据是否真的要修改,事务一还并没有确定,事务二就读到了,所以我们可以说,事务二读到了脏数据,这种问题就叫脏读。
举个例子,数据库中有一条数据a=1,此时事务一做了修改:a=2,但并未提交
如果此时事务的隔离级别是Read uncommitted的话,那么此时事务二再读a的值,就会读到2,那么如果此时事务一又回滚了,那么数据库真实的a的值还是1,但对于事务二来说,已经错了,因为事务二读到了2。
如果此时事务的隔离级别是Read committed或Repeatable read或Serializable,那么事务二读到的a的值,就还是1。 - 不可重复读
一个事务重复两次读取一条数据的中间,另一个事务对这条数据做了修改,导致第一个事务的两次读的结果出现不同。
举个例子,小李买东西的时候,刷银行卡,一开始系统查到账户余额200元,于是准备开始执行扣款,于此同时,小李老婆刚好在另外一个地方将这200元取走了,从而导致小李这边扣款失败,这就是不可重复读的问题。
注意:
mysql的默认隔离级别下,是不会出现这种问题的;
oracle的默认隔离级别下,会出现这种问题。 - 幻读
一个事务以相同的条件查询以前检索过的数据,一般指查询数据条数的时候,由于其他事务对某些数据做了增或者删,导致第一个事务查出来的总条数和以前的结果不同了,这叫幻读。
其实一般情况的业务下,幻读是可以接受的,所以我个人认为幻读不算什么问题吧。
而且mysql和oracle的默认隔离级别下,也都会出现这个幻读的现象。
如果非要解决幻读,无非就是加个表级别的锁,这样效率太差了,而且一般都没必要。
5.mysql 对隔离级别的查询和设置
查询隔离级别:
SHOW VARIABLES LIKE ‘tx_isolation’;
设置隔离级别:
SET GLOBAL tx_isolation=‘REPEATABLE-READ’;
SET SESSION tx_isolation=‘SERIALIZABLE’;
GLOBAL是全局,SESSION是当前会话,如果没有指定,默认是SESSION。