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

PolarDB:ホット行の最適化

最終更新日:Nov 14, 2025

ホット行 (ホットスポット行) は、データベース内で頻繁に変更されるデータ行です。高い同時実行性のシナリオでは、ホット行を更新すると、深刻な行ロックの競合と長い待機時間が発生し、システムパフォーマンスが低下します。この問題に対処するため、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 文はホット行の最適化機能を使用しません。

使用方法

  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コンソールでは、次のパラメーターを変更できません。変更するには、Quota Center に移動します。[PolarDB Hotspot Row Parameter Adjustment] という名前のクォータを見つけ、[アクション] 列の [適用] をクリックします。

パラメーター

説明

hotspot_for_autocommit

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

  • ON: 有効。

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

hotspot_update_max_wait_time

グループ更新中に Leader が Follower のグループ参加を待つ待機時間。

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

  • デフォルト値: 100 us。

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

テストシナリオ

1 つのホット行と 8 コア CPU。

テスト結果

1 つのホット行と 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

テストシナリオ

1 つのホット行と 8 コア CPU。

テスト結果

1 つのホット行と 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

lat95th (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

テストシナリオ

1 つのホット行と 8 コア CPU。

テスト結果

1 つのホット行と 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

lat95th (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_read_only run

    入力パラメーター

    パラメーター

    説明

    mysql-host

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

    mysql-port

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

    mysql-user

    クラスターのユーザー名。

    mysql-password

    クラスターユーザーのパスワード。

    mysql-db

    データベース名。

    出力パラメーター

    パラメーター

    表示内容

    説明

    tables

    データテーブルの数

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

    table_size

    データテーブルの行数

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

    データサイズ

    テーブル内のデータのサイズ。MB や GB などのストレージ単位で表示されます。

    threads

    同時実行スレッド数

    構成されたスレッドの数。

    スレッドステータス

    スレッドのリアルタイム実行ステータス。