全局锁是什么?全局锁有什么用?全局锁怎么用?
全局锁主要用在逻辑备份过程中,对于InnoDB 引擎的库,使用–single-transaction
;
MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL),让整个库处于只读状态。
表锁是什么?表锁有什么用?表锁怎么用?
表锁一般是在数据库引擎不支持行锁的时候才会被用到的.
表锁的语法是 lock tables … read/write;
加上读锁,不会限制别的线程读,但会限制别的线程写。加上写锁,会限制别的线程读写。
行锁是什么?行锁有什么用?行锁怎么用?
行锁就是针对数据表中行记录的锁。
比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。
一定知道了事务 A 持有的两个记录的行锁,都是在 commit 的时候才释放的。若行锁不是在 commit 之后被释放,而是在该语句执行完就被释放,则不会出现事务 B 被锁住。
如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
调整语句顺序并不能完全避免死锁。
死锁与死锁检测
并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。
为了避免这个操作,常用死锁检测。
发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参innodb_deadlock_detect
设置为 on
,表示开启这个逻辑。
死锁检测算法复杂度很高 N个进程,遍历N遍,M个资源,每个资源操作一次。则复杂度 O(M*N^2)。
假设有 1000 个并发线程要同时更新同一行,那么死锁检测操作就是 100 万这个量级的。最终检测的结果可能是没有死锁,但是这期间要消耗大量的 CPU 资源。
何时会死锁检测
每条事务执行前都会进行检测吗?
并不是,如果他要加锁访问的行上有锁,他才要检测。
一致性读不会加锁,就不需要做死锁检测;
并不是每次死锁检测都都要扫所有事务。比如某个时刻,事务等待状态是这样的:
B在等A,
D在等C,
现在来了一个E,发现E需要等D,那么E就判断跟D、C是否会形成死锁,这个检测不用管B和A。
死锁检测其实就是算法,环的检测,不必每次遍历一遍当前事务,只需要判断事务链表中,每加入一个新事物后是否有环的生成,有就形成死锁。这个方法和leetcode的链表中的环检测应该是一个道理。
如何避免高量级的死锁检测
为了避免这个问题,一般来说有两种方法:
1、果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉。一旦发生死锁现象,则会出现超时(50s)
2、控制并发度:
1、对于相同行的更新,在进入引擎之前排队。
2、减少行更新锁冲突的方法:将单行拆成逻辑上的多行
练习
如果你要删除一个表里面的前 10000 行数据,有以下三种方法可以做到:
第一种,直接执行 delete from T limit 10000;
第二种,在一个连接中循环执行 20 次 delete from T limit 500;
第三种,在 20 个连接中同时执行 delete from T limit 500。
方案一,事务相对较长,则占用锁的时间较长,会导致其他客户端等待资源时间较长。
方案二,串行化执行,将相对长的事务分成多次相对短的事务,则每次事务占用锁的时间相对较短,其他客户端在等待相应资源的时间也较短。这样的操作,同时也意味着将资源分片使用(每次执行使用不同片段的资源),可以提高并发性。
方案三,人为自己制造锁竞争,加剧并发量。