
# 分区级MDL锁
#### 操作场景
在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.60.241200时可使用该功能。内核版本的查询方法请参见[如何查看云数据库 TaurusDB实例的版本号](https://support.huaweicloud.com/taurusdb_faq/taurusdb_faq_0141.html)。
- transaction_isolation参数的全局隔离级别需要设置为"READ-COMMITTED"。
 
#### 开启分区级MDL锁
您可以通过"rds_partition_level_mdl_enabled"参数来开启分区级MDL锁功能。如需使用，请[提交工单](https://console.huaweicloud.com/ticket/?locale=zh-cn#/ticketindex/createIndex)，联系客服开启分区级MDL锁功能。
表1参数说明 
| **参数名称**                            | **级别** | **描述**                                                                                                                                                                                                                                                             |
|:---|:---|:---|
| **rds_partition_level_mdl_enabled** | Global | 分区级MDL锁特性开关，默认值为OFF。 ON：开启分区级MDL锁特性。 OFF：关闭分区级MDL锁特性。 说明： 修改该参数需要重启。 |
   
#### 使用示例
#### 示例1：分区表的数据访问和分区维护各自独立
分区级MDL锁功能确保分区表的数据访问和分区维护操作互不阻塞。用户可以在不影响分区表业务流量的情况下，更灵活地进行分区维护。
1. 准备数据。
   ```
   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
   ```
   
2. 客户端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)
   ```
   
3. 客户端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)
   ```
   
4. 客户端1在事务内能看到新分区数据。
   ```
   SELECT * FROM t1 WHERE c1 >= 300;
   +-----+------+
   | c1  | c2   |
   +-----+------+
   | 300 | abc  |
   | 400 | abc  |
   | 500 | abc  |
   +-----+------+
   3 rows in set (0.00 sec)
   ```
   
5. 客户端2删除旧分区。
   ```
   ALTER TABLE t1 DROP PARTITION p0;
   Query OK, 0 rows affected (0.13 sec)
   Records: 0  Duplicates: 0  Warnings: 0
   ```
   
6. 客户端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)
   ```
   
7. 客户端1提交事务。
   ```
   COMMIT;
   Query OK, 0 rows affected (0.01 sec)
   ```
   
 
#### 示例2：查看分区级别的MDL锁获取情况
分区级MDL锁功能通过降低DML和DDL过程中获取的锁粒度到分区上，以提升并发性能。在进行分区维护时，通过performance_schema.metadata_locks表可以查看分区级别的MDL锁获取情况。
1. 客户端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)
   ```
   
2. 客户端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分区是实际需要访问分区，通过分区裁剪获得。
   
3. 客户端2删除p5分区。
   ```
   ALTER TABLE t1 DROP PARTITION p5;
   ```
   客户端1正在访问p5分区，且已经获取p5分区级别的SHARED_READ锁，此时客户端2的删除p5分区会被阻塞等待。
   
4. 确认客户端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)
   ```
   
5. 通过客户端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
   ```
   
 
