すべてのプロダクト
Search
ドキュメントセンター

PolarDB:ホット行のパフォーマンス最適化

最終更新日:Apr 09, 2026

ホット行とは、頻繁に変更されるデータベースの行のことです。高い同時実行性が求められるシナリオでは、ホット行の更新によって深刻な行ロック競合と長い待機時間が発生し、システムパフォーマンスが低下します。PolarDB はこの問題に対処するため、データベースカーネル層で最適化を実装し、システムパフォーマンスを大幅に向上させます。

背景情報

ホット行は、以下のような課題をもたらします。

  • トランザクションがデータ行を更新する際、対象の行にロックを取得し、トランザクションがコミットまたはロールバックされるまでそのロックを解放しません。この間、1 つのトランザクションしか行を更新できず、他のトランザクションは待機する必要があります。これは、単一のホット行に対する更新リクエストが直列に実行されることを意味します。従来のデータベースやテーブルシャーディング戦略では、このシナリオにおけるパフォーマンス向上は限定的です。

  • このようなシナリオでは、ホット行に対する大量の更新リクエストが短時間でバックエンドデータベースシステムに到達する可能性があります。これにより、深刻な行ロック競合と長い待機時間が発生し、システムパフォーマンスが低下します。更新リクエストの待機時間が長くなると、ビジネス運用に大きな影響を与える可能性があります。

ハードウェアをスケールアップするだけでは、このような低レイテンシーの要件を満たすことはできません。この問題に対処するため、PolarDB はデータベースカーネル層で革新的な最適化を提供します。システムはホット行の更新リクエストを自動的に識別し、特定の時間間隔内に発生した同じデータ行への更新をグループ化します。その後、異なるグループがパイプラインを使用して並列処理されます。これらの最適化により、システムパフォーマンスが大幅に向上します。

技術的ソリューション

  • シーケンシャル処理からパイプライン処理へ

    並列処理はデータベースのパフォーマンスを向上させる最も直接的な方法ですが、同じホット行に対する更新操作を完全に並列化することは困難です。PolarDB は、革新的なパイプライン処理アプローチを使用して、ホット行の更新操作の並列性を最大限に高めます。

    ホット行の更新操作に使用される SQL ステートメントは、autocommit または COMMIT_ON_SUCCESS でマークされます。最適化された MySQL カーネル層は、これらのマークが付いた更新操作を自動的に識別します。一定の時間間隔内で、収集された更新操作に対してプライマリキーまたは一意キーに基づいて Hash を実行します。Hash によって同じバケットにマッピングされた更新操作については、カーネルは到着順に従ってそれらをグループ化し、バッチでコミットします。

    これらの更新操作をパイプラインで処理するために、2 つの実行ユニットがグループ化に使用されます。最初のグループが収集されコミットの準備ができた時点で、2 番目のグループはすぐに更新操作の収集を開始します。2 番目のグループが収集されコミットの準備ができたときには、最初のグループはすでにコミットされており、新しいバッチの更新操作の収集を開始します。2 つのグループは交互に並列実行されます。

    このパイプライン処理モデルは、ハードウェアリソースを最大限に活用し、CPU 使用率を高め、システムの並列処理能力を向上させることで、スループットを最大化します。

  • 行ロック要求時の待機を排除

    論理的なデータ整合性を確保するため、更新はまず対象のデータ行のロックを取得します。ロック要求がすぐに許可されない場合、リクエストは待機状態になります。これは処理レイテンシーを増加させるだけでなく、デッドロック検出をトリガーし、余分なオーバーヘッドを追加します。

    前述のように、同じデータ行に対する更新操作は時系列でグループ化されます。グループ内の最初の更新操作がリーダーです。リーダーは対象のデータ行を読み取り、ロックします。後続の更新操作はフォロワーです。フォロワーが対象データ行のロックを要求し、リーダーがすでに行ロックを保持していることを見つけると、待機することなくすぐにロックを取得できます。

    この最適化により、行ロックの取得回数とそれに関連する時間的オーバーヘッドが削減されます。結果として、システム全体のパフォーマンスが大幅に向上します。

  • B-tree インデックスの走査を削減

    MySQL は B-tree インデックスを使用してデータを管理します。各クエリは、対象のデータ行を見つけるためにインデックスを走査する必要があります。データテーブルが大きく、インデックスが深いほど、走査にかかる時間は長くなります。

    前述のグループ化メカニズムでは、各グループのリーダーのみがインデックスを走査してデータ行を特定し、更新された行をメモリ (行キャッシュ) にキャッシュします。同じグループ内のフォロワーは、ロックを正常に取得した後、インデックスを再度走査することなく、メモリから直接対象のデータ行を読み取ります。

    これにより、インデックスの総走査回数とそれに関連するオーバーヘッドが削減されます。

前提条件

  • ご利用の PolarDB クラスターは、次のいずれかのバージョンを実行している必要があります。

    • PolarDB for MySQL 5.6、マイナーエンジンバージョン 20200601 以降。

    • PolarDB for MySQL 5.7、マイナーエンジンバージョン 5.7.1.0.17 以降。

    • PolarDB for MySQL 8.0、マイナーエンジンバージョン 8.0.1.1.10 以降。

  • binlog が有効になっていること。

  • クラスターパラメーター rds_ic_reduce_hint_enable が無効になっている必要があります。

    • PolarDB for MySQL 5.6 および PolarDB for 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 を選択する必要があります。

制限事項

ホット行の最適化は、以下のシナリオでは適用されません。

  • ホット行を含むテーブルがパーティションテーブルである場合。

  • ホット行を含むテーブルにトリガーが定義されている場合。

  • ホット行がステートメントキューメカニズムを使用している場合。

  • グローバル binlog が有効で、セッションレベルの binlog が無効な場合、ホット行の最適化は UPDATE 文には適用されません。

  • ホット行の最適化を有効にすると、ON UPDATE CURRENT_TIMESTAMP 属性のみに依存して自動更新されるカラムは、自動更新されなくなります。UPDATE 文で SET column_name = CURRENT_TIMESTAMP(n) を使用して、これらのカラムに明示的に値を割り当てる必要があります。

使用方法

  1. ホット行の最適化を有効にします。

    PolarDBコンソールで、以下のパラメーターを変更して、ホット行の最適化を有効または無効にできます。

    パラメーター

    説明

    hotspot

    ホット行の最適化機能を有効にするかどうかを指定します。有効な値:

    1. ON:有効。

    2. OFF (デフォルト):無効。

    説明

    MySQL の設定ファイルとの互換性を確保するため、PolarDBコンソールでは、すべてのクラスターパラメーターにプレフィックス loose_ が付きます。PolarDBコンソールhotspot パラメーターを変更するには、loose_ プレフィックスが付いたパラメーター、つまり loose_hotspot を選択します。

  2. ヒント構文を使用してホット行の最適化を適用します。

    ヒント

    必須

    説明

    COMMIT_ON_SUCCESS

    必須

    更新が成功した場合にトランザクションをコミットします。

    ROLLBACK_ON_FAIL

    オプション

    更新が失敗した場合にトランザクションをロールバックします。

    TARGET_AFFECT_ROW(1)

    オプション

    リクエストが 1 行のみを更新することを期待していることを指定します。この条件が満たされない場合、更新は失敗します。

    説明

    このヒントはトランザクションを自動的にコミットするため、トランザクションの最後の SQL ステートメントに含める必要があります。

    例:sbtest テーブルの c カラムの値を更新します。

    UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;

関連操作

カスタムパラメーター

PolarDBコンソールでは、以下のパラメーターを変更できません。変更するには、クォータセンターに移動し、クォータ ID polardb_mysql_hotspot を見つけて、操作列の 申請をクリックします。

パラメーター

説明

hotspot_for_autocommit

autocommit モードの UPDATE 文に対してホット行の最適化を有効にするかどうかを指定します。有効な値:

  • ON:有効。

  • OFF (デフォルト):無効。

hotspot_update_max_wait_time

グループ更新中にリーダーがフォロワーの参加を待つ最大時間。

  • 単位:マイクロ秒 (us)。

  • デフォルト値:100。

hotspot_lock_type

グループ更新のために新しい行ロックタイプを有効にするかどうかを指定します。有効な値:

  • ON:有効。

  • OFF (デフォルト):無効。

説明
  • このパラメーターを有効にすると、更新操作が同じホット行の行ロックを要求したときに、待機することなくすぐにロックを取得できます。これにより、パフォーマンスが向上します。

  • 新しいロックタイプは、待機せずにすぐに取得できるロックです。

パラメーター設定

次のコマンドを実行して、ホット行の最適化に関するパラメーター設定を表示します。

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

テストシナリオ

8 コア CPU 上の単一ホット行。

テスト結果

このテストシナリオでは、ホット行の最適化を有効にすると、高い同時実行性下で在庫関連のホット行の更新パフォーマンスが約 50 倍向上します。

テストデータ (QPS)

image.png

同時実行性

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

テストシナリオ

8 コア CPU 上の単一ホット行。

テスト結果

このテストシナリオでは、ホット行の最適化を有効にすると、高い同時実行性下で在庫関連のホット行の更新パフォーマンスが約 35 倍向上します。

テストデータ

QPS

image.png

同時実行性

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

95 パーセンタイルレイテンシー

image

同時実行性

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

テストシナリオ

8 コア CPU 上の単一ホット行。

テスト結果

このテストシナリオでは、ホット行の最適化を有効にすると、高い同時実行性下で在庫関連のホット行の更新パフォーマンスが約 26 倍向上します。

テストデータ

QPS

image

同時実行性

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

95 パーセンタイルレイテンシー

image

並行性

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

性能テストの手順

  1. ECS インスタンスを準備し、Sysbench をインストールします。

  2. 次の 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()
    end
  3. コマンドラインを使用してクラスターに接続します

  4. Sysbench テストを実行します。

    1. データを準備します。

      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
    2. テストを実行します。

      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_inventory run

    入力パラメーター

    パラメーター

    説明

    mysql-host

    クラスターエンドポイント。

    mysql-port

    クラスターエンドポイントのポート。

    mysql-user

    データベースアカウントのユーザー名。

    mysql-password

    データベースアカウントのパスワード。

    mysql-db

    データベース名。

    出力パラメーター

    パラメーター

    メトリック

    説明

    tables

    テーブル数

    テストで使用されるテーブルの総数。

    table_size

    テーブルごとの行数

    各テーブルのレコード数。

    データサイズ

    テーブルのデータサイズ。MB や GB などの単位で測定されます。

    threads

    同時実行スレッド数

    設定されたスレッド数。

    スレッドの状態

    実行中のスレッドのリアルタイムの状態。