ホット行 (ホットスポット行) は、データベース内で頻繁に変更されるデータ行です。高い同時実行性のシナリオでは、ホット行を更新すると、深刻な行ロックの競合と長い待機時間が発生し、システムパフォーマンスが低下します。この問題に対処するため、PolarDB はデータベースカーネルレベルで革新的な最適化を提供し、システムパフォーマンスを大幅に向上させます。
概要
ホット行には、次の課題があります:
トランザクションがデータ行を更新すると、トランザクションがコミットまたはロールバックされるまでその行をロックします。この間、1 つのトランザクションのみが行を更新でき、他のトランザクションは待機する必要があります。これは、単一のホット行に対する更新リクエストが順次実行されることを意味します。従来のデータベースおよびテーブルシャーディング戦略では、このシナリオでのパフォーマンス向上は限定的です。
E コマースでは、購入制限やタイムセールなどのプロモーションが一般的です。これらのイベントにより、非常に短い期間にホット行に対する多くの更新リクエストがデータベースシステムに到着する可能性があります。これにより、深刻な行ロックの競合と長い待機時間が発生し、システムパフォーマンスが低下します。更新リクエストが長時間待機する必要がある場合、ビジネスに大きな影響を与える可能性があります。
ハードウェアを改善するだけでは、これらの低レイテンシーの要求を満たすことはできません。そのため、PolarDB はデータベースカーネルレベルで革新的な最適化を提供します。システムはホット行の更新リクエストを自動的に識別し、特定の時間間隔内に発生する同じデータ行に対する更新操作をグループ化します。これらのグループは、パイプラインを使用して並列処理されます。これらの最適化により、システムパフォーマンスが大幅に向上します。
技術的なソリューション
シーケンシャル処理からパイプライン処理へ
並列処理は、データベースシステムのパフォーマンスを向上させる直接的な方法です。しかし、同じホット行に対する更新操作を完全に並列化することは困難です。PolarDB は、革新的なパイプライン処理メソッドを使用して、ホット行の更新操作の並列性を最大化します。
ホット行の更新のための SQL 文は、
autocommitまたは COMMIT_ON_SUCCESS でマークされます。最適化された MySQL カーネルレイヤーは、これらのマークが付いた更新操作を自動的に検出します。特定の時間間隔内に、更新操作を収集し、プライマリキーまたはunique keyに基づいてバケットにハッシュします。同じバケットにハッシュされた更新操作は、到着した順にグループ化されて処理のために送信されます。これらの更新操作をパイプラインを使用して処理するために、2 つの実行ユニットがそれらをグループ化するために使用されます。最初のグループが収集され、コミットの準備ができたとき、2 番目のグループはすぐに更新操作の収集を開始します。2 番目のグループが収集され、コミットの準備ができたとき、最初のグループはすでにコミットされており、新しいバッチの更新操作の収集を開始します。2 つのグループは交互に、並列で実行されます。
マルチコア CPU は現在非常に一般的です。このパイプライン処理メソッドは、ハードウェアリソースを最大限に活用し、CPU 使用率を高め、データベースシステムの並列処理能力を向上させることができます。これにより、データベースシステムのスループットが最大化されます。
行ロック要求時の待機を排除
データ整合性を確保するために、データ行が更新されるときにロックがかけられます。ロックリクエストがすぐに許可されない場合、リクエストは待機状態になります。これは処理レイテンシーを増加させるだけでなく、デッドロック検出をトリガーし、余分なリソースを消費します。
前述のように、同じデータ行に対する更新操作は時系列でグループ化されます。グループ内の最初の更新操作は Leader です。ターゲットデータ行を読み取り、ロックします。後続の更新操作は Follower です。Follower がターゲットデータ行のロックを要求したとき、Leader がすでにその行のロックを保持していることを見つけた場合、待機することなくすぐにロックを取得できます。
この最適化により、ロックリクエストの数と行ロックの時間的オーバーヘッドが削減されます。データベースシステム全体のパフォーマンスが大幅に向上します。
B-tree インデックスの走査を削減
MySQL は B-tree インデックスを使用してデータを管理します。各クエリは、ターゲットデータ行を見つけるためにインデックスを走査する必要があります。データテーブルが大きく、インデックスレベルが多いほど、走査に時間がかかります。
前述のグループ化メカニズムでは、各グループの Leader のみがインデックスを走査してデータ行を特定します。その後、更新されたデータ行はメモリ (Row Cache) にキャッシュされます。同じグループの Follower がロックを正常に取得した後、インデックスを再度走査することなく、メモリからターゲットデータ行を直接取得できます。
これにより、インデックス走査の総数と時間的オーバーヘッドが削減されます。
前提条件
お使いの PolarDB クラスターは、次のいずれかのバージョンを実行している必要があります:
PolarDB for MySQL 5.6、マイナーエンジンバージョンが 20200601 以降。
MySQL 5.7、マイナーエンジンバージョンが 5.7.1.0.17 以降。
MySQL 8.0、マイナーエンジンバージョンが 8.0.1.1.10 以降。
バイナリロギングが有効になっていること。
クラスターパラメーター [rds_ic_reduce_hint_enable] は無効です。
MySQL 5.6 および MySQL 8.0 の場合、クラスターパラメーターはデフォルトで無効になっています。
PolarDB for MySQL 5.7 の場合、このパラメーターはデフォルトで有効になっています。ホット行の最適化機能を有効にする前に、パラメーター値を変更して OFF にする必要があります。
説明MySQL 構成ファイルとの互換性のため、PolarDBコンソール内のすべてのクラスターパラメーターには loose_ というプレフィックスが付きます。PolarDBコンソールで [rds_ic_reduce_hint_enable] パラメーターを変更するには、loose_ プレフィックスが付いたパラメーター [loose_rds_ic_reduce_hint_enable] を選択する必要があります。
制限事項
ホット行の最適化機能は、次のシナリオでは使用されません:
ホット行を含むテーブルがパーティションテーブルである場合。
ホット行を含むテーブルにトリガーが定義されている場合。
ホット行に対してステートメントキューが使用されている場合。
グローバルバイナリロギングが有効で、セッションレベルのバイナリロギングが無効な場合、
UPDATE文はホット行の最適化機能を使用しません。
使用方法
ホット行の最適化機能を有効にします。
PolarDBコンソールで、クラスターとノードのパラメーターを構成して、ホット行の最適化機能を有効または無効にできます。
パラメーター
説明
hotspot
ホット行の最適化機能のスイッチ。有効な値:
ON: 有効。
OFF (デフォルト): 無効。
説明MySQL 構成ファイルとの互換性を確保するため、PolarDBコンソールのすべてのクラスターパラメーターには loose_ というプレフィックスが付いています。PolarDBコンソールで [hotspot] パラメーターを変更するには、loose_ プレフィックスが付いたパラメーターである [loose_hotspot] を選択する必要があります。
ヒント構文を使用してホット行の最適化機能を有効にします。
ヒント
必須
説明
必須
更新が成功した場合にトランザクションをコミットします。
オプション
更新が失敗した場合にトランザクションをロールバックします。
オプション
リクエストが 1 行のみを更新することを明示的に指定します。この条件が満たされない場合、更新は失敗します。
説明ヒントはトランザクションを自動的にコミットするため、トランザクションの最後の SQL 文に含める必要があります。
例:
sbtestテーブルのc列の値を更新します。UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;
関連操作
カスタムパラメーター構成
PolarDBコンソールでは、次のパラメーターを変更できません。変更するには、Quota Center に移動します。[PolarDB Hotspot Row Parameter Adjustment] という名前のクォータを見つけ、[アクション] 列の [適用] をクリックします。
パラメーター | 説明 |
hotspot_for_autocommit |
|
hotspot_update_max_wait_time | グループ更新中に Leader が Follower のグループ参加を待つ待機時間。
|
hotspot_lock_type | グループ更新中に新しいタイプの行ロックを使用するかどうかを指定します。有効な値:
説明
|
パラメーター構成の表示
次のコマンドを実行して、ホット行の最適化機能のパラメーター構成を表示します。
SHOW variables LIKE "hotspot%";結果の例:
+------------------------------+-------+
|Variable_name | Value |
+------------------------------+-------+
|hotspot | OFF |
|hotspot_for_autocommit | OFF |
|hotspot_lock_type | OFF |
|hotspot_update_max_wait_time | 100 |
+------------------------------+-------+使用状況の表示
次のコマンドを実行して、ホット行の最適化機能の使用状況を表示します。
SHOW GLOBAL status LIKE 'Group_update%';パフォーマンステスト
テストツール
Sysbench は、オープンソースのクロスプラットフォームパフォーマンステストツールです。主に MySQL などのデータベースベンチマークや、CPU、メモリ、I/O、スレッドなどのコンポーネントのシステムパフォーマンステストに使用されます。マルチスレッドテストをサポートし、Lua スクリプトを使用してテストロジックを柔軟に制御します。データベースのパフォーマンス評価やストレステストなどのシナリオに適しています。
テストデータテーブルとテストステートメント
テーブル定義
CREATE TABLE sbtest (id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id));テストステートメント
UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;
テスト結果
PolarDB for MySQL 5.6
テストシナリオ
1 つのホット行と 8 コア CPU。
テスト結果
1 つのホット行と 8 コア CPU のシナリオでは、ホット行の最適化機能を有効にすると、高い同時実行性下での在庫ホットスポットのパフォーマンスが約 50 倍向上します。
テストデータ (QPS)

同時実行性 | 1 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
ホット行の最適化が無効 | 1365.31 | 1863.94 | 1866.6 | 1862.64 | 1867.32 | 1832.51 | 1838.31 | 1819.52 | 1833.2 |
ホット行の最適化が有効 | 1114.79 | 7000.19 | 12717.32 | 22029.48 | 43096.06 | 61349.7 | 83098.69 | 90860.94 | 87689 |
PolarDB for MySQL 5.7
テストシナリオ
1 つのホット行と 8 コア CPU。
テスト結果
1 つのホット行と 8 コア CPU のシナリオでは、ホット行の最適化機能を有効にすると、高い同時実行性下での在庫ホットスポットのパフォーマンスが約 35 倍向上します。
テストデータ
QPS

同時実行性 | 1 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
ホット行の最適化が無効 | 1348.49 | 1892.29 | 1889.77 | 1895.86 | 1875.2 | 1850.26 | 1843.62 | 1849.92 | 1835.68 |
ホット行の最適化が有効 | 1104.9 | 6886.89 | 12485.17 | 16003.23 | 16460.31 | 16548.86 | 27920.89 | 47893.96 | 66500.92 |
lat95th (95 パーセンタイルレイテンシー)

同時実行性 | 1 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
ホット行の最適化が無効 | 0.9 | 5.47 | 9.91 | 18.95 | 36.89 | 73.13 | 164.45 | 297.92 | 590.56 |
ホット行の最適化が有効 | 1.08 | 1.44 | 1.58 | 3.25 | 5.28 | 9.56 | 12.08 | 13.22 | 18.28 |
PolarDB for MySQL 8.0
テストシナリオ
1 つのホット行と 8 コア CPU。
テスト結果
1 つのホット行と 8 コア CPU のシナリオでは、ホット行の最適化機能を有効にすると、高い同時実行性下での在庫ホットスポットのパフォーマンスが約 26 倍向上します。
テストデータ
QPS

同時実行性 | 1 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
ホット行の最適化が無効 | 1559.14 | 2103.82 | 2116.4 | 2082.1 | 2079.74 | 2031.64 | 1993.09 | 1977.6 | 1983.61 |
ホット行の最適化が有効 | 1237.28 | 7443.04 | 12244.19 | 15529.52 | 23041.15 | 33931.18 | 53924.24 | 54598.6 | 50988.22 |
lat95th (95 パーセンタイルレイテンシー)

同時実行性 | 1 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
ホット行の最適化が無効 | 0.8 | 5 | 8.9 | 17.32 | 33.12 | 66.84 | 153.02 | 287.38 | 549.52 |
ホット行の最適化が有効 | 0.97 | 1.34 | 1.89 | 3.19 | 4.82 | 5.88 | 7.17 | 13.46 | 28.16 |
付録: パフォーマンステストの手順
ECS インスタンスを準備し、Sysbench をインストールします。
次の
oltp_inventory.luaを Sysbench ソースコードのsrc/luaフォルダに配置します。#!/usr/bin/env sysbench -- 在庫ホットスポットのパフォーマンスをテストするためのものです sysbench.cmdline.options= { inventory_hotspot = {"ali 在庫ホットスポットを有効にする", 'off'}, tables = {"テーブル数", 1}, table_size = {"テーブルサイズ", 1}, oltp_skip_trx = {'trx をスキップ', true}, hotspot_rows = {'ホットスポット行数', 1} } function cleanup() drv = sysbench.sql.driver() con = drv:connect() for i = 1, sysbench.opt.tables do print(string.format("テーブル sbtest%d をドロップしています...", i)) drop_table(drv, con, i) end end function drop_table(drv, con, table_id) local query query = string.format("drop table if exists sbtest%d ", table_id) con:query(query) end function create_table(drv, con, table_id) local query query = string.format("CREATE TABLE sbtest%d (id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id))", table_id) con:query(query) for i=1, sysbench.opt.table_size do con:query("INSERT INTO sbtest" .. table_id .. "(id, c) values (" ..i.. ", 1)") end end function prepare() drv = sysbench.sql.driver() con = drv:connect() for i = 1, sysbench.opt.tables do print(string.format("テーブル sbtest%d を作成しています...", i)) create_table(drv, con, i) end end function thread_init() drv = sysbench.sql.driver() con = drv:connect() begin_query = 'BEGIN' commit_query = 'COMMIT' end function event() local table_name table_name = "sbtest" .. sysbench.rand.uniform(1, sysbench.opt.tables) local min_line = math.min(sysbench.opt.table_size, sysbench.opt.hotspot_rows) local row_id = sysbench.rand.uniform(1, min_line) if not sysbench.opt.oltp_skip_trx then con:query(begin_query) end if (sysbench.opt.inventory_hotspot == "on") then con:query("UPDATE COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW 1 " .. table_name .. " SET c=c+1 WHERE id =" .. row_id) else con:query("UPDATE " .. table_name .. " SET c=c+1 WHERE id = " .. row_id) end if not sysbench.opt.oltp_skip_trx then if (sysbench.opt.inventory_hotspot == "on") then con:query(commit_query) end end end function thread_done() con:disconnect() endSysbench テストを実行します。
データを準備します。
sysbench --hotspot_rows=1 --histogram=on --mysql-user=<user> --inventory_hotspot=on --mysql-host=<host> --threads=1 --report-interval=1 --mysql-password=<password> --tables=1 --table-size=1 --oltp_skip_trx=true --db-driver=mysql --percentile=95 --time=300 --mysql-port=<port> --events=0 --mysql-db=<database> oltp_inventory prepareテストを実行します。
sysbench --db-driver=mysql --mysql-host=<host> --mysql-port=<port> --mysql-user=<user> --mysql-password=<password> --mysql-db=<database> --range-selects=0 --table_size=25000 --tables=250 --events=0 --time=600 --rand-type=uniform --threads=<threads> oltp_read_only run
入力パラメーター
パラメーター
説明
mysql-host
クラスターエンドポイント。
mysql-port
クラスターエンドポイントのポート。
mysql-user
クラスターのユーザー名。
mysql-password
クラスターユーザーのパスワード。
mysql-db
データベース名。
出力パラメーター
パラメーター
表示内容
説明
tables
データテーブルの数
テストで使用されるデータテーブルの総数。
table_size
データテーブルの行数
各テーブルのレコード数。
データサイズ
テーブル内のデータのサイズ。MB や GB などのストレージ単位で表示されます。
threads
同時実行スレッド数
構成されたスレッドの数。
スレッドステータス
スレッドのリアルタイム実行ステータス。