|||
作者简介:高鹏,笔名八怪。《深入理解MySQL主从原理》图书作者,同时运营个人公众号“MySQL学习”,持续分享遇到的有趣case以及代码解析!
最近在进行purge binary logs的时候遇到一个错误如下,
当然这个错误提示非常明显,就是在某个session执行了LOCK INSTANCE FOR BACKUP的情况下,不允许进行purge binary logs 命令,但是这个错误视乎没有遇到过,是不是8028新加入的报错呢?
在文章<<MySQL:8.0新的备份锁的浅析及其在xtrbackup的应用>>一文中我们详细描述过他的作用,以及其实现方式,如下。 这个操作主要堵塞的是DDL操作,包含不限于如下一些常见的操作:
CREATE_TABLE、CREATE_INDEX、ALTER_TABLE、TRUNCATE、DROP_TABLE、LOAD、CREATE_DB
、DROP_DB、ALTER_DB、RENAME_TABLE、DROP_INDEX、CREATE_VIEW、DROP_VIEW、CREATE_TRIGGER、DROP_TRIGGER、CREATE_EVENT、ALTER_EVENT、DROP_EVENT、IMPORT、RENAME_USER、DROP_USER、ALTER_USER、GRANT、REVOKE、GRANT_ROLE、REVOKE_ROLE、DROP_ROLE、CREATE_ROLE、OPTIMIZE、CREATE_FUNCTION、CREATE_PROCEDURE、DROP_PROCEDUR、DROP_FUNCTION、ALTER_PROCEDURE、ALTER_FUNCTION、REPAIR、ANALYZE、ALTER_TABLESPACE
我也随意测试了一些操作,确实都会被堵塞。从内部来看,实际上这个操作的功效依然是通过MDL LOCK的实现的,其策略为m_scoped_lock_strategy,那么从scoped策略的兼容性来看,如下:
对于lock instance for backup本生而言实际上是做了的如下操作:
Sql_cmd_lock_instance::execute
->acquire_exclusive_backup_lock
->acquire_mdl_for_backup
获取的MDL LOCK为 MDL_key::BACKUP_LOCK+MDL_SHARED(S)
并且看起来 MDL_key::BACKUP_LOCK的锁类型只会是MDL_SHARED(S)或者MDL_INTENTION_EXCLUSIVE(IX),因为acquire_mdl_for_backup里有断言:
DBUG_ASSERT(mdl_type == MDL_SHARED || mdl_type == MDL_INTENTION_EXCLUSIVE);
对于上面提到的会被堵塞的这些操作,则是在响应的地方加入了获取 MDL_key::BACKUP_LOCK+MDL_INTENTION_EXCLUSIVE(IX) 根据兼容矩阵,S和IX并不兼容,但是IX和IX之间是兼容的,因此只要不执行lock instance for backup操作,则不会有任何影响。而对于用户而言会发现processlist出现Waiting for backup lock字样则为这样的堵塞。测试如下:
mysql> show processlist;
+----+------+-----------+---------+---------+------+-------------------------+-------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+---------+---------+------+-------------------------+-------------------+
| 11 | root | localhost | testpri | Query | 4 | Waiting for backup lock | OPTIMIZE table t1 |
| 12 | root | localhost | t10 | Sleep | 175 | | NULL |
| 13 | root | localhost | testpri | Query | 0 | init | show processlist |
当然既然是MDL LOCK,它同样受到参数lock_wait_timeout参数的影响。比如我这里的操作为OPTIMIZE如下:
mysql> OPTIMIZE table t1;
+------------+----------+----------+--------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+------------+----------+----------+--------------------------------------------------------+
| testpri.t1 | optimize | Error | Lock wait timeout exceeded; try restarting transaction |
| testpri.t1 | optimize | status | Operation failed |
+------------+----------+----------+--------------------------------------------------------+
2 rows in set (2 min 0.01 sec)
需要的注意的这个锁并不会堵塞任何DML和SELECT操作(即便是mysiam表也不会堵塞DML操作),但是保护了元数据的正确性。如果要获取的一致的数据我们就需要额外的机制也就是下面谈到的ps.log_status。
关于LOCK INSTANCE FOR BACKUP的常用场景大概有2个地方,如下,
/* Acquire backup lock */
if (block_ddl()) {
auto failed = mysql_service_mysql_backup_lock->acquire(
thd, BACKUP_LOCK_SERVICE_DEFAULT, m_client_ddl_timeout);
这里可以看到就是lock instance的调用
mysql_acquire_backup_lock
->acquire_exclusive_backup_lock
->acquire_mdl_for_backup
->MDL_context::acquire_lock
而如前文所言,在8.0.28中为了防止在lock instance for backup下能够修改文件,因此把清理binlog也加入其中,所以有了新的报错,引入这个错误的BUG修复如下,
也就是修复这个BUG引入的新的报错,主要增加的判定函数就是,
以上。。。
合作电话:010-64087828
社区邮箱:greatsql@greatdb.com