脏读是指一个事务读到了另一个事务执行的中间结果 。还用我们刚才的转账的例子举例:
文章插图
当我们转账的事务没有执行完,另一个事务就读取了它的中间结果,很有可能就造成脏读 。因为万一之前的事务回滚,那么新读取到的结果就是错的,和A账号回滚之后的余额不一致 。如果这个数据应用在其他的系统当中,就会引起大规模的数据问题 。
2. 不可重复读
不可重复读的意思是说,如果在一个事务当中,我们读取了某个数据两次 。刚好在这中间,有另一个事务修改了这条数据,那么同样会引起数据错误,因为这两次读取到的结果不一致 。
文章插图
比如我们对A账户的一个事务还没有结束,这时候它的结果就被其他事务修改了 。那么程序就会发生错乱,因为读到了它没有预料到的修改 。
解决方法是针对当前修改的数据进行隔离,同一时刻只允许一个事务对该条数据进行修改,以保证数据的一致性 。
3. 幻读
幻读的概念也很简单,就是一个事务读取两次,读到的数据条数不一致 。这点和不可重复读非常类似,不过不同的是不可重复读针对的是确定的某一条数据,而幻读指的是对整个数据库或者是整个表而言 。
文章插图
要解决也很简单,因为幻读是其他事务修改新增或者修改其他数据产生的,所以要排除掉这种情况,只针对我们修改的数据进行加锁和隔离是不够的 。我们需要将整个数据库,或者是分区进行隔离,同一时刻,只允许一个事务对一个分片或者是数据表进行修改 。
4. 更新丢失
更新丢失的定义很直观,当我们针对一条数据进行修改的时候 。同时也有另一个事务在修改同一条内容,会导致后者覆盖前者的内容 。比如说账户里原本100元,A事务往账户里添加10元,B事务往账户里扣除20元 。A修改成110的同时,被B事务的80所覆盖,导致A的操作就像是没有执行过一样,引起更新丢失 。这个问题在并发场景当中也最为经典 。
文章插图
解决的办法同样是做好隔离操作,在一个写入完成之前,禁止其他事务的读入 。事实上更新丢失是并发场景下最容易出现的错误,而且如果设计不合理,出现了错误也会非常难排查 。
数据库解决隔离性问题的办法就是设置不同的隔离级别,不同的隔离级别对应不同的隔离策略,可以保证不同级别下的隔离性 。不同的隔离级别意味着使用不同级别的锁,显然隔离级别越高意味着性能越差 。所以这就需要数据库管理员(DBA)对于当前的应用场景,以及并发量和数据风险有一个非常清楚的认知 。能够在性能和安全性之间做一个权衡 。这里,我们不多做具体的探究,观察一下下图,简单了解一下即可:
文章插图
从上到下以此是四种隔离级别,越往下隔离级别越高,能够解决的隔离性问题也就越多 。同样的,用到的锁也就越多,系统的性能也就越差 。
最上面未提交读是最低的隔离级别,在读取的时候并不会判断是否可能会读取到没有提交的数据 。所以它的隔离性最差,连最简单的脏读都无法解决 。
已提交读则是通过锁限制了只会读取已经提交的数据,读数据的时候使用的共享锁,在读取完成之后立即释放 。这种隔离级别只能够解决最常见的脏读问题,它也是SQL server数据库的默认隔离级别 。
可重复读的读取过程和已提交级别一样,但是在读取的时候会保持共享锁,一直到事务结束 。也就是说只要一个事务没有结束,锁就不会释放 。其他的事务无法更新数据,保证了不会出现不可重复读的情况 。
最后是可串行读,它是在可重复读的基础上进一步加强了隔离性 。在事务进行当中,不仅会锁定受影响的数据本身,而且还会锁定整个范围 。这就阻止了其他事务影响整体的情况出现 。在这个隔离级别下,保证了事务之间不会有任何踩踏 。
推荐阅读
- MySQL数据库下的Explain命令深度解析
- 一文搞懂SQL中的所有JOIN
- MySQL, PostgreSQL CentOS常用数据库安装和python使用
- 程序员该如何进行 SQL 数据库的优化?
- 一条SQL搞定数据库设计文档
- 开源免费的多功能数据库管理软件DBeaver
- 一文读懂Socket通信原理
- 安装MySQL数据库
- 史上最全数据库中间件详解
- PHP操作Redis常用方法总结