7. 如何人为的制造死锁
Last updated
Was this helpful?
Last updated
Was this helpful?
死锁是一种因为加锁而产生的冲突, 简单来说就是我持有资源A, 等你手上的资源B, 而你也在等我的资源A, 这样咱俩等于说就是在这里耗着了. 这种现象不一定出现在数据库上, 操作系统乃至任何包含锁设计的东西都可能会这种问题. 我们举个例子:
在上面的场景中 [事务A已经拿到了id=1的行锁, 等待id=2的行锁] [事务B拿到了id=2的行锁, 等待id=1的行锁]. 在这种情况下, 一个简单的死锁就被制造出来了.
这个问题的出现并不是一个非常刁钻的角度, 似乎也是轻轻松松就能被复现的, 如果这两条记录代表两个人, 俩人互相给对方转一块钱, 这个场景就能被立刻复现出来, 然后两个人就在这耗着了.
MySQL不是行锁, 而是任何锁都是存储引擎制造出来的, 因为MySQL的Server层只负责简单的解析并把查询语句传给引擎, 至于你怎么查, 用什么策略完成, 都是存储引擎这边负责的. 你比如MyISAM甚至就没有行锁, 它只有表锁, 可以说是古代人引擎了.
表锁/行锁会让你的查询hang在哪儿, 也许你现在美滋滋的在想自己表设计的多完美, 索引玩的多专业, 一个死锁就能让你的程序回到原点, 而且这一切说实话我觉得不太好预测, 你需要在脑海里模拟出业务发生时的场景, 而业务复杂度一上来就完全没法想象了.
MySQL的行锁会在需要的时刻被加上, 比如在你将更新id=1的时候才会为为id=1加上行锁, 但是释放也并不是用完立刻释放, 而是等事务结束了才会释放, 这个就是二段锁协议.那么死锁的原理你也知道了, 有什么办法处理呢?
如果要一次性更新好几行, 把比较热点的, 争抢性比较高的尽量放在后面争抢性比较高的尽量放在后面, 这样你在拿到锁以后能尽量更快的释放.
把热点行拆一拆, 比如商家账户余额这一栏显然就是热点区, 将商家余额拆成好几行, 随便插入哪一个都行, 最后商家余额就是这几行的和
一个稍hacking的办法是在你的中间件里动手脚, 比如我们做一个proxy承接抢茅台的请求, 所有到访请求会进入queue, 然后排队购买
最后数据库本身也有死锁检测机制, 每次产生了锁等待, 数据库就会开始检测到底是谁抢占了这个锁, 而这个人又在等待什么, 由此展开死锁检测, 但是这种行为本身也是有负担的, 设想有1000个人秒杀, 进入锁等待, 那么1000个线程全都开始遍历, 时间复杂度是一百万