2

MySQL学习笔记-4-锁

 2 years ago
source link: https://segmentfault.com/a/1190000040994933
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MySQL学习笔记-4-锁

发布于 今天 14:29

I、锁分类

锁加锁释放锁全局锁Flush tables with read lock(FTWRL)unlock tables表锁lock tables ... read/writeunlock tables行锁事务更新记录时事务COMMIT之后
锁粒度越大,开销越小,锁冲突的概率越小,安全性也就越高,但业务并发度以及性能越差;反之锁粒度越小,开销也就越大,锁冲突的概率越大(易导致死锁),安全性也就越低,但业务并发度以及性能越好。

II、全局锁

1、使用场景:全库逻辑备份。支持MVCC的可不用。
2、使用风险:
——如果在主库备份,在备份期间不能更新,业务停摆
——如果在从库备份,备份期间不能执行主库同步的binlog,导致主从延迟。
3、官方自带的逻辑备份工具mysqldump,当mysqldump使用参数--single-transaction的时候,会启动一个事务,确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。但是前提是引擎要支持这个隔离级别。
4、如果要全库只读,为什么不使用set global readonly=true的方式?
——在有些系统中,readonly的值会被用来做其他逻辑,比如判断主备库。所以修改global变量的方式影响太大。
——在异常处理机制上有差异。如果执行FTWRL命令之后由于客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为readonly之后,如果客户端发生异常,则数据库就会一直保持readonly状态,这样会导致整个库长时间处于不可写状态,风险较高。

III、表级锁

1、分类:表锁,元数据所(meta data lock,MDL)。
表锁的语法是:lock tables ... read/write
2、unlock tables命令主动释放锁,客户端断开的时自动释放。
3、lock tables语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。对于InnoDB这种支持行锁的引擎,一般不使用lock tables命令来控制并发,毕竟锁住整个表的影响面还是太大。
4、MDL保证读写的正确性
——不需要显式使用,在访问一个表的时候会被自动加上。
——何时使用:在对一个表做增删改查操作的时候;当要对表做结构变更操作的时候
——读锁之间不互斥。读写锁之间,写锁之间互斥。
——MDL直到事务提交才释放

IV、行锁

1、两阶段锁协议:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。
2、如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。锁的粒度越大,锁冲突的可能性越大,语句越后执行;锁粒度越小,锁冲突的可能性越小,语句越先执行。
3、死锁:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态。如A等B,B等C,C等A。
4、死锁解决方案:
——设置超时时间(参数:innodb_lock_wait_timeout,InnoDB默认50s)。
——发起死锁检测。发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行(参数:innodb_deadlock_detect=on,默认开启)。过程示例:新来的线程F,被锁了后就要检查锁住F的线程(假设为D)是否被锁,如果没有被锁,则没有死锁,如果被锁了,还要查看锁住线程D的是谁,如果是F,那么肯定死锁了,如果不是F(假设为B),那么就要继续判断锁住线程B的是谁,一直走知道发现线程没有被锁(无死锁)或者被F锁住(死锁)才会终止。
5、热点行更新现象:每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是O(n)的操作。假设有1000个并发线程要同时更新同一行,那么死锁检测操作就是100万这个量级的。虽然最终检测的结果是没有死锁,但是这期间要消耗大量的CPU资源。
热点行更新:
1、不建议:如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关闭掉。
2、控制并发度。对应相同行的更新,在进入引擎之前排队。这样在InnoDB内部就不会有大量的死锁检测工作了。如果你有中间件,可以考虑在中间件实现;如果你的团队有能修改 MySQL 源码的人,也可以做在 MySQL 里面。基本思路就是,对于相同行的更新,在进入引擎之前排队。这样在 InnoDB 内部就不会有大量的死锁检测工作了。
3、将热更新的行数据拆分成逻辑上的多行来减少锁冲突,但是业务复杂度可能会大大提高。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK