事务特性&安全问题&隔离级别

事务特性 (ACID)

  • 原子性(Atomicity):事务中的逻辑要全部执行,不可分割。否则回滚。
  • 一致性(consistency):事务执行前后,数据的完整性保持不变。

    举个栗子:张三有1000元,李四有1000元。张三给李四转500元,转完之后,张三还剩500元,李四有1500元。转账前后,张三和李四的钱的总和都是2000元。不会发生变化。

  • 隔离性(isolation):一个事务在执行的过程中不应该受到其他事务的影响。
  • 持久性(durability):事务执行完成后,数据都应持久化下来。

其中的隔离性又可以分为不同的隔离级别。用于应对不同层次读的安全问题(当然也有写的安全问题,但是这里不展开)。

安全问题

问题出现的场景:张三有1000元,李四有1000元。张三给李四转500元,这里面其实是两步,首先第一步张三扣500元,接着第二步李四加500元。但是如果我们在这两步之间进行一次查询,会查到张三有500元,李四有1000元,这里就会出问题。这个问题就是脏读问题

  • 脏读:一个事务读到了另一个事务尚未提交的数据。

    解决脏读问题,我们需要提高隔离级别,很容易想到:不让一个事务读到另一个事务尚未提交的数据即可,那很自然的想到一个隔离级别就是“读已提交”。这个隔离级别其实就是字面意思:只能读其他事务已提交的数据,自然能够应付脏读的问题。


再考虑一个场景:张三有1000元,李四有1000元。张三给李四转500元,前面说了这里面其实是两步,如果我们开启一个事务去查询张三和李四的账户,在第一步之前,我们查询张三和李四的账户分别为1000元、1000元。在第二步之后,也就是提交之后,我们再查询张三和李四的账户现在为500元、1500元。
这就引出了下面的这个问题:在同一个事务中两次查询同一个数据的结果却不同。也就是不可重复读,因为读出来的结果不同嘛。

  • 不可重复读:一个事务读到了另一个事务提交的数据,导致多次查询结果不一致。

    其实这不一定算问题,因为在一个事务中,另一个事务对数据库的更新实时的能够查询出来算问题吗?很多数据库的默认级别是不应对这个问题的,比如oracle sqlserver的默认隔离级别都只是“读已提交”,只能应付脏读问题。并不能应对不可重复读问题。只有mysql的默认隔离级别是可以应付不可重复读问题的。那么到底不可重复读的真正缺点或者问题在哪呢?或者说如果不能满足可重复读,有啥实际危害的例子?(从v2ex摘几个贴在下面)

    1.我觉得把因为不知道自己读到的数据是不是正确的。他要是一开始拿 1000 去做计算,算完心想咱们读个 A 看看,读了一看呀,A 怎么变成 900 了?难道我前面做的计算都白做了么!
    2.事务 A:select 最近 1 个月注册的新用户,把他们的用户组改成 GroupA
    事务 B:注册了一个新用户,commit
    事务 A:select 最近 1 个月注册的新用户,给他们发送一条私信:恭喜你升级为 GroupA !
    不一致本身没有危害。有危害的是不知道会不一致。
    3.mysql 默认 rr 主要是为了主从同步时,采用逻辑 sql 同步时的一致性,因为主库的 sql 是并发执行的,会有两个事务一起再跑,从库同步是单线程的,不会有两个事务同时在跑,如果不是 rr,出现楼主的栗子说的情况时,主从数据就不一致了

    那么如何解决不可重复读问题呢? mysql使用了MVCC–一种多版本并发控制机制。简单来说,MVCC是通过保存数据在某个时间点的快照来实现的,简单来说,直觉上,在一个事务中,先将数据保存为一个快照,在再次读取数据时,读取这个快照,也就是旧值。自然能保证在一个事务中前后读取同一个数据的一致性。
    还有比较正常的方法是通过锁来实现,当一个事务访问某些数据时,会对这个表进行加表级锁,如此以来,其他想要更改这个表中的数据的事务,必须等我们的事务完成之后才能更改。


但是上述也说了,应对不可重复读问题通过加表级锁的方式是可以避免其他事务在一个事务查询数据时对这些数据进行更改的,但是它能防止我们往那个表中插入一条数据吗?答案是不能。因此这就引出了一个新的问题,幻读。所谓幻读,简单来说就是第二次查的数据比第一次查的数据多一条,并且这一条是奇奇怪怪的。
对于mysql来说,MVCC同样可以应对幻读的问题,还是因为他读的是老数据!是快照!并不会因为你插入某条数据他就会幻读,因为他根本读不到。

  • 幻读:一个事务读到了另一个事务已提交的插入的数据,导致多次查询结果不一致。

    怎么解决呢?mysql不用管,因为他有MVCC。对用锁来实现的,只需将锁更新为行级锁即可。也就是Serializable。但是Serializable的效率比较低


隔离级别

  • 读未提交:能够读未提交的数据,隔离级别最低
  • 读已提交:只能读已提交的数据
  • 可重复读:能重复读取数据,而数据不会改变
  • Serializable:可串行化。隔离级别最高

猜你喜欢

转载自blog.csdn.net/baidu_38815750/article/details/85039990