分区级MDL锁
在MySQL社区版中,分区表的数据访问操作(DML)和分区维护操作(DDL)会互相阻塞,这意味着分区维护只能在业务低峰期进行。然而,创建和删除分区的操作相对频繁,这在很大程度上限制了分区表的使用。
GaussDB(for MySQL)实现了分区级别的MDL锁,使得分区表的锁粒度从表级降低到了分区级,不同分区上的DML和特定DDL(如增加和删除分区)在MDL锁上不会相互阻塞,从而大大提升分区间操作的并发性。
使用限制
- 当前版本支持分区级MDL锁功能,包括DROP PARTITION操作、RANGE和LIST分区方式的ADD PARTITION操作。
- DROP PARTITION操作和ADD PARTITION操作仅支持inplace算法,不支持copy算法。
- 由于隔离级别可以设置为会话级别,如果transaction_isolation设置为REPEATABLE-READ或更高的隔离级别,在并发执行DDL过程中,可能会出现如下报错:
ERROR HY000: Table definition has changed, please retry transaction。
这是正常现象,因为事务访问到了DDL创建的新分区。可以通过重新执行事务来解决这个问题。
前提条件
- GaussDB(for MySQL)内核版本大于等于2.0.57.240900时可使用该功能。
- transaction_isolation参数的全局隔离级别需要设置为“READ-COMMITTED”。
开启分区级MDL锁
您可以通过“rds_partition_level_mdl_enabled”参数来开启分区级MDL锁功能。
参数名称 |
级别 |
描述 |
---|---|---|
rds_partition_level_mdl_enabled |
Global |
分区级MDL锁特性开关,默认值为OFF。 ON:开启分区级MDL锁特性。 OFF:关闭分区级MDL锁特性。
说明:
修改该参数需要重启。 |
使用示例
分区级MDL锁功能确保分区表的数据访问和分区维护操作互不阻塞。用户可以在不影响分区表业务流量的情况下,更灵活地进行分区维护。
以下是使用示例:
- 准备数据。
mysql> mysql> CREATE TABLE t1 ( c1 INTEGER NOT NULL PRIMARY KEY, c2 CHAR(10)) PARTITION BY RANGE (c1) ( -> PARTITION p0 VALUES LESS THAN (100), -> PARTITION p1 VALUES LESS THAN (200), -> PARTITION p2 VALUES LESS THAN (300), -> PARTITION p3 VALUES LESS THAN (400), -> PARTITION p4 VALUES LESS THAN (500)); Query OK, 0 rows affected (0.22 sec) mysql> INSERT INTO t1 VALUES(0,'abc'),(100,'abc'),(200,'abc'),(300,'abc'),(400,'abc'); Query OK, 5 rows affected (0.02 sec) Records: 5 Duplicates: 0 Warnings: 0
- 客户端1开启事务。
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM t1 WHERE c1 >= 300; +-----+------+ | c1 | c2 | +-----+------+ | 300 | abc | | 400 | abc | +-----+------+ 2 rows in set (0.00 sec)
- 客户端2添加新分区。
mysql> ALTER TABLE t1 ADD PARTITION (PARTITION p5 VALUES LESS THAN (600)); Query OK, 0 rows affected (0.21 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> INSERT INTO t1 VALUES(500,'abc'); Query OK, 1 row affected (0.00 sec)
- 客户端1在事务内能够看到新分区数据。
mysql> SELECT * FROM t1 WHERE c1 >= 300; +-----+------+ | c1 | c2 | +-----+------+ | 300 | abc | | 400 | abc | | 500 | abc | +-----+------+ 3 rows in set (0.00 sec)
- 客户端2删除旧分区。
mysql> ALTER TABLE t1 DROP PARTITION p0; Query OK, 0 rows affected (0.13 sec) Records: 0 Duplicates: 0 Warnings: 0
- 客户端1可以看到旧分区已不存在,新分区存在。
mysql> SHOW CREATE TABLE t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `c1` int NOT NULL, `c2` char(10) DEFAULT NULL, PRIMARY KEY (`c1`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci /*!50100 PARTITION BY RANGE (`c1`) (PARTITION p1 VALUES LESS THAN (200) ENGINE = InnoDB, PARTITION p2 VALUES LESS THAN (300) ENGINE = InnoDB, PARTITION p3 VALUES LESS THAN (400) ENGINE = InnoDB, PARTITION p4 VALUES LESS THAN (500) ENGINE = InnoDB, PARTITION p5 VALUES LESS THAN (600) ENGINE = InnoDB) */ 1 row in set (0.00 sec)
- 客户端1提交事务。
mysql> COMMIT; Query OK, 0 rows affected (0.01 sec)
分区级MDL锁功能通过降低DML和DDL过程中获取的锁粒度到分区上,以提升并发性能。在分区维护时,通过performance_schema.metadata_locks表可以查看分区级别的MDL锁获取情况。示例如下:
- 客户端1开启事务。
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM t1 WHERE c1 >= 500; +-----+------+ | c1 | c2 | +-----+------+ | 500 | abc | +-----+------+ 1 rows in set (0.00 sec)
- 客户端1查看MDL锁获取状态。
mysql> SELECT * FROM performance_schema.metadata_locks; +-------------------+--------------------+----------------+-----------------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------------+--------------------+----------------+-----------------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+ | TABLE | test | t1 | NULL | 140082560509056 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:8006 | 69 | 23 | | PARTITION | test | t1 | p5 | 140082560508384 | SHARED_READ | TRANSACTION | GRANTED | sql_lex.cc:5434 | 69 | 23 | | TABLE | performance_schema | metadata_locks | NULL | 140082560511936 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:8006 | 69 | 24 | +-------------------+--------------------+----------------+-----------------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+ 4 rows in set (0.01 sec)
客户端1的事务获得了t1表级的SHARED_READ锁和p5分区级别的SHARED_READ锁。其中,p5分区是实际需要访问分区,通过分区裁剪获得。
- 客户端2删除p5分区。
mysql> ALTER TABLE t1 DROP PARTITION p5;
客户端1正在访问p5分区,且已经获取p5分区级别的SHARED_READ锁,此时客户端2的删除p5分区会被阻塞等待。
- 确认客户端2删除p5分区操作处于阻塞等待中。
mysql> SHOW PROCESSLIST; +----+-----------------+-----------------+------+---------+-------+-------------------------------------------+----------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-----------------+-----------------+------+---------+-------+-------------------------------------------+----------------------------------+ | 5 | event_scheduler | localhost | NULL | Daemon | 33127 | Waiting on empty queue | NULL | | 13 | root | localhost:42926 | test | Query | 0 | init | SHOW PROCESSLIST | | 14 | root | localhost:42936 | test | Query | 180 | Waiting for table partition metadata lock | ALTER TABLE t1 DROP PARTITION p5 | | 15 | root | localhost:42938 | test | Sleep | 1542 | | NULL | +----+-----------------+-----------------+------+---------+-------+-------------------------------------------+----------------------------------+ 4 rows in set (0.00 sec)
- 通过客户端1提交事务后,客户端2删除p5分区的操作解除阻塞,执行成功。
mysql> COMMIT; Query OK, 0 rows affected (0.01 sec)
客户端2:
mysql> ALTER TABLE t1 DROP PARTITION p5; Query OK, 0 rows affected (1 min 2.48 sec) Records: 0 Duplicates: 0 Warnings: 0