## モジュール 6: リクエストユニット（RU）の理解

![demo_203-tidb-x-ru-ja.jpg 85%](https://lab-static.pingcap.com/images/2025/12/2/5daed374642d231196e609c691756314cc12456e.jpg)

このモジュールでは、さまざまなデータベース操作がリクエストユニット（RU）をどのように消費するかを探ることで、RUへの理解を深めていきます。ではRUとは何でしょうか？TiDB Cloud を利用すると、TiDB Cloud クラスタで TiDB や TiKV ノードの管理は不要となります。その代わり、課金は実際のストレージ利用量と読み書きで消費された RU の数によって決まります。本質的に、RU とは SQL リクエストで消費されるリソースを重み付き合計で表した指標です。

以下の表は TiDB Cloud における RU の概算を示します。こうした値の意味が完全に理解できなくてもご安心ください ― ラボの手順には影響しません。

| RU 種別   | 概算定義                                                                               |
| -------- | :----------------------------------------------------------------------------------- |
| 1 Read RU  | `0.5 x バッチ PointGet の回数 + 0.125 x PointGet の回数 + 64Kデータ量 + 0.3 x CPU (ms)` |
| 1 Write RU | `(0.5 x バッチ PointGet の回数 + 0.5 x PointGet の回数 + 2Kデータ量 ) x 3`            |

1秒あたりの RU の最小・最大値を設定して、クラスタの自動スケーリング範囲を自動的に制御できます。詳細は下表をご参照ください。

| サブスクリプションプラン       | Starter                              | Essential                 |
| :---------------------------- | ------------------------------------:| ------------------------: |
| 無料利用枠                    | 5 GiB & 月間 5000万 RU               | なし                     |
| 最小RU（毎秒）                | 0                                    | 2,000                    |
| 最大RU（毎秒）                | 保証なし                             | 100,000                  |
| クラスタ自動スケーリング     | 保証なし                             | 毎秒 2,000～100,000 RU   |
| 可用性                        | 99.9% ゾーン                          | 99.99% リージョン         |

![payg.jpg 50%](https://lab-static.pingcap.com/images/2025/12/12/1077d4a1ea93f3ba5a38f93250ba42f30ee81bdb.jpg)

[リクエストユニット（RU）](https://www.pingcap.com/jp/tidb-cloud-starter-pricing-details/)は TiDB でシステムリソース（CPU/IO など）が各データベースリクエストによって消費された量を算出する統一的な抽象指標です。無料の TiDB Cloud Starter クラスタを活用して、実際にご自身の SQL ステートメントがどれだけ RU を利用するかを試算・検証し、今後の判断材料としましょう。

このモジュールでは、RU 情報を取得する方法をステップバイステップで説明します。

### 手順 1. テスト用テーブルの作成

- [ターミナル 1](ssh://monitor1-ssh#1)でまだ TiDB に接続していなければ接続します。

  ```bash
  mysql \
  --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
  -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
  --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt
  ```

  ```output
  mysql>
  ```

- `${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}` スキーマにテスト用テーブル `tbl_ru_1` および `tbl_ru_2` を作成します。下記 DDLは両テーブルが同一定義であることを示しています。

  ```sql
  CREATE DATABASE IF NOT EXISTS ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};
  USE ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};

  DROP TABLE IF EXISTS tbl_ru_1;
  CREATE TABLE tbl_ru_1 (
  `ID` bigint PRIMARY KEY AUTO_RANDOM,
  `pState` varchar (100) NOT NULL,
  `cState` tinyint (4) DEFAULT NULL,
  `created_at` DATETIME,
  `updated_at` DATETIME,
  `txt` varchar(64) DEFAULT 'TTLTEST',
  KEY `pcStateIndex`(`pState`, `cState`)
  ) PRE_SPLIT_REGIONS=4;

  DROP TABLE IF EXISTS tbl_ru_2;
  CREATE TABLE tbl_ru_2 (
  `ID` bigint PRIMARY KEY AUTO_RANDOM,
  `pState` varchar (100) NOT NULL,
  `cState` tinyint (4) DEFAULT NULL,
  `created_at` DATETIME,
  `updated_at` DATETIME,
  `txt` varchar(64) DEFAULT 'TTLTEST',
   KEY `pcStateIndex` (`pState`, `cState`)
   ) PRE_SPLIT_REGIONS=4;
  ```

  ```output
  ...
  Query OK, 0 rows affected, 1 warning (0.28 sec)
  ```

### 手順 2. モニタリングコンソールに慣れる

- TiDB Cloud コンソールの [クラスタ一覧ページ](https://tidbcloud.com/project/clusters) にアクセスし、ラボのクラスタ名をクリックします。`新規クラスタを作成`で始めた場合、クラスタ名は `${LAB:SERVERLESS_CLUSTER_NAME}` です。

![Demo-RU-001.png 85%](https://lab-static.pingcap.com/images/2025/11/24/9c016ddc77e89cb9d9ae0669890d36cb316f6d20.png)

- クラスタ名 `${LAB:SERVERLESS_CLUSTER_NAME}` をクリックし、左上の `Overview` を選択します。

![Demo-RU-002.png 85%](https://lab-static.pingcap.com/images/2025/11/24/8d064cdda27759df1640171cbf92f7d340e4364d.png)

- `Monitoring` タブを展開し、`Diagnosis` を選択します。ここでは、実行された SQL 文と各 SQL が消費した RU の数を確認できます。

![Demo-RU-003.png 85%](https://lab-static.pingcap.com/images/2025/11/24/586655532161734f86ee41c826cd99221b3533ce.png)

- 次に `Monitoring` タブを展開し `Metrics` を選択し、`Cluster Status` タブを開きます。この画面ではデータベースクラスタの毎秒 RU 消費量や QPS メトリクスなどをリアルタイムで監視できます。

![Demo-RU-004.png 85%](https://lab-static.pingcap.com/images/2025/11/24/1be07d68448a466052182b702096201f01e83fbc.png)

- このページはこの後も利用するので、開いたままにしてください。

### 手順 3. シングルワーカーで挿入ワークロードを実行

- [ターミナル 1](ssh://monitor1-ssh#1) で OS プロンプトに戻ります。

  ```sql
  EXIT;
  ```

- [ターミナル 1](ssh://monitor1-ssh#1) で Python 仮想環境を作成します。

  ```bash
  python3 -m venv ~/.venv
  ~/.venv/bin/pip install mysql-connector-python prettytable
  ```

- [ターミナル 1](ssh://monitor1-ssh#1) で、1スレッドで書き込み操作を実行する Python スクリプトを作成します。

  ```bash
  cat > demo_insert_single_thread.py <<'EOF'
  import mysql.connector
  from prettytable import PrettyTable
  import datetime
  import time
  import threading
  import sys

  config = {
  'user': '${LAB:SERVERLESS_CLUSTER_USERNAME}',
  'password': '${LAB:SERVERLESS_CLUSTER_PASSWORD}',
  'host': '${LAB:SERVERLESS_CLUSTER_HOST}',
  'database': '${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}',
  'raise_on_warnings': False,
  'port': ${LAB:SERVERLESS_CLUSTER_PORT},
  }

  def database_worker(thread_id):
     try:
         num_loop = 100;

         # 接続の確立
         conn = mysql.connector.connect(**config)
         # カーソルオブジェクト作成
         cursor = conn.cursor(prepared=True)

         for n in range(num_loop):
             queries = ["INSERT INTO tbl_ru_1(pState, cState, created_at, updated_at) VALUES (ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), 1 + FLOOR(RAND() * 2), NOW(), NOW());"
                     ]
             num_query = 1;
             cursor.execute("BEGIN")

             for i in range(num_query):
                 cursor.execute(queries[i])

             cursor.execute("COMMIT")

             if n % 10 == 0 and n != 0:
                 print("%d rows have been inserted." % n)

         print("%d rows have been inserted." % num_loop)

         try:
             cursor.close()
             conn.close()
         except Error as e:
             pass

     except (Error, MySQLInterfaceError) as connect_err:
         print(f"Thread {thread_id}: Connection or execution error: {connect_err}")
  def main():
     num_threads = 1

     print(f"Starting {num_threads} database worker threads...")

     # スレッド作成と起動
     threads = []
     for i in range(num_threads):
         thread = threading.Thread(
             target=database_worker,
             args=(i + 1,),
             daemon=True
         )
         threads.append(thread)
         thread.start()
         time.sleep(0.1)

     print(f"All {num_threads} threads started successfully!")

     try:
         # メインスレッドで全スレッドが終了するのを待つ
         for thread in threads:
             thread.join()
     except KeyboardInterrupt:
         print("\nReceived interrupt signal. Stopping all threads...")
         sys.exit(0)

  if __name__ == "__main__":
     main()
  EOF
  ls -l demo_insert_single_thread.py
  ```

  ```output
  -rw-rw-r-- 1 ubuntu ubuntu 2250 Nov 20 11:35 demo_insert_single_thread.py
  ```

- [ターミナル 1](ssh://monitor1-ssh#1)でこの Python スクリプトを実行します。このスクリプトは、1つのスレッドがテーブル `tbl_ru_1` にランダムデータを計100回挿入します。この処理は約2分かかります。

  ```bash
  ~/.venv/bin/python demo_insert_single_thread.py
  ```

  ```output
  Starting 1 database worker threads...
  All 1 threads started successfully!
  10 rows have been inserted.
  20 rows have been inserted.
  ...
  100 rows have been inserted.
  ```

### 手順 4. RU 消費量の確認

- `${LAB:SERVERLESS_CLUSTER_NAME}` クラスタのOverviewページに移動し、左メニューから `Monitoring` > `Metrics` を選択、`Cluster Status` タブを開きます。時間範囲を「直近15分」に、リフレッシュレートを「30秒」に設定し、2～3分待つと、`Query Per Second` および `Request Units` パネルに最近の書き込みアクティビティが反映されます。ここでシングルスレッド書き込み時の RU 毎秒消費量を記録しましょう。

![Demo-RU-005.png 85%](https://lab-static.pingcap.com/images/2025/11/24/37a67e7f484d93f87bf7684636f6b09a4f13da29.png)

- 次に、`Monitoring` > `Diagnosis` を選択し、`SQL Statement` タブに移動します。時間範囲を「直近15分」にし、SQL種別を「Insert」に絞り込みます。1～3分後に各挿入SQLの実行がリスト表示されるので、`Executions Count`（実行回数）、`Total RU`、`Mean RU`（1回のSQL実行で消費されるRU）などを記録します。
- `Mean RU` は、対象SQL 1回実行あたりのRU消費量です。今回の例だと2.55です。
- `Total RU` は、そのSQLコマンドが指定期間内に総計で消費したRU数です。たとえば100回の挿入で合計255.00となります。

![Demo-RU-006.png 85%](https://lab-static.pingcap.com/images/2025/11/24/6ac06a5976dfb3ae4df234937f28b02e31973b25.png)

- ちなみに、TiDB のようにリニアにスケーラブルな分散システムでは、RU消費量の予測が容易です。次のステップで実際に確かめてみましょう。

### 手順 5. 10倍のワーカーで挿入ワークロードを実行

- [ターミナル 1](ssh://monitor1-ssh#1) で、10ワーカーで挿入操作を実施する Python スクリプトを作成します。

  ```bash
  cat > demo_insert_multi_thread.py <<'EOF'
  import mysql.connector
  from prettytable import PrettyTable
  import datetime
  import time
  import threading
  import sys

  config = {
  'user': '${LAB:SERVERLESS_CLUSTER_USERNAME}',
  'password': '${LAB:SERVERLESS_CLUSTER_PASSWORD}',
  'host': '${LAB:SERVERLESS_CLUSTER_HOST}',
  'database': '${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}',
  'raise_on_warnings': False,
  'port': ${LAB:SERVERLESS_CLUSTER_PORT},
  }

  def database_worker(thread_id):
     try:
         num_loop = 100;

         # 接続の確立
         conn = mysql.connector.connect(**config)

         # カーソルオブジェクト作成
         cursor = conn.cursor(prepared=True)

         for n in range(num_loop):
             queries = ["INSERT INTO tbl_ru_2(pState, cState, created_at, updated_at) VALUES (ELT(1 + FLOOR(RAND() * 9), 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's0'), 1 + FLOOR(RAND() * 2), NOW(), NOW());"
                     ]
             num_query = 1;
             cursor.execute("BEGIN")
             for i in range(num_query):
                 cursor.execute(queries[i])
             cursor.execute("COMMIT")
             if n % 10 == 0 and n != 0:
                 print("Thread %d: %d rows have been inserted." % (thread_id, n))
         print("Thread %d: %d rows have been inserted." % (thread_id, num_loop))

         try:
             cursor.close()
             conn.close()
         except Error as e:
             pass

     except (Error, MySQLInterfaceError) as connect_err:
         print(f"Thread {thread_id}: Connection or execution error: {connect_err}")
  def main():
     num_threads = 10

     print(f"Starting {num_threads} database worker threads...")

     # スレッド作成と起動
     threads = []
     for i in range(num_threads):
         thread = threading.Thread(
             target=database_worker,
             args=(i + 1,),
             daemon=True
         )
         threads.append(thread)
         thread.start()
         time.sleep(0.1)

     print(f"All {num_threads} threads started successfully!")

     try:
         # メインスレッドで全スレッドが終了するのを待つ
         for thread in threads:
             thread.join()
     except KeyboardInterrupt:
         print("\nReceived interrupt signal. Stopping all threads...")
         sys.exit(0)

  if __name__ == "__main__":
     main()
  EOF
  ls -l demo_insert_multi_thread.py
  ```

  ```output
  -rw-rw-r-- 1 ubuntu ubuntu 2212 Nov 21 09:03 demo_insert_multi_thread.py
  ```

- この Python スクリプトを実行します。このスクリプトでは10スレッドがそれぞれ `tbl_ru_2` テーブル（定義は `tbl_ru_1` と同一）にランダムデータを計100回ずつ挿入します。

  ```bash
  ~/.venv/bin/python demo_insert_multi_thread.py
  ```

  ```output
  Starting 10 database worker threads...
  ...
  Thread 9: 100 rows have been inserted.
  Thread 6: 100 rows have been inserted.
  Thread 7: 100 rows have been inserted.
  Thread 8: 100 rows have been inserted.
  ```

### 手順 6. 再度 RU の消費量を確認

- `${LAB:SERVERLESS_CLUSTER_NAME}` クラスタのOverviewページから、左メニューで `Monitoring` > `Metrics` > `Cluster Status` を選択し、「直近15分」「リフレッシュ30秒」に設定します。2～3分ほど置くと、書き込みアクティビティ（QPS と Request Units）が確認できます。シングルスレッド時・マルチスレッド時のRU毎秒消費量を記録しましょう。

![Demo-RU-007.png 85%](https://lab-static.pingcap.com/images/2025/11/24/a3d5f12c2f36c28700d1ed35e56f85e9b78d98f0.png)

- `Monitoring` > `Diagnosis` で `SQL Statement` タブに移動し、「直近15分」「Insert」でフィルタリングします。マルチスレッド挿入SQLの `Executions Count`、`Total RU`、`Mean RU` を確認します。

![Demo-RU-008.png 85%](https://lab-static.pingcap.com/images/2025/11/24/460a92179f90a203c65482ae697f7265a242ffae.png)

- 同じワークロードを10並列実行しても `Mean RU` はほぼ変わらず、 `Total RU` はシングルスレッド実行の10倍程度になります。今回は `Mean RU` 約2.55、 `Total RU` 約2,550です。

### 手順 7. EXPLAIN で SQL ごとの RU 情報を確認

- Monitoring Console では2～3分遅延する場合がありますが、`EXPLAIN ANALYZE` を使えば直ちにSQLクエリごとのRU消費統計が得られます。

- [ターミナル 1](ssh://monitor1-ssh#1)で TiDB に接続します。

  ```bash
  mysql \
  --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
  -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
  --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt \
  -D ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  ```

- 以下のクエリを `explain analyze` 付きで実行します。

  ```sql
  PAGER less -S
  EXPLAIN ANALYZE
  SELECT MAX(created_at)
  FROM tbl_ru_1;
  ```

- 出力中の `execution info` 列を左右キーで探し、最初の行で `RU:` に続く値に注目してください。これが実際にこのクエリが消費したRUです。下記例では `RU:0.581812` ですが、ご自身の環境では若干異なることがあります。

  ```output
  ... -----------+---------------+----------------------------------------------------->
  ...  task      | access object | execution info                                      >
  ... -----------+---------------+----------------------------------------------------->
  ...  root      |               | time:1.39ms, loops:2, RU:0.581812
  ```

- 出力を抜けるには `Q` キーを押します。

### 手順 8. クリーンアップ

- [ターミナル 1](ssh://monitor1-ssh#1)で `${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}` スキーマ配下の `tbl_ru_1` および `tbl_ru_2` テーブルを削除します。

  ```sql
  NOPAGER;
  USE ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME};
  DROP TABLE tbl_ru_1;
  DROP TABLE tbl_ru_2;
  ```

  ```output
  Query OK, 0 rows affected (0.55 sec)
  ...
  Query OK, 0 rows affected (0.63 sec)
  ```

- RUの仕組みをしっかり理解することで、SQLごとのRU使用量を評価したり、TiDB Cloudの料金プランをRU指標で比較し、自分のワークロードに最適なプランを選べるようになります。RUや料金に関する詳細は[ドキュメント](https://www.pingcap.com/jp/tidb-cloud-starter-pricing-details/)を参照してください。では次のモジュールへ進みましょう。
