锁解决了那些问题

锁主要用于解决并发问题,

锁的种类以及解决了那些问题

1: 全局锁
解决备份的库是逻辑不一致的问题, 比如 用户有个操作, 需要在a表上增加一行, 在b表上删除一行, 那我们在备份库的时候, 可能备份了老的a表, 但是b表确是新的, 导致数据逻辑不一致
使用全局锁备份库的时候, 1: 如果备份从库, 会导致主从延迟增大, 2: 如果备份主库, 备份期间不能更新数据, 会导致业务停滞
2: 表级锁
分为表锁和元数据锁.
i: 表锁: lock tables … read/write(除了会限制别的线程的读写外,也限定了本线程接下来的操作对象), 假如本线程获取的是读锁, 就不可以更新数据 unlock tables /客户端连接断开(释放锁)
ii: 元数据锁: 自动加解锁, 无需用户调用.
解决的问题: 查询的数据和表结构对不上. 假设没有元数据锁, 一个用户在查数据, 另一个用户在增加表结构字段的操作, 那么查到的数据就会和表结构对不上.
加读锁时机: 在对数据进行增删改查的时候进行 加写锁时机: 在对表结构做变更的时候进行
结论: 读锁之间不互斥, 意味增删改查是并行的, 读写锁之间是互斥的, 意味不可以并行
使用注意事项: 使用的不好会导致线程被打满, 例如: 对表结构做变更, 因为需要获取写锁(前面有读数据的任务)导致阻塞, 而这个写锁阻塞会导致后面客户不能进行增删改查, 加上客户有超时重试机制, 这个库的线程很快就会打满
事务中的 MDL 锁: 整个事务结束后才会释放
3: 行锁:
更新同一行数据时, 会用到行锁
两阶段锁协议: 有需要的时候获取行锁, 但是在整个事务结束时才释放行锁
注意事项: 如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放

适用的场景

1: 全局锁: 全库逻辑备份(还有一个方法: 可重复读隔离级别 但是需要数据引擎支持事务才可以)
2: 表锁: 不支持行锁的时候会用到

问题

1: 如何给小表安全的加字段?

i: kill掉长事务, 因为长事务会长期占用元数据锁, 导致获取写锁阻塞
ii: alter table 语句里面设定等待时间,如果在这个指定的等待时间里面能够拿到 MDL 写锁最好,拿不到也不要阻塞后面的业务语句,先放弃

2: 死锁和死锁检测
image.png
事务a和事务b都在等待对方的资源释放, 造成了死锁
方法 1: 设置事务超时, 超时之后放弃执行
2: 发起死锁检测, 一旦发现就回滚事务:
问题: 很耗cpu: 每来一个被堵住的新线程, 都要做死锁检测, 时间复杂度o(n), 假设有1000个并发线程要更新同一行数据, 第800个线程的时间复杂度为800, 一次类推, 第1000个线程的时间复杂度是1000, 一共是1000*999/2, 百万级别复杂度
方法: 1: 保证整个业务一定不会出现死锁, 这样就不用发起死锁检测了
2: 做好并发控制度, 如果最多只有10个线程, 那么时间复杂度就是可控的. 如果并发控制做在客户端, 但是由于客户端有很多个, 所以并不是有效的, 假设有100个客户端, 每个客户端并发控制5个线程, 那么也有500个. 如果做在服务端, 就需要在做个中间件或者修改mysql源代码
3: 将一行分解为逻辑上的多行, 比如我们在增加数据的时候只要选择任意一行就可以了, 和具体业务有关, 需要详细设计