美文网首页
数据库内部锁事务和显式外部锁

数据库内部锁事务和显式外部锁

作者: 賈小強 | 来源:发表于2019-02-28 21:45 被阅读0次

    简书 賈小強
    转载请注明原创出处,谢谢!

    情况1:事务a往表X写入1 2 3

    由于事务的原子性,要么成功要么失败,所以要么写入1 2 3,要么一个也没有写入

    情况2:事务a往表X写入1 2 3,同时事务b进行相同操作

    由于事务的隔离性,事务a和事务b互相独立

    情况3:事务a往表X写入1 2 3,同时事务b往表Y写入 1 2 3

    不论是对同一张表还是同一个数据库的两张表操作,事务的隔离性体现在事务,所以并不会出现所谓的事务a提交了,然后影响事务b的情况

    情况4:事务a查找表X没有1则写入1,同时事务b进行相同操作
    • 悲剧:事务a查找发现没有,于是打算写入,事务b在a事务提交前查找也发现没有,于是也认为应该写入,最后都写入导致重复写问题

    • 分析:数据库有两种内部锁:单个SQL语句比如select和insert而言,对于select为读锁,这种锁允许多个事务同时查询一张表,对于insert为写锁,这种锁只允许同一时间只有一个事务能够操作,由于select是幂等的,而insert是原子性的,也就说可以认为单条SQL语句是原子性的,但是select...insert形式两个SQL语句的组合,数据库自身并没有维护其原子性,这在并发的时候很容易出现重复写问题,对于select...update则是丢失修改问题

    • 解决方式:

      1. 采用select...for update,比如Oracle数据库提供了这种形式的语句,在某个事务执行这条SQL语句的时候将阻塞其它事务,所以是安全的,不过由于是悲观锁会阻塞其他所有事务,所以可能导致性能问题
      2. 采用redis显式构造一个悲观锁,其效果和方式一的一样,安全但是效率不高,相比方式一优点是可以将多个SQL语句写在一个锁之间
      3. 可以换一个角度改进设计方案,避免这种需要悲观锁的方式
      4. 改用redis中形式的乐观锁,并发的时候都可以修改,谁先修改成功就算谁的,其他同时修改的发现提交时已经被别的修改了则回退,乐观锁的优点就是让耗时少的先通过,不过并不是所有情况都适合乐观锁,在竞争激烈的情况下可能导致很多的无效尝试
    • 结论:select...insert操作需要加锁同步

    情况5:事务a查找表X没有1写入1,查找表Y没有2写入2,同时事务b进行相同操作
    • 问题:通过select...for insert或者显式悲观锁给每个表的select...insert加悲观锁,期望缩小锁导致的同步代码范围,提升程序并发效率,是否没问题?
    • 悲剧:虽然保证了对某张表的select...insert原子性,但比如事务a查找表X没有1写入1,然后对表Y处理,但是事务还没提交,而事务b在事务a释放表X锁后,获取到表X的锁可以操作表X,进行查找表X没有1写入1的操作,同样导致了重复写问题,或者可能事务a现提交成功写入了id=1的数据,到事务b的时候提交结果发现有的id=1的已经存在了,导致违反数据库完整性约束
    • 结论:如果打算对一系列表的select...insert操作看作一个整体事务,那么应该对整个事务加一个显式外部悲观锁,只有这一系列表并不需要看作一个整体事务,那么才可以增多锁和事务,达到减少同步代码的目的,但是锁和事务总是维持同范围的

    Happy learning !!

    相关文章

      网友评论

          本文标题:数据库内部锁事务和显式外部锁

          本文链接:https://www.haomeiwen.com/subject/ogvhuqtx.html