锁种类
- 全局锁
- 表级锁
- 元数据锁(MDL)
- 表锁
- 行级锁
- 行锁
- 间隙锁
- next-key lock
行级锁在下一篇文章介绍
全局锁
加锁方式
flush table with read lock;
- 加锁后整个库处于只读状态
- 增删改语句被阻塞,DDL语句被阻塞,更新类事务提交语句被阻塞
典型使用场景
- 全库逻辑备份
- 官方自带的逻辑备份工具是mysqldump,通过参数-single-transaction导出数据的时候,会启动一个事务,确保拿到一致性试图,期间可以正常更新数据
- 需要存储引擎支持可重复读的事务隔离级别,所以FTWRL命名还是有必要的
- -single-transaction只适用于存储引擎支持事务的表
其他方式
- set global readonly = true方式也可以设置全库只读
- 推荐使用FTWRL方式
- 在主从结构中,readonly的值是用于判断一个库是从库还是主库
- 执行FTWRL命令,客户端异常断开,MySQL会自动释放全局锁,但set readonly不会
有点像Java中的synchronized和lock
表级锁
表锁
加锁方式
lock tables ... read/wirte;
可以通过unlock tables主动释放锁,客户端断开连接的时候也会主动释放
- lock tables ... read
当前线程和其他线程只能在表中读取数据
- lock tables ... write
当前线程才可以从表中读写数据
元数据锁(MDL)
- 不需要显示使用,访问表的时候会被自动加上
- 防止DDL跟DML并发冲突
- 对表做增删改查的操作,会加上MDL读锁
- 对表结构做变更操作,会加上MDL写锁
- 事务中的MDL锁在语句开始执行的时候申请,在语句结束后不会马上释放,等到整个事务提交后再释放
死锁
死锁解决策略
- 通过参数
innodb_lock_wait_timeout
设置等待超时时间,默认值为50s- 设置等待时间太长,容易导致线上服务不可用
- 设置等待时间太短,容易会误伤友军
- 发起死锁检测,设置参数
innodb_deadlock_detect = on
(默认开启),发现死锁后,主动回滚死锁链中的某个事务,让其他事务继续执行
热点行更新容易导致死锁的问题
- 如果能从业务上保证不会出现死锁,可以临时把死锁检测关掉(危险操作)
- 控制并发度(比如限制同一行最多只有10个线程更新)
- 从设计上优化,将一行变成逻辑上的多行,细化锁粒度,较少死锁检测的CPU消耗
将原本要更新的1行,拆分为n行,那么每次冲突的概率变为原来的1/n