文档

非阻塞DDL

更新时间:

PolarDB新增非阻塞DDL(Nonblock DDL)功能。Nonblock DDL规避了执行DDL过程中MDL锁长时间获取不成功导致的连接堆积和阻塞,有效地解决了DDL导致的锁表问题。

背景信息

用户在执行DDL操作的时候,若目标表存在未提交的长事务或大查询,DDL将持续等待获取MDL-X锁。在PolarDB中由于MDL-X锁具有最高优先级,DDL在等待MDL-X锁的过程中,将阻塞目标表上所有的新事务,这将导致业务连接的堆积和阻塞,可能会造成整个业务系统崩溃的严重后果。PolarDB提供的Nonblock DDL功能,可以保证即使在无法获得MDL-X锁的情况下,依然允许新事务进入目标表,从而保证整个业务系统的稳定。

前提条件

PolarDB MySQL版集群需满足如下条件之一:

  • PolarDB MySQL版8.0.1版本且Revision version为8.0.1.1.29及以上。

  • PolarDB MySQL版8.0.2版本且Revision version为8.0.2.2.12及以上。

您可以通过查询版本号来确认集群版本。

注意事项

开启Nonblock DDL功能会导致DDL的优先级降低,同时因MDL锁获取失败从而导致执行DDL的失败概率也会相应增大。

使用限制

  • PolarDB MySQL版8.0.1版本且Revision version为8.0.1.1.29及以上,以及8.0.2.2.12版本:

    • ALTER TABLE语句支持Nonblock DDL功能,应用于ALTER TABLE table_name ADD INDEX index_name ( `column1`, `column2`, `column3` )语句效果更佳。

    • 对于在InnoDB引擎上创建的表,需要使用ALTER TABLE table_name engine=innodb命令来代替 OPTIMIZE TABLE table_name 命令进行碎片整理。

  • PolarDB MySQL版8.0.2版本且Revision version为8.0.2.2.13及以上:

    ALTER TABLEOPTIMIZE TABLETRUNCATE TABLE语句支持Nonblock DDL功能。

使用方法

您可以先通过loose_polar_nonblock_ddl_mode参数开启Nonblock DDL功能,并通过loose_polar_nonblock_ddl_retry_times参数设置获取MDL-X锁超时重试的次数、loose_polar_nonblock_ddl_retry_interval参数设置获取MDL-X锁超时重试的时间间隔、loose_polar_nonblock_ddl_lock_wait_timeout参数设置获取MDL-X锁超时的时间。具体操作请参见设置集群参数和节点参数。参数说明如下:

参数

级别

说明

loose_polar_nonblock_ddl_mode

Session

Nonblock DDL功能开关。取值范围如下:

  • ON:开启Nonblock DDL功能。

  • OFF(默认):关闭Nonblock DDL功能。

loose_polar_nonblock_ddl_retry_times

Session

设置获取MDL-X锁超时重试的次数。取值范围:0~31536000。默认值为0(由参数lock_wait_timeout计算得到的值)。

说明

该参数值建议设置为4194304。

loose_polar_nonblock_ddl_retry_interval

Session

设置获取MDL-X锁超时重试的时间间隔。取值范围:1~31536000。单位为秒。默认值为6。

loose_polar_nonblock_ddl_lock_wait_timeout

Session

设置获取MDL-X锁超时的时间。取值范围:1~31536000。单位为秒。默认值为1。

性能对比

本文档对比了开启和关闭Nonblock DDL功能对业务的影响情况,同时与使用外部工具gh-ost进行表结构变更也进行了性能对比。

测试工具

SysBench是一个跨平台且支持多线程的模块化基准测试工具,用于评估系统在运行高负载的数据库时相关核心参数的性能表现。使用SysBench是为了绕过复杂的数据库基准设置,甚至在没有安装数据库的前提下,快速了解数据库系统的性能。关于如何使用SysBench,请参见性能测试方法(OLTP)

测试环境

一个规格为8核64 GB的标准版PolarDB MySQL版8.0版本的集群。

测试方法

  1. 使用SysBench创建1个测试表sbtest1,并插入1000000行数据。

    ./oltp_read_write.lua --mysql-host="集群地址" --mysql-port=“端口号” --mysql-user=“用户名” --mysql-password=“用户密码” --mysql-db="sbtest" --tables=1 --table-size=1000000 --report-interval=1 --percentile=99 --threads=8 --time=6000 prepare
  2. 通过SysBench中的oltp_read_write.lua模拟用户业务。

    ./oltp_read_write.lua --mysql-host="集群地址" --mysql-port=“端口号” --mysql-user=“用户名” --mysql-password=“用户密码” --mysql-db="sbtest" --tables=1 --table-size=1000000 --report-interval=1 --percentile=99 --threads=8 --time=6000 run
  3. 在目标表sbtest1上开启一个事务但不提交,该事务将持有目标表sbtest1的MDL锁。

    /* session 1 */
    begin;
    select * from sbtest1;
  4. 在新会话中,开启和关闭Nonblock DDL功能的条件下,分别对表sbtest1进行加列操作,观察TPS的变化情况。

    /* session 2 */
    alter table sbtest1 add column d int;
  5. 使用gh-ost工具进行加列操作,观察TPS的变化情况(开启Binlog)。

    ./gh-ost --assume-rbr --user="用户名" --password="用户密码" --host="集群地址" --port="端口号" --database="sbtest" --table="sbtest1"  --alter="ADD COLUMN d INT" --allow-on-master --aliyun-rds --initially-drop-old-table --initially-drop-ghost-table --execute;

测试结果

  • 关闭Nonblock DDL,TPS持续跌零。默认超时时间为31536000,严重影响用户业务。关闭Nonblock DDL

  • 开启Nonblock DDL,TPS周期性下降,但未跌零。对用户业务影响较小,能保证业务系统的稳定。开启Nonblock DDL

  • 使用gh-ost进行表结构无锁变更,TPS周期性跌零。对用户业务影响很大,这是cut-over阶段短暂锁表造成的。使用gh-ost进行结构变更

Nonblock DDL与gh-ost工具性能对比

开启Nonblock DDL并使用传统的加列方式(INSTANT、INPLACE、COPY)与使用gh-ost工具分别对100万行数据的表进行加列操作的性能对比如下:

  • 在无负载的情况下,通过下图可以看出开启Nonblock DDL并使用传统的加列方式(INSTANT、INPLACE、COPY)比使用gh-ost工具耗时更少。无负载

  • 使用SysBench的oltp_read_write模拟业务负载,通过下图可以看出开启Nonblock DDL并使用传统的加列方式(INSTANT、INPLACE、COPY)比使用gh-ost工具耗时更少。read_write.

测试结论

Nonblock DDL功能在DDL执行期间能更好的规避新事务阻塞、TPS跌零等问题,从而尽可能的保证业务系统的稳定性。同时,能够提供更高效的DDL变更能力。