## モジュール 1: TiDB サーバーのスケールアウト時における TiProxy のサービスディスカバリー

このモジュールでは、TiProxy のサービスディスカバリー機能を紹介します。一般的なロードバランサーを利用して TiDB サーバーをスケールアウトする場合、TiDB サーバーの一覧を手動で更新する必要がありますが、TiProxy を使えば手動の介入なしにサーバー一覧が自動調整されます。

次の図は本演習環境の TiDB トポロジです。赤い点線で示された TiDB サーバーがスケールアウト対象です。

![demo018_splash_image_aa_003_20250926.png 85%](https://lab-static.pingcap.com/images/2025/9/26/359c3af54e50e857a606f12425b9849e2fcff4ba.png)

### 手順 1. TiUP 管理ノードへ接続

+ こちらをクリックし、割り当てられた仮想マシンへ接続された新規ターミナルを 2 つ開きます: [ターミナル 1](ssh://monitor1-ssh#1), [ターミナル 2](ssh://monitor1-ssh#2)
+ 特に記載がない限り、以降の操作は [ターミナル 1](ssh://monitor1-ssh#1) で実施します。

### 手順 2. クラスタトポロジの確認

+ 現在は TiDB サーバー 1 台、TiKV サーバー 3 台、TiProxy 1 台が稼働しています。
+ TiProxy は監視サービスが動作する仮想マシン上に配置されています。この仮想マシンは本演習の管理ノードも兼ねています。演習としては問題ありませんが、プロダクションでは通常 TiProxy は専用ノードに配置します。
+ TiProxy のポートは `6000` です。

   ```bash
   ~/.tiup/bin/tiup cluster display tidb-demo
   ```

  ```output
  Cluster type:       tidb
  Cluster name:       tidb-demo
  Cluster version:    v${LAB:TIDB_VERSION}
  Deploy user:        ec2-user
  SSH type:           builtin
  Dashboard URL:      http://${LAB:HOST_PD1_PUBLIC_IP}:2379/dashboard
  Grafana URL:        http://${LAB:HOST_MONITOR1_PRIVATE_IP}:3000
  ID                 Role          Host         Ports        OS/Arch       Status  Data Dir                      Deploy Dir
  --                 ----          ----         -----        -------       ------  --------                      ----------
  ${LAB:HOST_MONITOR1_PRIVATE_IP}:9093     alertmanager  ${LAB:HOST_MONITOR1_PRIVATE_IP}    9093/9094    linux/x86_64  Up      /tidb-data/alertmanager-9093  /tidb-deploy/alertmanager-9093
  ${LAB:HOST_MONITOR1_PRIVATE_IP}:3000     grafana       ${LAB:HOST_MONITOR1_PRIVATE_IP}    3000         linux/x86_64  Up      -                             /tidb-deploy/grafana-3000
  ${LAB:HOST_PD1_PRIVATE_IP}:2379   pd            ${LAB:HOST_PD1_PRIVATE_IP}  2379/2380    linux/x86_64  Up|L    /tidb-data/pd-2379            /tidb-deploy/pd-2379
  ${LAB:HOST_MONITOR1_PRIVATE_IP}:9090     prometheus    ${LAB:HOST_MONITOR1_PRIVATE_IP}    9090/12020   linux/x86_64  Up      /tidb-data/prometheus-9090    /tidb-deploy/prometheus-9090
  ${LAB:HOST_DB1_PRIVATE_IP}:4000   tidb          ${LAB:HOST_DB1_PRIVATE_IP}  4000/10080   linux/x86_64  Up      -                             /tidb-deploy/tidb-4000
  ${LAB:HOST_KV1_PRIVATE_IP}:20160   tikv          ${LAB:HOST_KV1_PRIVATE_IP}   20160/20180  linux/x86_64  Up      /tidb-data/tikv-20160         /tidb-deploy/tikv-20160
  ${LAB:HOST_KV2_PRIVATE_IP}:20160  tikv          ${LAB:HOST_KV2_PRIVATE_IP}  20160/20180  linux/x86_64  Up      /tidb-data/tikv-20160         /tidb-deploy/tikv-20160
  ${LAB:HOST_KV3_PRIVATE_IP}:20160  tikv          ${LAB:HOST_KV3_PRIVATE_IP}  20160/20180  linux/x86_64  Up      /tidb-data/tikv-20160         /tidb-deploy/tikv-20160
  ${LAB:HOST_MONITOR1_PRIVATE_IP}:6000   tiproxy       ${LAB:HOST_MONITOR1_PRIVATE_IP}  6000/3080    linux/x86_64  Up      -                             /tidb-deploy/tiproxy-6000
  Total nodes: 9
  ```

+ この TiDB クラスタを構成する `yaml` ファイルを確認します。
+ `server_configs` セクションでは `graceful-wait-before-shutdown` を `15` 秒に設定しています（詳しくはモジュール 2 を参照）。
+ `yaml` の末尾に TiProxy の設定が記載されています。

  ```bash
  cat topology.yaml
  ```

  ```output
  global:
    user: "ec2-user"
    ssh_port: 22
    deploy_dir: "/tidb-deploy"
    data_dir: "/tidb-data"
    arch: "amd64"
  server_configs:
    pd:
      replication.max-replicas: 3
    tidb:
      security.auto-tls: true
      graceful-wait-before-shutdown: 15
  pd_servers:
    - host: ${LAB:HOST_PD1_PRIVATE_IP}
      client_port: 2379
      peer_port: 2380
      advertise_client_addr: "${LAB:HOST_PD1_PUBLIC_IP}:2379"
  tidb_servers:
    - host: ${LAB:HOST_DB1_PRIVATE_IP}
      port: 4000
      status_port: 10080
      deploy_dir: /tidb-deploy/tidb-4000
      log_dir: /tidb-deploy/tidb-4000/log
  tikv_servers:
    - host: ${LAB:HOST_KV1_PRIVATE_IP}
      port: 20160
      status_port: 20180
    - host: ${LAB:HOST_KV2_PRIVATE_IP}
      port: 20160
      status_port: 20180
    - host: ${LAB:HOST_KV3_PRIVATE_IP}
      port: 20160
      status_port: 20180
  monitoring_servers:
    - host: ${LAB:HOST_MONITOR1_PRIVATE_IP}
  grafana_servers:
    - host: ${LAB:HOST_MONITOR1_PRIVATE_IP}
  alertmanager_servers:
    - host: ${LAB:HOST_MONITOR1_PRIVATE_IP}
  tiproxy_servers:
    - host: ${LAB:HOST_MONITOR1_PRIVATE_IP}
  ```

### 手順 3. ワークロード用スキーマの準備

  ```bash
  mysql -h ${HOST_DB1_PRIVATE_IP} -P 4000 -u root
  ```

  ```sql
  USE test
  CREATE TABLE `users` (
    `id` int,
    `name` varchar(100),
    `balance` int
  );
  INSERT INTO users VALUES (1, 'Alice', 10);
  INSERT INTO users VALUES (2, 'Bob', 10);
  INSERT INTO users VALUES (3, 'Charlie', 10);
  INSERT INTO users VALUES (4, 'Doug', 10);
  INSERT INTO users VALUES (5, 'Edison', 10);
  INSERT INTO users VALUES (6, 'Frank', 10);
  INSERT INTO users VALUES (7, 'Grace', 10);
  INSERT INTO users VALUES (8, 'Hannah', 10);
  INSERT INTO users VALUES (9, 'Iris', 10);
  INSERT INTO users VALUES (10, 'Jack', 10);
  SELECT * FROM users;
  exit
  ```

  ```output
  ...
  mysql> SELECT * FROM users;
  +------+---------+---------+
  | id   | name    | balance |
  +------+---------+---------+
  |    1 | Alice   |      10 |
  |    2 | Bob     |      10 |
  |    3 | Charlie |      10 |
  |    4 | Doug    |      10 |
  |    5 | Edison  |      10 |
  |    6 | Frank   |      10 |
  |    7 | Grace   |      10 |
  |    8 | Hannah  |      10 |
  |    9 | Iris    |      10 |
  |   10 | Jack    |      10 |
  +------+---------+---------+
  10 rows in set (0.00 sec)
  ...
  ```

### 手順 4. Python コードの確認

  ```bash
  cat demo_tiproxy_no_retry.py
  ```

+ 以下のとおり、メイン関数（コード末尾付近）で 4 つのスレッドが起動されます。

  ```output
  num_threads = 4

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

  # Create and start 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.5)
  ```

+ 各スレッドは TiProxy の `6000` ポートへ接続し、その後 TiDB サーバーへルーティングされます。
+ ユーザー名とパスワードは、TiDB に直接接続する場合と同様に指定できます。

  ```output
  conn = connect(
      database="test",
      host="${LAB:HOST_MONITOR1_PRIVATE_IP}",
      port=6000,
      user="root",
      password="",
  )
  cursor = conn.cursor(prepared=True)
  ```

+ 各スレッドが接続した TiDB サーバーの実 IP とポートを出力します。これにより、TiProxy のルーティング挙動を観察できます。

  ```output
   cursor.execute("SELECT @@hostname as hostname, @@port as port, CONNECTION_ID() as connection_id")
            server_info = cursor.fetchone()
            hostname, port, connection_id = server_info

            # Extract IP address from hostname (format: ip-10-90-2-124.ap-northeast-1.compute.internal)
            if hostname.startswith('ip-'):
                ip_parts = hostname.split('.')[0].split('-')[1:]  # Split by '.' then by '-', skip 'ip'
                ip_address = '.'.join(ip_parts)
            else:
                ip_address = hostname
  ```

+ 各スレッドはテーブルのデータをランダムに更新します。
+ すべてのトランザクションは明示的に開始され、4 秒以上（`time.sleep(4)`）継続します。

  ```output
  random_integer = random.randint(0, 10)
  random_id=random.randint(1, 10)
  query = "UPDATE test.users SET balance = " + str(random_integer) + " WHERE id = " + str(random_id) + ";"

  cursor.execute("BEGIN")
  cursor.execute(query)
  time.sleep(4)

  try:
      cursor.execute("COMMIT")
      print(f"Thread {thread_id}: Connected to TiDB server {ip_address}:{port}. User {random_id} balance updated to: {random_integer}")
  except Error as e:
      print(f"Thread {thread_id}: Connected to TiDB server {ip_address}:{port}. Error during query execution: {e}")
  ...
  ```

### 手順 5. ワークロードの実行

+ この段階では、全スレッドが同一の TiDB サーバー（IP `${LAB:HOST_DB1_PRIVATE_IP}`, ポート `4000`）で動作していることを確認できます。

  ```bash
  python3 demo_tiproxy_no_retry.py
  ```

  ```output
  ...
  Thread 1: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 6 balance updated to: 4
  Thread 2: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 3 balance updated to: 3
  Thread 4: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 8 balance updated to: 3
  Thread 1: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 4 balance updated to: 5
  Thread 3: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 8 balance updated to: 7
  ...
  ```

### 手順 6. TiDB サーバーを 1 台スケールアウト

+ [ターミナル 2](ssh://monitor1-ssh#2) で、スケールアウト用の yaml ファイルを確認します。

  ```bash
  cat scale-out-tidb-2.yaml
  ```

  ```output
  tidb_servers:
    - host: ${LAB:HOST_DB2_PRIVATE_IP}
      port: 4000
      status_port: 10080
      deploy_dir: /tidb-deploy/tidb-4000
      log_dir: /tidb-deploy/tidb-4000/log
  ```

+ [ターミナル 2](ssh://monitor1-ssh#2) で TiDB サーバーを 1 台増設します。

  ```bash
  ~/.tiup/bin/tiup cluster scale-out tidb-demo scale-out-tidb-2.yaml \
  --user ec2-user \
  -i ~/.ssh/pe-class-key-${LAB:REGION_NAME}.pem \
  -y
  ```

  ```output
  ...
  Scaled cluster `tidb-demo` in successfully
  ```

+ 数分待機した後、[ターミナル 1](ssh://monitor1-ssh#1) に戻ると、一部のスレッドが新しい TiDB サーバーへ接続されていることが分かります。この過程は自動的に行われます。

  ```output
  ...
  Thread 3: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 6 balance updated to: 9
  Thread 4: Connected to TiDB server ${LAB:HOST_DB2_PRIVATE_IP}:4000. User 1 balance updated to: 9
  Thread 2: Connected to TiDB server ${LAB:HOST_DB2_PRIVATE_IP}:4000. User 9 balance updated to: 5
  Thread 1: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 3 balance updated to: 2
  Thread 4: Connected to TiDB server ${LAB:HOST_DB2_PRIVATE_IP}:4000. User 1 balance updated to: 6
  Thread 3: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 9 balance updated to: 8
  Thread 1: Connected to TiDB server ${LAB:HOST_DB1_PRIVATE_IP}:4000. User 10 balance updated to: 5
  Thread 2: Connected to TiDB server ${LAB:HOST_DB2_PRIVATE_IP}:4000. User 1 balance updated to: 3
  ...
  ```

> [!TIP]
> 2 台の TiDB サーバーへ均等に接続が分散されるまで、数分かかる場合があります。しばらくお待ちください。

+ [ターミナル 1](ssh://monitor1-ssh#1) のワークロードは停止せず、稼働させたままにしてください。
+ 次はモジュール 2 に進みます。

### まとめ

+ 本モジュールでは、TiProxy が新しい TiDB（SQL レイヤー）サービスを自動検出し、複数の TiDB サーバーへ負荷を均等に分配できることを確認しました。
