# mysql-connector-python を使用した TiDB の操作

## 概要

TiDBは、ハイブリッドトランザクション分析処理（HTAP）をサポートする分散型、クラウドネイティブ、水平方向にスケーラブルなデータベースです。アプリケーション開発者にとって、TiDBは常に利用可能で信頼性が高く、制限なくスケーリングできるデータベースです。

+ この演習では、以下の方法を学びます：
  + Pythonコードを使用してTiDB Cloud Serverlessクラスターに接続し、データにアクセスして変更する方法。
  + プリペアドステートメントとバッチ挿入を使用してアプリケーションのパフォーマンスを向上させる方法。
  + ホットスポットを回避するために`AUTO_INCREMENT`の代わりに`AUTO_RANDOM`を使用する方法。
  + 進行中のトランザクションをブロックまたは中断することなく、オンラインDDLを使用してテーブルに列を追加する方法。
  + 同時実行制御のための悲観的モードと楽観的モードを使用して比較する方法。

### 所要時間

+ この演習は約 90 分程度かかります。

> [!NOTE]
> この演習では、Webベースの VS Code を使用します。WebベースのIDEの使用はTiDBでは必須ではありません。ただし、クラウドデータベースのコラボレーションでは一般的なアプローチです。クラウドデータベースの近くに開発者クライアントをデプロイすることで、アプリケーションサーバーの実際の動作とネットワーク条件をより適切にシミュレートできます。

> [!TIP]
> 演習環境のデプロイが完了したら、下部のバーにある **次のモジュール** をクリックして、**モジュール 1** に進みます。

## モジュール 1: Python コードを実行する準備

### 手順 1. IDE でターミナルを開く

+ ブラウザで自分の [VS Code](${LAB:SECURITY_ACCESS_PROXY_URL}?next=${LAB:"/vscode/?folder=/root/tidb-lab-python"|urlencode}) を開きます。
+ ポップアップウィンドウが表示されたら、「Yes, I trust the authors（はい、著者を信頼します）」をクリックしてください。

  ![90%](https://lab-static.pingcap.com/quick-demo/vscode-trust.jpg)

+ 左上の `≡` をクリックし、次に `Terminal`、そして `New Terminal` をクリックして新しいターミナルを開いてください。

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-open-terminal-0307.jpg)

### 手順 2. Python の依存関係をインストールする

+ VS Code ターミナルで以下のコマンドを実行して、必要な依存関係をインストールします。

  ```bash
  poetry install
  ```

  ```output
  Creating virtualenv tidb-lab-python-CQ_Jo6Zv-py3.11 in /root/.cache/pypoetry/virtualenvs
  Installing dependencies from lock file

  Package operations: 4 installs, 0 updates, 0 removals

    - Installing python-dotenv (1.0.1)
    - Installing dotenv (0.9.9)
    - Installing mysql-connector-python (9.2.0)
    - Installing pymysql (1.1.1)
  ```

### 手順 3. .env ファイルを作成する

+ `tidb-lab-python/` フォルダの下に `.env` ファイルを作成します。

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-new-env-0307.jpg)

+ 以下の内容を `.env` ファイルに追加して保存します。

  ```properties
  TIDB_HOST=${LAB:SERVERLESS_CLUSTER_HOST}
  TIDB_PORT=${LAB:SERVERLESS_CLUSTER_PORT}
  TIDB_USER='${LAB:SERVERLESS_CLUSTER_USERNAME}'
  TIDB_PASSWORD='${LAB:SERVERLESS_CLUSTER_PASSWORD}'
  TIDB_DB_NAME=${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  ```

## モジュール 2: Python コードを実行する

Python コードを実行して TiDB Cloud Serverless クラスタに接続し、基本的な操作を行います。

### 手順 1. TiDB Cloud Serverless クラスタに接続する

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `demo_connection.py` ファイルをクリックし、ソースコードを簡単に確認してください。この Python スクリプトは、演習用の TiDB Cloud Serverless クラスタに接続し、`${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}` という名前の新しいデータベースを作成します。

+ VS Code ターミナルで `demo_connection.py` を実行します。

  ```bash
  poetry run python mysql_connector_python_demo/demo_connection.py
  ```

  ```output
  Connected to gateway01.us-west-2.prod.aws.tidbcloud.com:4000 as 42yK4xxxxxxxxxxxxsP (Connection ID: 1233194)
  The server is running 5.7.28-TiDB-Serverless
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-demo-connection-0307.jpg)

> [!IMPORTANT]
> 接続文字列は`.env`ファイルに保存されています。
> 今後の手順では、`demo_connection.py`の`get_connection`関数を使用してTiDB Cloud Serverlessクラスタへの接続を取得します。

### 手順 2. データを挿入する

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `positional_placeholders.py` ファイルをクリックし、ソースコードを簡単に確認してください。

+ `positional_placeholders.py` を実行して位置プレースホルダーを使用してデータを挿入します。

  ```bash
  poetry run python mysql_connector_python_demo/positional_placeholders.py
  ```

  ```output
  1 row inserted into table t1 by positional placeholders
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-positional-placeholders-0307.jpg)

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `named_placeholders.py` ファイルをクリックし、ソースコードを簡単に確認してください。

+ 名前付きプレースホルダーを使用してデータを挿入します。

  ```bash
  poetry run python mysql_connector_python_demo/named_placeholders.py
  ```

  ```output
  1 row inserted into table t1 by named placeholders
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-named-placeholders-0307.jpg)

### 手順 3. データを照会する

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `query_fetch_all.py` ファイルをクリックし、ソースコードを簡単に確認してください。

+ `fetchall()` 関数を使用してデータを照会します。

  ```bash
  poetry run python mysql_connector_python_demo/query_fetch_all.py
  ```

  ```output
  PK of ABC is 1
  PK of DEF is 2
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-fetchall-0307.jpg)

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `query_fetch_one.py` ファイルをクリックし、ソースコードを簡単に確認してください。

+ `fetchone()` 関数を使用してデータを照会します。

  ```bash
  poetry run python mysql_connector_python_demo/query_fetch_one.py
  ```

  ```output
  PK of ABC is 1
  PK of DEF is 2
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-fetchone-0307.jpg)

## モジュール 3: プリペアドステートメントとバッチ挿入の使用

これで、Pythonコードを使用してTiDB Cloud Serverlessクラスタにデータを読み書きできるようになりました。しかし、アプリケーションのパフォーマンスを向上させたい場合は、プリペアドステートメント（事前コンパイルされた繰り返し実行用）と効率的なバッチ挿入（単一のSQL文でテーブルに複数行のデータを挿入するため）を使用することができます。

### 手順 1. プリペアドステートメントの使用

+ VS Codeの左側の**EXPLORER**で、`mysql_connector_python_demo`フォルダの下にある`prepared_statement.py`ファイルをクリックし、サンプルコードの`connection.cursor(prepared=True)`と`print("Generated PK is", cursor.lastrowid)`の行に注目してください。パラメータ`prepared=False`でカーソルを作成すると、クライアント側のプリペアドステートメントのみを使用することになります。`cursor.lastrowid`は、最近挿入された行の自動ランダムIDを取得するためのものです。

+ `prepared_statement.py` を実行します。

  ```bash
  poetry run python mysql_connector_python_demo/prepared_statement.py
  ```
  ```output
  The generated PK is 4035225266123964417
  ```

![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-prepared-statement-0307.jpg)

### 手順 2. バッチ挿入の使用

+ 次に、バッチ挿入と単一挿入のパフォーマンスを比較します。VS Codeの左側の**EXPLORER**で、`mysql_connector_python_demo`フォルダの下にある`batch_insert.py`ファイルをクリックしてください。

+ このステップでは、テーブル`t1_batchtest`に1000行を挿入します。バッチ挿入関数`_batch_style_insert`では、`executemany`関数を使用してデータを挿入します。一方、単一挿入関数`_single_style_insert`では、`execute`関数を使用して行ごとにデータを挿入します。

  ```bash
  poetry run python mysql_connector_python_demo/batch_insert.py
  ```
  ```output
  Batch Inserting 1000 rows in 84.9921875 (ms).
  Total rows in t1_batchtest table: 1000.
  Non-Batch Inserting 1000 rows in 19608.541259765625 (ms).
  Total rows in t1_batchtest table: 1000.
  ```

![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-batch-insert-0307.jpg)

+ ご覧のとおり、バッチ挿入は単一挿入よりもはるかに高速です。

## モジュール 4: ホットスポットを回避するために AUTO_INCREMENT の代わりに AUTO_RANDOM を使用する

+ TiDBのような分散データベースでは、データは複数のストレージノード（TiKVノード）にリージョンとして分割され分散されています。

+ `AUTO_INCREMENT` の問題点:
  + `AUTO_INCREMENT` を使用すると、IDは連続的になります（1, 2, 3, 4, ...）
  + 新しい挿入はすべて、最新のIDが格納されている同じリージョンに向かいます
  + これにより、1つのストレージノードが大部分の書き込みトラフィックを処理する「ホットスポット」が生じます
  + 残りのストレージノードは十分に活用されません
  + このボトルネックにより、書き込みスループットが著しく制限される可能性があります

+ `AUTO_RANDOM` の解決策:
  + `AUTO_RANDOM` は非連続的で分散されたIDを生成します
  + これらのシャードビットは、書き込みを異なるリージョンに分散させるのに役立ちます
  + INSERT ワークロードはすべてのストレージノードに分散されます
  + これにより、高並行性シナリオでの書き込みスループットとスケーラビリティが向上します

  ```bash
  poetry run python mysql_connector_python_demo/auto_random.py
  ```
  ```output
  ...
  AUTO_RANDOM IDs (distributed to avoid hotspots):
  3
  288230376151711748
  1441151880758558729
  1729382256910270465
  3458764513820540934
  3458764513820540935
  4899916394579099650
  5764607523034234888
  6917529027641081866
  7782220156096217093

  AUTO_INCREMENT IDs (sequential, can cause hotspots):
  1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  ...
  ```

## モジュール 5: オンライン DDL を使用してオンラインスキーマ変更を実行する

+ このモジュールでは、進行中の**INSERT ジョブ**が中断されることなく継続実行されることを確保しながら、**TiDB**で**オンラインスキーマ変更（DDL操作）**を実行する方法を説明します。このプロセスにおける重要な課題は、スキーマ変更がトランザクションを一時的にブロックする際に発生する**エラーコード8028**の処理です。このデモでは、スキーマ変更にもかかわらず**insertジョブ**が回復して継続できるようにする堅牢なアプローチを示します。

+ このモジュールは主に2つの操作で構成されています：

1. **挿入ジョブ（`--insert`）**
   + 指定されたテーブルに新しいレコードを継続的に挿入します。
   + 同時DDL操作により発生する可能性のあるスキーマ変更例外（エラーコード**8028**）を処理します。
   + サービスの可用性を維持するために、失敗した場合は挿入操作を再試行します。

2. **スキーマ変更（`--alter`）**
   + 挿入が進行中の間にテーブル構造を変更する`ALTER TABLE`コマンドを実行します。
   + TiDBが**オンラインスキーマ変更**を許可することを示しますが、スキーマ変更によりトランザクションが一時的に失敗する可能性があります。
   + 進行中のトランザクションが適切に適応する間、新しいスキーマが正常に適用されることを確保します。

> [!TIP]
> **主な実装の詳細：**
>
> + **TiDBメタデータロックの処理**: スクリプトは**メタデータロックを無効化**（`SET GLOBAL tidb_enable_metadata_lock = OFF`）して、クエリをブロックせずにDDL変更を許可します。
> + **自動エラー処理**: 挿入ジョブは**エラーコード8028を検出**し、クラッシュを防ぐために**失敗したトランザクションを再試行**します。
> + **並行実行**: スクリプトは**挿入ジョブ**と**スキーマ変更**を別々のターミナルで実行できるようにし、データベース更新が進行中のトランザクションと並行して発生する実際のシナリオをシミュレートします。

+ まず、挿入ジョブを実行します。

  ```bash
  poetry run python mysql_connector_python_demo/online_ddl.py --insert
  ```
  ```output
  Starting insert job on table online_ddl_test
  You can now run ALTER TABLE in another terminal with:
  python online_ddl.py --alter online_ddl_test
  Connected to TiDB: 2nR9s2Y6oh4rkp7.6z3VQEZ4@gateway01.us-west-2.prod.aws.tidbcloud.com:4000 using database test
  Disabled TiDB metadata lock for online DDL operations
  Current setting: tidb_enable_metadata_lock = OFF
  Created table online_ddl_test
  Inserting record 0
  Inserting record 1
  Inserting record 2
  Inserting record 3
  Inserting record 4
  Inserting record 5
  ...
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-online-ddl-0319.jpg)

+ VS Codeで`+`ボタンをクリックして、新しいターミナルを作成します。

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-add-terminal-0319.jpg)

+ 新しいターミナルでスキーマ変更を実行します。

  ```bash
  poetry run python mysql_connector_python_demo/online_ddl.py --alter
  ```
  ```output
  Executing ALTER TABLE on online_ddl_test
  Connected to TiDB: 2nR9s2Y6oh4rkp7.6z3VQEZ4@gateway01.us-west-2.prod.aws.tidbcloud.com:4000 using database test
  Table online_ddl_test exists, proceeding with ALTER TABLE
  Executing: ALTER TABLE online_ddl_test ADD COLUMN (ed VARCHAR(10) DEFAULT 'N/A')
  ALTER TABLE completed successfully in 0.39 seconds

  Updated table structure:
    id: int(11)
    k: int(11)
    c: char(120)
    pad: char(60)
    ed: varchar(10)
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-alter-table-0319.jpg)

+ VS Codeで最初のターミナルに戻り、挿入ジョブが再試行ログとともに引き続き実行されていることを確認します。

  ```output
  ...
  Inserting record 105
  Inserting record 106
  Schema mutation encountered, retrying...
  Inserting record 107
  Inserting record 108
  ...
  ```

  ![90%](https://public-pingcap-innovation.s3.us-west-2.amazonaws.com/demo-405/405-error-catch.jpg)

+ 挿入DMLもDDL操作もブロックされていません。

## モジュール 6: 同時実行制御 - 悲観的モード vs. 楽観的モード

同時アクセスをサポートするデータベースでは、複数のトランザクションが同時に同じデータを更新しようとする場合に競合のリスクがあります。適切に設計されたデータモデルはこのような競合の可能性を最小限に抑えることができますが、それでも競合は発生する可能性があり、効果的に管理する必要があります。データベースは同時データアクセスを処理するメカニズムを提供しており、主な2つのアプローチは悲観的同時実行制御と楽観的同時実行制御です。TiDBは両方のアプローチをサポートしています。

このモジュールでは、TiDBにおける悲観的ロックと楽観的ロックの両方の動作を示します。

### 手順 1. 悲観的モードのデモコードを読む

+ VS Codeの左側の**エクスプローラー**で、`mysql_connector_python_demo`フォルダの下にある`lock_pessimistic.py`ファイルをクリックします。内容を確認しましょう。

+ 以下のコードでは、2つのスレッドが開始されます。各スレッドはTiDBへの接続を作成します。

  ```output
  ...
  t1 = threading.Thread(target=thread1_task)
  t2 = threading.Thread(target=thread2_task)
  t1.start()
  t2.start()
  ...
  ```

+ 以下のコードでは、`threading.Event`を使用してスレッド1とスレッド2のアクションを調整し、確実に競合が発生するようにしています。スレッド1と2の両方で自動コミットは無効になっています。

  ```output
  def thread1_task():
    conn1 = get_connection(autocommit=False)
    cur1 = conn1.cursor(dictionary=True)
    cur1.execute("BEGIN PESSIMISTIC;")
    print("[Thread1] BEGIN PESSIMISTIC", flush=True)

    # notify thread2 to begin transaction
    thread1_begin.set()

    # update and hold lock
    cur1.execute("UPDATE users SET name = 'conn1' WHERE id = 1;")
    print("[Thread1] UPDATED row → name='conn1'", flush=True)

    # notify thread2 to begin transaction
    thread1_updated.set()

    # wait thread2 to attempt update (blocked)
    thread2_ready.wait()

    cur1.execute("COMMIT;")
    print("[Thread1] COMMITTED", flush=True)
    ...
  ```
  ```output
  def thread2_task():
    conn2 = get_connection(autocommit=False)
    cur2 = conn2.cursor(dictionary=True)

    # wait thread1 to complete BEGIN
    thread1_begin.wait()

    cur2.execute("BEGIN PESSIMISTIC;")
    print("[Thread2] BEGIN PESSIMISTIC", flush=True)

    # wait thread1 to update and hold lock
    thread1_updated.wait()

    # notify thread1 that I will attempt UPDATE
    thread2_ready.set()
    print("[Thread2] attempting UPDATE (will block)...", flush=True)

    # this line will be blocked until thread1 COMMIT release row lock
    cur2.execute("UPDATE users SET name = 'conn2' WHERE id = 1;")
    print("[Thread2] UPDATED row → name='conn2'", flush=True)

    cur2.execute("COMMIT;")
    print("[Thread2] COMMITTED", flush=True)
    ...
  ```

### 手順 2. 悲観的モードでデモを実行する

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `lock_pessimistic.py` ファイルをクリックしてください。内容を確認しましょう。

+ 悲観的モードでデモコードを実行し、出力を観察してください。スレッド1がコミットするまで、スレッド2がコミットできないことに注目してください。

  ```bash
  poetry run python mysql_connector_python_demo/lock_pessimistic.py
  ```
  ```output
  Create database lab_demo_405_3450001
  [Thread1] BEGIN PESSIMISTIC
  [Thread2] BEGIN PESSIMISTIC
  [Thread1] UPDATED row → name='conn1'
  [Thread2] attempting UPDATE (will block)...
  [Thread1] COMMITTED
  [Thread2] UPDATED row → name='conn2'
  [Thread2] COMMITTED
  Update Result: name is changed to conn2
  ```

### 手順 3. 楽観的モードのデモコードを読む

+ VS Code の左側の **EXPLORER** で、`mysql_connector_python_demo` フォルダの下にある `lock_optimistic.py` ファイルをクリックしてください。内容を確認しましょう。

+ 楽観的ロックのデモコードは悲観的ロックのものと似ていますが、楽観的ロックでは両方のスレッドに `try` と `except` のロジックが実装されている点が異なります。また、楽観的ロックのデモコードでは `threading.Event` は使用されていません。

+ スレッド1を例として見てみましょう。

  ```output
  def thread1_task():
    ...
    cur1.execute("BEGIN OPTIMISTIC;")
    print("[Thread1] BEGIN OPTIMISTIC", flush=True)
    
    # update
    cur1.execute("UPDATE users SET name = 'conn1' WHERE id = 1;")
    print("[Thread1] UPDATED row → name='conn1'", flush=True)
    ...

    # thread1 try to commit.
    # error(s) will be raised if there is a conflict
    try:
        cur1.execute("UPDATE users SET name = 'conn1' WHERE id = 1;")
        print("[Thread1] UPDATED row → name='conn1'", flush=True)
        print("[Thread1] Try to commit", flush=True)

        cur1.execute("COMMIT;")
        print("[Thread1] Commit Successful", flush=True)

    except mysql.connector.Error as err:
        print("[Thread1] Conflict occurs: {}".format(err), flush=True)
        print("[Thread1] Retry", flush=True)

        try:
            cur1.execute("BEGIN OPTIMISTIC;")
            print("[Thread1 retry] BEGIN OPTIMISTIC", flush=True)

            cur1.execute("UPDATE users SET name = 'conn1' WHERE id = 1;")
            print("[Thread1 retry] UPDATED row → name='conn1'", flush=True)

            cur1.execute("COMMIT;")
            print("[Thread1 retry] Commit successful", flush=True)

        except mysql.connector.Error as err:
            print("[Thread1 retry] Conflict occurs: {}".format(err), flush=True)
  ```

### 手順 4. 楽観的モードでデモを実行する

+ 以下のコマンドを使用して楽観的ロックのデモコードを実行できます。`threading.Event`を使用して実行順序を制御していないため、多くの可能な出力があります。ここでは3つの典型的な出力を紹介します。最初のサンプル出力では、`Thread 2`が競合を起こし、その後再試行しています。2番目のサンプル出力では、`Thread 1`が競合を起こしています。3番目のサンプル出力では、競合は発生していません。

+ ただし、このシナリオではトランザクションロックの待機は発生しません。競合が発生した場合、その解決はあなたの裁量に委ねられます。このデモでは、単純な再試行が実行されます。

  ```bash
  poetry run python mysql_connector_python_demo/lock_optimistic.py
  ``` 

  ```output
  [Thread1] BEGIN OPTIMISTIC
  [Thread1] UPDATED row → name='conn1'
  [Thread1] UPDATED row → name='conn1'
  [Thread1] Try to commit
  [Thread2] BEGIN OPTIMISTIC
  [Thread1] Commit Successful
  [Thread2] UPDATED row → name='conn2'
  [Thread2] Try to commit
  [Thread2] Conflict occurs: 9007 (HY000): Write conflict, txnStartTS=458156067333603352, conflictStartTS=458156067333603338, conflictCommitTS=458156067333603359, key=????, reason=Optimistic [try again later]
  [Thread2] Retry
  [Thread2 retry] BEGIN OPTIMISTIC
  [Thread2 retry] UPDATED row → name='conn2'
  [Thread2 retry] Commit successful
  Update Result: name is changed to conn2
  ```

  ```output
  [Thread2] BEGIN OPTIMISTIC
  [Thread2] UPDATED row → name='conn2'
  [Thread2] Try to commit
  [Thread1] BEGIN OPTIMISTIC
  [Thread2] Commit Successful
  [Thread1] UPDATED row → name='conn1'
  [Thread1] UPDATED row → name='conn1'
  [Thread1] Try to commit
  [Thread1] Conflict occurs: 9007 (HY000): Write conflict, txnStartTS=458156066350563402, conflictStartTS=458156066350563397, conflictCommitTS=458156066350563406, key=????, reason=Optimistic [try again later]
  [Thread1] Retry
  [Thread1 retry] BEGIN OPTIMISTIC
  [Thread1 retry] UPDATED row → name='conn1'
  [Thread1 retry] Commit successful
  Update Result: name is changed to conn1
  ```

  ```output
  [Thread2] BEGIN OPTIMISTIC
  [Thread2] UPDATED row → name='conn2'
  [Thread2] Try to commit
  [Thread2] Commit Successful
  [Thread1] BEGIN OPTIMISTIC
  [Thread1] UPDATED row → name='conn1'
  [Thread1] UPDATED row → name='conn1'
  [Thread1] Try to commit
  [Thread1] Commit Successful
  Update Result: name is changed to conn1
  ```
  
### 手順 5. TiDB Cloud コンソールにアクセスして独自の旅を始めましょう

+ ハンズオンラボはここで終了します。今すぐ[ここ](https://tidbcloud.com/clusters)でTiDB Cloudの旅を始めることができます。このラボで独自のフリーティアクラスターを使用していた場合、同じページでクラスター名${LAB:SERVERLESS_CLUSTER_NAME}でリストされているのが見つかります。

## 結論

この演習では、Pythonコードを使用してTiDBクラスタに接続する方法を学びました。また、`mysql-connector-python`ライブラリを使用してTiDBと対話する方法も学びました。接続、挿入、単一クエリ、複数クエリなどが含まれます。

次に、アプリケーションのパフォーマンスを向上させるためのプリペアドステートメントとバッチ挿入について学びました。

その後、ホットスポットを回避するための`AUTO_RANDOM`の使用方法と、ビジネスを中断することなくオンラインスキーマ変更を実行する方法を学びました。

最後に、TiDBにおける同時実行制御のための悲観的ロックと楽観的ロックについて紹介しました。

```hbsptform
{
  "ui": {
    "titleLevel": 3
  },
  "portalId": "4466002",
  "formId": "62bfb32a-a60a-4f9d-b2dc-1107a93db568",
  "region": "na1",
  "sfdcCampaignId": "701RC00000NdFlAYAV"
}
```