参考文章

数据库的四大特性以及四个隔离级别和引发的问题
https://www.cnblogs.com/shan-kylin/p/9543294.html

SpringBoot 系列教程之事务隔离级别知识点小结
https://www.cnblogs.com/yihuihui/p/12254910.html

四大特性(ACID)

1.原子性(Atomicity)

  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。失败回滚的操作事务,将不能对事务有任何影响。

2. 一致性(Consistency)

  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
  例如:A和B进行转账操作,A有200块钱,B有300块钱;当A转了100块钱给B之后,他们2个人的总额还是500块钱,不会改变。

3. 隔离性(Isolation)

  隔离性是指当多个用户并发访问数据库时,比如同时访问一张表,数据库每一个用户开启的事务,不能被其他事务所做的操作干扰(也就是事务之间的隔离),多个并发事务之间,应当相互隔离。
  例如同时有T1和T2两个并发事务,从T1角度来看,T2要不在T1执行之前就已经结束,要么在T1执行完成后才开始。将多个事务隔离开,每个事务都不能访问到其他事务操作过程中的状态;就好比上锁操作,只有一个事务做完了,另外一个事务才能执行。

4. 持久性(Durability)

  持久性是指事务的操作,一旦提交,对于数据库中数据的改变是永久性的,即使数据库发生故障也不能丢失已提交事务所完成的改变。

事务的隔离级别

  事务的隔离级别从低到高分别是:读未提交,读已提交,可重复读,串行化,这四个隔离级别可以分别解决脏读,不可重复读,幻读的问题。

先介绍一下不存在事务隔离级别的时候会出现的情况

数据库的四大特性以及四个隔离级别和引发的问题 - 图1

  很明显的看出,旺财对A添加的20块不翼而飞了,这就是“数据丢失”,对事务不加任何锁(不存在事务隔离),就会导致这种问题。

读未提交(Read uncommitted)

示例1:

  操作:写数据的时候添加一个X锁(排他锁),也就是在写数据的时候不允许其他事务进行写操作,但是读不受限制,读不加锁。
数据库的四大特性以及四个隔离级别和引发的问题 - 图2

这样就可以解决了多个人一起写数据而导致了“数据丢失”的问题,但是会引发新的问题——脏读。
脏读:读取了别人未提交的数据。
数据库的四大特性以及四个隔离级别和引发的问题 - 图3

因而引入了另外一个事务隔离级别——读已提交

示例2

公司发工资了,把50000元打到我的账号上,但是该事务并未提交,而我正好去查看账户,发现工资已经到账,是50000元整,非常高兴。可是不幸的是,领导发现发给的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后我实际的工资只有2000元,空欢喜一场。
  脏读是两个并发的事务,“事务A:领导发工资”、“事务B:我查询工资账户”,事务B读取了事务A尚未提交的数据。
  当隔离级别设置为Read uncommitted时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。

读已提交(Read committed)

  操作:写数据的时候加上X锁(排他锁),读数据的时候添加S锁(共享锁),而且有约定:如果一个数据加了X锁就没法加S锁;同理如果加了S锁就没法加X锁,但是一个数据可以同时存在多个S锁(因为只是读数据),并且规定S锁读取数据,一旦读取完成就立刻释放S锁(不管后续是否还有很多其他的操作,只要是读取了S锁的数据后,就立刻释放S锁)。
数据库的四大特性以及四个隔离级别和引发的问题 - 图4
  这样就解决了脏读的问题,但是又有新的问题出现——不可重复读。
不可重复读:同一个事务对数据的多次读取的结果不一致
数据库的四大特性以及四个隔离级别和引发的问题 - 图5

解决方法——引入隔离级别更高事务隔离:可重复读

可重复读(Repeatable read)

  操作:对S锁进行修改,之前的S锁是:读取了数据之后就立刻释放S锁,现在修改是:在读取数据的时候加上S锁,但是要直到事务准备提交了才释放该S锁,X锁还是一致。
数据库的四大特性以及四个隔离级别和引发的问题 - 图6
这样就解决了不可重复读的问题了,但是又有新的问题出现——幻读。
例如:
有一次旺财对一个“学生表”进行操作,选取了年龄是18岁的所有行, 用X锁锁住, 并且做了修改。
改完以后旺财再次选择所有年龄是18岁的行, 想做一个确认, 没想到有一行竟然没有修改!
这是怎么回事? 出了幻觉吗?
原来就在旺财查询并修改的的时候, 小强也对学生表进行操作, 他插入了一个新的行,其中的年龄也是18岁! 虽然两个人的修改都没有问题, 互不影响, 但从最终效果看, 还是出了事。
码农翻身注: 正是小强的操作, 让旺财出现了“幻读”)
解决幻读的方式——串行化

串行化(Serializable )

  事务只能一件一件的进行,不能并发进行。

总结:
数据库的四大特性以及四个隔离级别和引发的问题 - 图7

mysql默认的隔离级别是:可重复读。
oracle中只支持2个隔离级别:读已提交和串行化,默认是读已提交。

扩展一些关于范式的的总结:

第一范式:一个单元格只存储一个值。
第二范式:满足所有的属性字段唯一依赖主键
第三范式:消除传递依赖,例如:订单号——决定——->用户id———决定———>用户名;这个时候我们就需要消除这种依赖传递;但是又时候也得兼顾查询效率,高查询率低修改率的字段可以考虑违反第三范式。
  
参考:刘欣《码农翻身》中的数据库村的旺财和小强

幻读和不可重复读的主要区别在于:

  • 幻读针对的是查询结果为多个的场景,出现了数据的增加 or 减少
  • 不可重复度读对的是某些特定的记录,这些记录的数据与之前不一致