什么是锁?
为什么会出现锁这个东西呢,我们知道数据库是一个共享的数据池,当多个线程都读取同一个数据的时候难免会发生抢占。那么我们的锁就是出来为了解决这个问题的。一般来说mysql分为:全局锁,表锁,行锁,我们讲一下我们常见的行锁吧,一般的话我们通常见到的也是行级锁的问题。
两阶段锁
image.png我们可以看出图中行id=1的时候会发生行锁,此时我们的事务B就必须等待事物A完成后才能进行。此时事物A拥有两个行锁:行id=1,id=2 两个行锁,而且必须等待事物提交后才会释放锁。
这也就是两阶段锁
在innodb事务中,行锁是在需要的时候才加上的,但并不是不需要了就立即释放,
而是要等到事务结束时才释放,这就是两阶段锁
事务中排序数据库操作顺序
当我们知道了这个两阶段锁特性后,我们就可以进行常用查询的优化了。此时我们就需要按照一些原则进行查询优化。
这里我们提一个案例:用户A通过支付宝给用户B转了一笔账,假设用户B是个生意老板,分分钟几百笔进账的那种
image.png
此时我们有3个步骤:
1.减少用户A的存款
2.增加用户B的存款
3.记录交易日志
此时我们对这种操作一般都会用事物来保证一致性。
那么我们在代码中应该如何置放1.2.3的顺序呢。
B用户的收账是比较频繁的步骤,日志记录是新增操作不需要锁行,A用户相对没那么频繁的账户操作,那么我们应该这么进行代码放置:
3,1,2 这样我们锁住B的余额的时间最短,在高频请求下可以大大增加代码的并发度。
死锁和死锁检查
首先我们看一下什么是死锁
image.png
当我们有两个事务AB的时候,很不幸的有着类似或者相同的业务的时候,我们可能会发生这种事情:事务A要修改id=2的时候需要事务B释放行锁,事务B要修改id=1的时候需要事务A释放行锁,两个事务相互纠缠就形成了死锁。
那么我们的系统有什么自查功能呢
1.直接进入等待状态,直到超时,可以通过参数innodb_lock_wait_time进行设置,默认时间50秒
2.另一种策略是死锁检测,innodb发现死锁后主动回滚某一个请求链条的事务,让其它事务继续执行,将参数innodb_deadlock_detect 设置成on
一般来说两种方法我们会选择后者。
在innodb中我们用第一种默认50秒的解决方案实际上对线上服务很不友好,假如我们有两个线程发生了这两个事务,那么就意味着其他线程就访问不了,要默认等待50s,对用户来说根本是不能接受的。那么我们把锁等待值设置很小,比如一秒呢。这种解决方案也是不可取的,如果是简单的锁等待,没有死锁,那么一个事物还在执行期间行锁就被解开,万一该事务失败,数据回滚就会发生数据错误,也就破坏了事物的一致性原则。
那么我们用主动死锁检测吗?其实mysql配置中innodb_deadlock_detect 默认是开的,但是它也是有负担的,每个请求下:新进入innodb的线程都要判断由自己的加入导致死锁,这个是一个时间复杂度在On的操作,有1000个并发线程同时更新一行,那么死锁检测就是百万量级,这期间要消耗大量的cpu,这时候就会发现cpu很高,却执行不了几个事务。如果我们能确定代码的操作流程绝对不会造成死锁,我们甚至可以兵行险着,关闭掉这个选项。
网友评论