全局锁
场景
- 全库逻辑备份:select整个库的每个表备份。
整个库处于只读状态的风险
Flush tables with read lock(FTWRL)- 如果在主库上备份,备份期间不能执行更新操作,业务基本上得停摆。
- 如果在从库上备份,备份期间从库不能执行主库同步过来的binlog,会导致主从延迟。
- 执行FTWRL命令的客户端异常断开,MySQL会自动释放全局锁。
set global readonly = true- 某些场景下,readonly用作判断数据库是主库还是从库,修改global变量影响面更大,不建议使用。
- 设置readonly,客户端异常断开,数据库依然保持readonly状态,会导致整个库长时间处于不可写状态,风险较高。
- 加上全局锁,增删改查操作(DML)或者表结构变更操作(DDL)都会被锁住。
表级锁
表锁
- 语法
lock tables ... read/write - 释放
- unlock tables
- 客户端断开
- 元数据锁(meta data lock, MDL)
- MySQL5.5版本引入,系统默认加上。
- MDL读锁之间不互斥,MDL读写锁、写锁之间互斥。
- 增删改查操作加MDL读锁,表结构变更操作加MDL写锁。
- 大表DDL操作
- 会扫描全表的数据,需要特别小心。
- 小表DDL操作
- information_schema库的innodb_trx表,查询当前执行的事务。如果此时有长事务在执行,考虑先暂停DDL,或者kill掉这个长事务。
- alert table指定超时时间(MariaDB和AliSQL具备此功能)
alert table <table_name> nowait add column
alert table <table_name> wait <n> add column
行锁
- 针对数据表中行记录的锁。
- MyISAM 引擎不支持行锁,InnoDB支持行锁。
- 在事务中,如果需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
死锁
-
事务A和事务B互相等待对方的锁资源释放。
事务A 事务B begin; begin; update t set k = 1 where id = 1 update t set k = 1 where id = 2 update t set k = 1 where id = 2 update t set k = 1 where id = 1
死锁检测
等待死锁超时
- 参数
innodb_lock_wait_timeout, 默认50s。 - 局限性
- 时间设置太短,会误伤真正需要锁等待的逻辑。
- 时间设置太长,等待死锁超时的时间,可能会影响其他在线的业务。
主动回滚锁链条中的某一个事务
- 参数
innodb_deadlock_detect = on - 局限性
- 如果每个堵住的线程都判断是否由于自己的加入导致了死锁,死锁的检测时间复杂度是n的平方(n=线程数)。假设1000个线程更新同一行,死锁检测操作就是100万量级的,这期间会消耗大量的CPU资源。