1. 行锁是引擎实现的,不是所有的引擎都支持行锁,不支持行锁的引擎在表更新的时候只能使用表锁,也就是说在同一时刻,只有一个更行在执行,很影响业务的并发度。InnoDB是支持行锁的。
  2. 行锁就是,事务A更新一行,事务B也要更新同一行,则必须等待事务A操作完成之后才能进行更新。
  3. 在InnoDB的事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
  4. 事务中,行锁是在语句执行时才加上的,不是事务开始就加上,但释放是统一在事务结束时才释放。根据这个特性,对于高并发的行记录的操作语句就可以尽可能的安排到最后面,以减少锁等待的时间,提高并发性能。
  5. InnoDB的行锁是通过索引实现的,准确的说InnoDB锁的是索引,如果没有索引就使用表锁。
  6. 在并发系统重不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都处于无限等待的状态,称为死锁。
  7. 当出现死锁之后,有两种策略:

    • 一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。
    • 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁连中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。
  8. 在InnoDB中,innodb_lock_wait_timeout默认值为50s,意味着如果采用第一个策略,当出现死锁以后,第一个被锁住的线程要过50s才会超时退出,其他线程才有可能继续执行。
  9. 如果将innodb_lock_wait_timeout设置为1s的话,就有可能误伤真的处于锁等待状态的事务。
  10. 所以正常情况下我们采用:主动死锁检测,而且innodb_deadlock_detect默认值本身就是on。
  11. 死锁检测的过程:当一个事务被锁的时候,就要看看它依赖的线程有没有别其他线程锁住,如果这个依赖的线程也被当前事务锁住,那死锁无疑。如果被不是当前事务的线程锁住,那就要再顺着往上查,直到查到没有被锁的线程,或当前事务锁住了一个线程。
  12. 如果出现所有事务都更新同一行的场景时,如果有1000个并发线程,那么死锁检测操作就是100w的量级。死锁检测会消耗大量cpu资源,因此会看到CPU利用率很高,但每秒确执行很少几个事务。
  13. 一种解法是确认自己的业务一定不会出现死锁,可以临时把死锁检测关闭。不建议使用。
  14. 另一个思路是控制并发,比如同一行同时最多只有10个线程在更新,那么死锁检测的成本很低,就不会出现问题。但如果这个检测在客户端做的话,当客户端不断增多的情况下,并发还是控制不住。因此并发控制最好坐在数据库的服务端,如果有中间件,可以考虑在中间件实现。
  15. 基本思路是对同一行的更新,在进入引擎之前排队,这样innodb内部就不会有大量的死锁检测工作了。
  16. 还有一个控制并发的方案,就是将一条记录拆成10个记录,记录的结果是10条记录加总,每次修改随机修改10条中的一条,这样也会降低同时修改一条的可能性,但这个是需要业务逻辑做详细设计。

牛刀杀鸡
3 声望0 粉丝