MySQL-锁

147 阅读2分钟

全局锁

场景

  • 全库逻辑备份: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资源。

参考

极客时间《MySQL实战45讲》