操作场景
在MySQL社区版中,分区表的数据访问操作(DML)和分区维护操作(DDL)会互相阻塞,这意味着分区维护只能在业务低峰期进行。然而,创建和删除分区的操作相对频繁,这在很大程度上限制了分区表的使用。
TaurusDB实现了分区级别的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创建的新分区。可以通过重新执行事务来解决这个问题。
前提条件
- TaurusDB内核版本大于等于2.0.57.240900时可使用该功能。内核版本的查询方法请参见如何查看云数据库 TaurusDB实例的版本号。
- transaction_isolation参数的全局隔离级别需要设置为“READ-COMMITTED”。
开启分区级MDL锁
您可以通过“rds_partition_level_mdl_enabled”参数来开启分区级MDL锁功能。
表1 参数说明
|
参数名称 |
级别 |
描述 |
|
rds_partition_level_mdl_enabled |
Global |
分区级MDL锁特性开关,默认值为OFF。
ON:开启分区级MDL锁特性。
OFF:关闭分区级MDL锁特性。
|
示例1:分区表的数据访问和分区维护各自独立
分区级MDL锁功能确保分区表的数据访问和分区维护操作互不阻塞。用户可以在不影响分区表业务流量的情况下,更灵活地进行分区维护。
- 准备数据。
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)
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开启事务。
BEGIN;
Query OK, 0 rows affected (0.00 sec)
SELECT * FROM t1 WHERE c1 >= 300;
+-----+------+
| c1 | c2 |
+-----+------+
| 300 | abc |
| 400 | abc |
+-----+------+
2 rows in set (0.00 sec)
- 客户端2添加新分区。
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
INSERT INTO t1 VALUES(500,'abc');
Query OK, 1 row affected (0.00 sec)
- 客户端1在事务内能看到新分区数据。
SELECT * FROM t1 WHERE c1 >= 300;
+-----+------+
| c1 | c2 |
+-----+------+
| 300 | abc |
| 400 | abc |
| 500 | abc |
+-----+------+
3 rows in set (0.00 sec)
- 客户端2删除旧分区。
ALTER TABLE t1 DROP PARTITION p0;
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
- 客户端1可以看到旧分区已不存在,新分区存在。
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提交事务。
COMMIT;
Query OK, 0 rows affected (0.01 sec)
示例2:查看分区级别的MDL锁获取情况
分区级MDL锁功能通过降低DML和DDL过程中获取的锁粒度到分区上,以提升并发性能。在进行分区维护时,通过performance_schema.metadata_locks表可以查看分区级别的MDL锁获取情况。
- 客户端1开启事务。
BEGIN;
Query OK, 0 rows affected (0.00 sec)
SELECT * FROM t1 WHERE c1 >= 500;
+-----+------+
| c1 | c2 |
+-----+------+
| 500 | abc |
+-----+------+
1 rows in set (0.00 sec)
- 客户端1查看MDL锁获取状态。
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分区。
ALTER TABLE t1 DROP PARTITION p5;
客户端1正在访问p5分区,且已经获取p5分区级别的SHARED_READ锁,此时客户端2的删除p5分区会被阻塞等待。
- 确认客户端2删除p5分区操作处于阻塞等待中。
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分区的操作解除阻塞,执行成功。
客户端1:
COMMIT;
Query OK, 0 rows affected (0.01 sec)
客户端2:
ALTER TABLE t1 DROP PARTITION p5;
Query OK, 0 rows affected (1 min 2.48 sec)
Records: 0 Duplicates: 0 Warnings: 0