時系列テーブル(ログ、注文、履歴レコードなど)は、一方の端でデータが増加し、他方の端で古くなっていきます。そのため、頻繁なパーティションの追加(roll-in)および削除(roll-out)が必要となります。標準の MySQL では、すべてのパーティション DDL 操作が完了またはキャンセルされるまで、DML トラフィック全体がブロックされます。このため、メンテナンス作業はトラフィックが少ない時間帯に実施する必要があり、スループットがゼロとなる期間を容認しなければなりません。
オンラインパーティションメンテナンスは、この課題を、テーブルレベルのメタデータロック(MDL)をパーティションレベルの MDL に置き換えることで解決します。DDL 操作が特定のパーティションを対象とする場合、PolarDB for MySQL はそのパーティションのみにパーティションレベルの MDL を取得します。他のパーティションには、並列のデータ操作言語(DML)操作が引き続きアクセス可能です。これにより、DML のブラックアウト期間が解消され、任意のタイミングでパーティションメンテナンスを実行できます。
以下の図は、この機能が、並列実行されるデータ定義言語(DDL)および DML 操作におけるロック競合をどのように低減するかを示しています。

前提条件
開始する前に、以下の条件を満たしていることを確認してください。
PolarDB for MySQL 8.0 クラスターのリビジョンバージョンが 8.0.2.2.0 以降であること。バージョンの確認方法については、「エンジンバージョンの照会」をご参照ください。
partition_level_mdl_enabledパラメーターがONに設定されていること。設定手順については、「クラスターおよびノードパラメーターの指定」をご参照ください。transaction_isolationパラメーターがグローバルレベルでREAD-COMMITTEDに設定されていること。設定手順については、「クラスターおよびノードパラメーターの指定」をご参照ください。
パーティションレベル MDL の有効化
partition_level_mdl_enabled を ON に設定することで、パーティションレベル MDL を有効化します。このパラメーターはロックの粒度を制御します。DDL 操作時にテーブル全体をロックする代わりに、PolarDB for MySQL は影響を受けるパーティションのみをロックします。
| パラメーター | レベル | 説明 |
|---|---|---|
partition_level_mdl_enabled | グローバル | パーティションレベル MDL を有効化します。有効な値: ON(有効)、OFF(無効)。 |
このパラメーターを変更した後は、変更を有効にするためにクラスターを再起動する必要があります。
サポートされる操作
オンラインパーティションメンテナンスは、以下の DDL 操作に対応しています。ADD PARTITION 操作は、RANGE および LIST パーティション化テーブルでのみサポートされます。その他の DDL 操作およびパーティションタイプのサポートは、今後のリリースで提供される予定です。
| 操作 | 説明 |
|---|---|
ADD PARTITION | 新しいパーティションを追加(RANGE および LIST パーティション化のみ) |
DROP PARTITION | 既存のパーティションを削除 |
EXCHANGE PARTITION | パーティションと非パーティション化テーブル間でデータを交換 |
REBUILD PARTITION | パーティションをインプレースで再構築 |
REORGANIZE PARTITION | 既存のパーティションを分割またはマージ |
制限事項
分離レベルが REPEATABLE-READ 以上に設定されており、DDL 操作が並列で実行された場合、以下のエラーが発生することがあります。
ERROR HY000: Table definition has changed, please retry transactionこれは想定される動作です。エラーは、DML 文が、並列で実行中の DDL 操作によって直前に作成されたパーティションにアクセスしたために発生します。トランザクションを再実行することで、この問題を解決できます。
分離レベルは、グローバルレベルだけでなく、セッションレベルでも設定可能です。
使用例
以下の例では、2 つの並列クライアントを使用して、オンラインパーティションメンテナンスによって DML 操作と DDL 操作が共存できることを実証します。
-- クライアント 1:現在のテーブル構造を表示
SHOW CREATE TABLE tr\G
*************************** 1. row ***************************
Table: tr
Create Table: CREATE TABLE `tr` (
`id` int(11) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`purchased` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
/*!50100 PARTITION BY RANGE (year(`purchased`))
(PARTITION p0 VALUES LESS THAN (1990) ENGINE = InnoDB,
PARTITION p1 VALUES LESS THAN (1995) ENGINE = InnoDB,
PARTITION p2 VALUES LESS THAN (2000) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (2005) ENGINE = InnoDB,
PARTITION p4 VALUES LESS THAN (2010) ENGINE = InnoDB,
PARTITION p5 VALUES LESS THAN (2015) ENGINE = InnoDB) */
1 row in set (0.00 sec)
-- クライアント 1:トランザクションを開始し、データをクエリ
BEGIN;
Query OK, 0 rows affected (0.01 sec)
SELECT * FROM tr WHERE purchased >= '2010-01-01';
+------+----------------+------------+
| id | name | purchased |
+------+----------------+------------+
| 5 | exercise bike | 2014-05-09 |
| 7 | espresso maker | 2011-11-22 |
+------+----------------+------------+
2 rows in set (0.01 sec)
-- クライアント 2:クライアント 1 のトランザクションがオープン中に、新しいパーティションを追加し、データを挿入
ALTER TABLE tr ADD PARTITION (PARTITION p6 VALUES LESS THAN (2020));
INSERT INTO tr VALUES (11, 'hope', '2017-11-04'), (12, 'carmen', '2018-06-08');
-- クライアント 1:同一トランザクション内で再度クエリ — 新しいパーティションのデータが可視化されます
SELECT * FROM tr WHERE purchased >= '2010-01-01';
+------+----------------+------------+
| id | name | purchased |
+------+----------------+------------+
| 5 | exercise bike | 2014-05-09 |
| 7 | espresso maker | 2011-11-22 |
| 11 | hope | 2017-11-04 |
| 12 | carmen | 2018-06-08 |
+------+----------------+------------+
4 rows in set (0.00 sec)
-- クライアント 2:クライアント 1 のトランザクションがまだオープン中に、古いパーティションを削除
ALTER TABLE tr DROP PARTITION p0;
-- クライアント 1:テーブル構造が両方の変更(p6 の追加および p0 の削除)を反映していることを確認
SHOW CREATE TABLE tr\G
*************************** 1. row ***************************
Table: tr
Create Table: CREATE TABLE `tr` (
`id` int(11) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`purchased` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
/*!50100 PARTITION BY RANGE (year(`purchased`))
(PARTITION p1 VALUES LESS THAN (1995) ENGINE = InnoDB,
PARTITION p2 VALUES LESS THAN (2000) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (2005) ENGINE = InnoDB,
PARTITION p4 VALUES LESS THAN (2010) ENGINE = InnoDB,
PARTITION p5 VALUES LESS THAN (2015) ENGINE = InnoDB,
PARTITION p6 VALUES LESS THAN (2020) ENGINE = InnoDB) */
1 row in set (0.00 sec)
-- クライアント 1:トランザクションをコミット
COMMIT;パフォーマンス比較
以下の 2 つのシナリオでは、オンラインパーティションメンテナンスの有無における DML スループットを比較します。
シナリオ 1:長時間実行中のトランザクションによる DDL のブロック
標準の MySQL では、DDL 操作がオープン中のトランザクションが保持するロックのために進行できない場合、その DDL 操作はその後続のすべての DML 操作をブロックします。これにより、DDL がキャンセルされるか、トランザクションがコミットされるまで、スループットがゼロに低下します。
オンラインパーティションメンテナンスを有効化した場合:
通常時のスループットは、機能が無効化された場合と同一です。この機能を有効化してもオーバーヘッドは発生しません。
長時間実行中のトランザクションが、パーティション DDL 操作をブロックしなくなります。DML トラフィックは、全体を通して安定したままです。

シナリオ 2:実行に時間がかかる DDL 操作
DDL 操作が遅い場合(例:大規模なパーティションの再構築)は、ブロッキングトランザクションが関与していない場合でも、DML スループットが著しくジッターを起こす可能性があります。
オンラインパーティションメンテナンスを有効化した場合、実行に時間がかかる DDL 操作が DML スループットに与える影響は極めて小さくなります。

MDL ステータスの確認
DDL 操作が実行中の場合、performance_schema.metadata_locks をクエリすることで、パーティションレベルのロック状態を確認できます。
以下の例では、クライアント 1 がパーティション p5 に対して読み取りロックを保持しており、クライアント 2 がそのパーティションを削除しようとしている際のロックテーブルを示しています。
-- クライアント 1:トランザクションを開始し、パーティション p5 をクエリ
BEGIN;
SELECT * FROM tr WHERE purchased >= '2010-01-01';
+------+----------------+------------+
| id | name | purchased |
+------+----------------+------------+
| 5 | exercise bike | 2014-05-09 |
| 7 | espresso maker | 2011-11-22 |
+------+----------------+------------+
2 rows in set (0.01 sec)
-- クライアント 1:ロックテーブルを確認
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 | tr | NULL | 140734887898944 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6759 | 67 | 17 |
| PARTITION | test | tr | p5 | 140734887896704 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6502 | 67 | 17 |
| TABLE | performance_schema | metadata_locks | NULL | 140734879511488 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6759 | 68 | 4 |
| SCHEMA | performance_schema | NULL | NULL | 140734879511648 | INTENTION_EXCLUSIVE | TRANSACTION | GRANTED | dd_schema.cc:108 | 68 | 4 |
+-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+
4 rows in set (0.02 sec)出力結果には、スレッド 67(クライアント 1)が保持する 2 つのロックが表示されています。1 つはテーブルレベルの SHARED_READ ロック(tr)、もう 1 つはパーティションプルーニング後のパーティションレベルの SHARED_READ ロック(p5)です。OWNER_THREAD_ID 列は、どのスレッドがロックを保持しているかを識別します。
-- クライアント 2:パーティション p5 の削除を試行 — クライアント 1 が依然として p5 に対する SHARED_READ ロックを保持しているため、待機状態になります
ALTER TABLE tr DROP PARTITION p5;
-- DDL がパーティションレベル MDL を待機中であることを確認
SHOW PROCESSLIST;
+----+-----------------+-----------+------+---------+------+-------------------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+------+-------------------------------------+----------------------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 1550 | Waiting on empty queue | NULL |
| 8 | root | localhost | test | Sleep | 426 | | NULL |
| 9 | root | localhost | NULL | Query | 0 | starting | SHOW PROCESSLIST |
| 10 | root | localhost | test | Query | 10 | Waiting for partition metadata lock | ALTER TABLE tr DROP PARTITION p5 |
+----+-----------------+-----------+------+---------+------+-------------------------------------+----------------------------------+
4 rows in set (0.00 sec)State 列には、DDL がパーティションレベルのロックの後にキューに入れられた際に Waiting for partition metadata lock と表示されます。DDL をアンブロックするには、ブロッキングセッションのトランザクションをコミットまたはロールバックします。OWNER_THREAD_ID を metadata_locks クエリの結果から特定し、該当するセッションのトランザクションをコミットまたはロールバックしてください。
-- クライアント 1 のコミット後に、クライアント 2 の DROP PARTITION が自動的に実行されますオンラインパーティションメンテナンス操作の追跡
Online_altered_partition ステータス変数を使用すると、実行されたオンラインパーティションメンテナンス操作の回数を確認できます。
SHOW STATUS LIKE 'Online_altered_partition';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| Online_altered_partition | 2565 |
+--------------------------+-------+
1 row in set (0.00 sec)