更新时间:2024-12-16 GMT+08:00
分享

分区级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.57.240900时可使用该功能。
  • transaction_isolation参数的全局隔离级别需要设置为“READ-COMMITTED”

开启分区级MDL锁

您可以通过“rds_partition_level_mdl_enabled”参数来开启分区级MDL锁功能。

表1 参数说明

参数名称

级别

描述

rds_partition_level_mdl_enabled

Global

分区级MDL锁特性开关,默认值为OFF。

ON:开启分区级MDL锁特性。

OFF:关闭分区级MDL锁特性。

说明:

修改该参数需要重启。

使用示例

分区级MDL锁功能确保分区表的数据访问和分区维护操作互不阻塞。用户可以在不影响分区表业务流量的情况下,更灵活地进行分区维护。

以下是使用示例:

  1. 准备数据。
    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
     
  2. 客户端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)
  3. 客户端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)
  4. 客户端1在事务内能够看到新分区数据。
    mysql> SELECT * FROM t1 WHERE c1 >= 300;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    | 300 | abc  |
    | 400 | abc  |
    | 500 | abc  |
    +-----+------+
    3 rows in set (0.00 sec)
  5. 客户端2删除旧分区。
    mysql> ALTER TABLE t1 DROP PARTITION p0;
    Query OK, 0 rows affected (0.13 sec)
    Records: 0  Duplicates: 0  Warnings: 0
  6. 客户端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)
  7. 客户端1提交事务。
    mysql> COMMIT;
    Query OK, 0 rows affected (0.01 sec) 

    分区级MDL锁功能通过降低DML和DDL过程中获取的锁粒度到分区上,以提升并发性能。在分区维护时,通过performance_schema.metadata_locks表可以查看分区级别的MDL锁获取情况。示例如下:

  8. 客户端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)
  9. 客户端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分区是实际需要访问分区,通过分区裁剪获得。

  10. 客户端2删除p5分区。
    mysql> ALTER TABLE t1 DROP PARTITION p5;

    客户端1正在访问p5分区,且已经获取p5分区级别的SHARED_READ锁,此时客户端2的删除p5分区会被阻塞等待。

  11. 确认客户端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)
     
  12. 通过客户端1提交事务后,客户端2删除p5分区的操作解除阻塞,执行成功。

    客户端1:

    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

相关文档