2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローカル環境で目指す、理想のTiDB開発ライフ

Last updated at Posted at 2025-12-23

本記事は TiDB Advent Calendar 2025 の12/24の記事です。

はじめに

こんにちは、SREエンジニアの†satoken†です。普段はクラウドインフラサービスの構築・運用や業務効率化を中心に取り組んでいます。退勤した後や休日は技術カンファレンスや勉強会に参加するのが趣味で、ここ3年で参加したイベントは100回を超えていることに最近気づきました。また、TiDBのユーザーグループの運営メンバーとして、数ヶ月に1回の頻度でMeetUpイベントやハンズオンイベントを主催しております

近年、ゲーム業界においてTiDBが注目度が高まっており、採用事例も着実に増えてきました!

そこで、TiDBを採用したプロダクトの開発体制について考えてみたいと考えてみたいと思います。
最近の開発では、Dockerコンテナ化したフロントエンド、バックエンド、ミドルウェアのそれぞれの設定ファイルをチーム内で共有して進める会社も多いと思います。
TiDBはミドルウェアのデータベースになりますが、TiDBがMySQL互換であるため、MySQLサーバーで開発することも可能です。
しかし、そのような状況でTiDBのアーキテクチャを考慮せずにテーブル設計や機能開発を進めた場合、クラウド環境で初めてデータベースのマイグレーションエラーに遭遇したり、本番環境でパフォーマンスが悪化したりする可能性があり、場合によっては致命的な問題に発展することも考えられます。

そのため、ソースコードの開発時からTiDBのアーキテクチャを意識できるよう、ローカルでTiDBを起動したいと個人的に考えています。

私がローカルの開発環境構築で求めるのは、以下の要件です。

  • すぐに環境を削除したり再構築でき、トライアンドエラーが簡単にできる
  • TiDBに詳しくないメンバーでも簡単に起動できる技術スタックである
  • 設定ファイルが少ない

今回はその要件を満たす構成をまとめてみました。
本記事は、TiDBをDockerで起動し、Laravelアプリから接続を切り替える手順の例を紹介します。

注)単純にローカルにTiDBを構築するのならtiup playgroundで起動するのが手っ取り早いので、こちらも試していただけたらと思います。(公式ページ)

概要

  • TiDBをDockerで起動するためのdocker-compose.ymlと、それを用いた起動手順を紹介します
  • PHPのウェブフレームワークの1つであるLaravelにおいて、ローカルで起動したTiDBと接続する手順を紹介します

本記事の対象者

  • MySQL、Laravel、docker-composeで開発環境構築したことがある方

注意事項

  • 本記事の設定ファイルはそのまま使用していただければ動く想定ですが、サンプルとして記載しておりTiDBの各ノードの設定などは最適化しておりません

詳細

今回の実行環境

今回実行した環境は以下の通りです。ご参考ください。
端末はMacOS 15.6.1を使用しています。

$ mysql --version
mysql  Ver 8.0.44 for macos15.7 on arm64 (Homebrew)

$ docker --version
Docker version 24.0.2, build cb74dfc

$ docker-compose version
Docker Compose version v2.18.1

# Laravel用
$ php --version
PHP 8.4.10 (cli) (built: Jul  2 2025 02:22:42) (NTS)
Copyright (c) The PHP Group
Built by Homebrew
Zend Engine v4.4.10, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.10, Copyright (c), by Zend Technologies

$ composer --version
Composer version 2.8.10 2025-07-10 19:08:33
PHP version 8.4.10 (/opt/homebrew/Cellar/php/8.4.10/bin/php)
Run the "diagnose" command to get more detailed diagnostics output.

TiDBをDockerで起動する

ターミナルで作業用のフォルダを作成します

$ mkdir -p ~/workspace/tidb-docker
$ cd ~/workspace/tidb-docker

TiDB起動用のdocker-compose.ymlファイルを作成します。

$ touch docker-compose.yml

ファイル内容は以下の通りです。
各serviceのcommandを確認すると、互いの関係が把握できます。万が一エラーが発生した場合は、その関係を意識して設定を確認してみてください。

services:
  pd0:
    image: pingcap/pd:${TIDB_VERSION:-v8.5.0}
    container_name: tidb-pd0
    ports:
      - "${PD0_CLIENT_PORT:-2379}:2379"
      - "${PD0_PEER_PORT:-2380}:2380"
    volumes:
      - pd0_data:/data
      - ./config/pd.toml:/pd.toml:ro
    command:
      - --name=pd0
      - --client-urls=http://0.0.0.0:2379
      - --peer-urls=http://0.0.0.0:2380
      - --advertise-client-urls=http://pd0:2379
      - --advertise-peer-urls=http://pd0:2380
      - --initial-cluster=pd0=http://pd0:2380
      - --data-dir=/data
      - --config=/pd.toml
      - --log-file=
    networks:
      - tidb-network
    restart: unless-stopped

  tikv0:
    image: pingcap/tikv:${TIDB_VERSION:-v8.5.0}
    container_name: tidb-tikv0
    ports:
      - "${TIKV0_PORT:-20160}:20160"
    volumes:
      - tikv0_data:/data
      - ./config/tikv.toml:/tikv.toml:ro
    command:
      - --addr=0.0.0.0:20160
      - --advertise-addr=tikv0:20160
      - --data-dir=/data
      - --pd=pd0:2379
      - --config=/tikv.toml
      - --log-file=
    networks:
      - tidb-network
    depends_on:
      - pd0
    restart: unless-stopped

  tikv1:
    image: pingcap/tikv:${TIDB_VERSION:-v8.5.0}
    container_name: tidb-tikv1
    ports:
      - "${TIKV1_PORT:-20161}:20160"
    volumes:
      - tikv1_data:/data
      - ./config/tikv.toml:/tikv.toml:ro
    command:
      - --addr=0.0.0.0:20160
      - --advertise-addr=tikv1:20160
      - --data-dir=/data
      - --pd=pd0:2379
      - --config=/tikv.toml
      - --log-file=
    networks:
      - tidb-network
    depends_on:
      - pd0
    restart: unless-stopped

  tikv2:
    image: pingcap/tikv:${TIDB_VERSION:-v8.5.0}
    container_name: tidb-tikv2
    ports:
      - "${TIKV2_PORT:-20162}:20160"
    volumes:
      - tikv2_data:/data
      - ./config/tikv.toml:/tikv.toml:ro
    command:
      - --addr=0.0.0.0:20160
      - --advertise-addr=tikv2:20160
      - --data-dir=/data
      - --pd=pd0:2379
      - --config=/tikv.toml
      - --log-file=
    networks:
      - tidb-network
    depends_on:
      - pd0
    restart: unless-stopped

  tidb0:
    image: pingcap/tidb:${TIDB_VERSION:-v8.5.0}
    container_name: tidb-tidb0
    ports:
      - "${TIDB0_PORT:-4000}:4000"
      - "${TIDB0_STATUS_PORT:-10080}:10080"
    volumes:
      - ./config/tidb.toml:/tidb.toml:ro
    command:
      - --advertise-address=tidb0
      - --store=tikv
      - --path=pd0:2379
      - --config=/tidb.toml
      - --log-file=
    networks:
      - tidb-network
    depends_on:
      - pd0
      - tikv0
      - tikv1
      - tikv2
    restart: unless-stopped

  tidb1:
    image: pingcap/tidb:${TIDB_VERSION:-v8.5.0}
    container_name: tidb-tidb1
    ports:
      - "${TIDB1_PORT:-4001}:4000"
      - "${TIDB1_STATUS_PORT:-10081}:10080"
    volumes:
      - ./config/tidb.toml:/tidb.toml:ro
    command:
      - --advertise-address=tidb1
      - --store=tikv
      - --path=pd0:2379
      - --config=/tidb.toml
      - --log-file=
    networks:
      - tidb-network
    depends_on:
      - pd0
      - tikv0
      - tikv1
      - tikv2
    restart: unless-stopped

networks:
  tidb-network:
    driver: bridge

volumes:
  pd0_data:
  tikv0_data:
  tikv1_data:
  tikv2_data:

環境変数として以下のファイル .env を用意します

$ touch .env

内容は以下の通りです

# TiDB Version
TIDB_VERSION="v8.5.0"

# PD(Placement Driver) Ports
PD0_CLIENT_PORT=2379
PD0_PEER_PORT=2380

# TiKV Ports
TIKV0_PORT=20160
TIKV1_PORT=20161
TIKV2_PORT=20162

# TiDB Ports
TIDB0_PORT=4000
TIDB0_STATUS_PORT=10080
TIDB1_PORT=4001
TIDB1_STATUS_PORT=10081

最後に各コンテナにマウントするtomlファイルを作成します。

$ mkdir config
$ touch config/pd.toml
$ touch config/tikv.toml
$ touch config/tidb.toml

各ファイルには以下のように記載してください。

pd.toml

[replication]
max-replicas = 3
leader-schedule-limit = 4
region-schedule-limit = 2048

[schedule]
max-store-down-time = "30m"
high-space-ratio = 0.7
low-space-ratio = 0.8

[log]
# ログレベル: debug, info, warn, error
level = "info"

tikv.toml

[storage]
data-dir = "/data"

[server]
grpc-concurrency = 4
grpc-raft-conn-num = 1

[raftstore]
apply-pool-size = 2
store-pool-size = 2
region-split-check-diff = "6MiB"

[rocksdb]
max-background-jobs = 4

[log]
# ログレベル:trace, debug, info, warn, error
level = "info"

tidb.toml

[performance]
max-procs = 0
tcp-keep-alive = true

[prepared-plan-cache]
enabled = true
capacity = 100

[tikv-client]
grpc-connection-count = 4

[log]
# ログレベル: debug, info, warn, error, fatal
level = "info"

ファイルが用意できたら、docker-composeコマンドで起動します。
(注:docker-composeは自動的に.envファイルを読み込みます)

$ ls -al
-rw-r--r--@  1 sato-kenta  staff   267 Dec 22 20:25 .env
drwxr-xr-x@  5 sato-kenta  staff   160 Dec 22 00:51 config
-rw-r--r--@  1 sato-kenta  staff  3207 Dec 22 21:11 docker-compose.yml

$ docker-compose up -d

エラーが発生して停止していないかをdocker-compose psコマンドで確認します。

(注:すべてのコンテナが「Up」のステータスであれば正常に起動しています。ただし、停止していなくてもエラーが発生している場合があるため、以降の操作ができない場合はdocker-compose logsコマンドでエラーログを確認してください)

$ docker-compose ps
NAME                IMAGE                 COMMAND                  SERVICE             CREATED             STATUS              PORTS
tidb-pd0            pingcap/pd:v8.5.0     "/pd-server --name=p…"   pd0                 34 seconds ago      Up 32 seconds       0.0.0.0:2379-2380->2379-2380/tcp
tidb-tidb0          pingcap/tidb:v8.5.0   "/tidb-server --adve…"   tidb0               33 seconds ago      Up 31 seconds       0.0.0.0:4000->4000/tcp, 0.0.0.0:10080->10080/tcp
tidb-tidb1          pingcap/tidb:v8.5.0   "/tidb-server --adve…"   tidb1               33 seconds ago      Up 31 seconds       0.0.0.0:4001->4000/tcp, 0.0.0.0:10081->10080/tcp
tidb-tikv0          pingcap/tikv:v8.5.0   "/tikv-server --addr…"   tikv0               34 seconds ago      Up 32 seconds       0.0.0.0:20160->20160/tcp
tidb-tikv1          pingcap/tikv:v8.5.0   "/tikv-server --addr…"   tikv1               34 seconds ago      Up 31 seconds       0.0.0.0:20161->20160/tcp
tidb-tikv2          pingcap/tikv:v8.5.0   "/tikv-server --addr…"   tikv2               34 seconds ago      Up 31 seconds       0.0.0.0:20162->20160/tcp

起動したTiDBにmysqlクライアントで接続します。
自分の端末では、host名をlocalhostで指定した場合に接続できなかったため、明示的に127.0.0.1にしています。

$ mysql -h 127.0.0.1 -P 4000 -u root                                 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2311061516
Server version: 8.0.11-TiDB-v8.5.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 8.0 compatible

Copyright (c) 2000, 2025, Oracle and/or its affiliates.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| INFORMATION_SCHEMA |
| METRICS_SCHEMA     |
| PERFORMANCE_SCHEMA |
| mysql              |
| sys                |
| test               |
+--------------------+

TiDBが起動できていることを確認できました。

Laravel プロジェクトの作成

composerコマンドでLaravelのプロジェクトを作成します。

$ composer create-project laravel/laravel tidb-app    
Creating a "laravel/laravel" project at "./tidb-app"
Installing laravel/laravel (v12.11.0)

コマンドの出力では以下のような表示がされ、デフォルトでマイグレーションファイルのサンプルが作成されます。

今回はこちらを利用します。

> @php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
> @php artisan migrate --graceful --ansi

   INFO  Preparing database.  

  Creating migration table .......................................................................................... 2.61ms DONE

   INFO  Running migrations.  

  0001_01_01_000000_create_users_table .............................................................................. 2.66ms DONE
  0001_01_01_000001_create_cache_table .............................................................................. 0.95ms DONE
  0001_01_01_000002_create_jobs_table ............................................................................... 3.09ms DONE

Laravelの動作確認をします。

$ cd tidb-app 

$ php artisan serve    

   INFO  Server running on [http://127.0.0.1:8000].  

  Press Ctrl+C to stop the server

ブラウザからアクセスしてみてエラーが出ずに下記画面が表示されることを確認します
image.png

データベース接続設定の確認

作成したプロジェクト内のファイルツリーは以下のようになっています。

$ tree -L2   
.
├── .env
├── .env.sample
├── app
│   ├── Http
│   ├── Models
│   └── Providers
├── artisan
├── bootstrap
│   ├── app.php
│   ├── cache
│   └── providers.php
├── composer.json
├── composer.lock
├── config
│   ├── app.php
│   ├── auth.php
│   ├── cache.php
│   ├── database.php
,,,

config/database.php を確認します

# L19 あたり:環境変数を設定していない場合はsqlite が指定される
'default' => env('DB_CONNECTION', 'sqlite'),

# L32 あたり:connectionsの設定。デフォルトでsqlite, mysql, pgsql, sqlsrvなどが用意されている。
'connections' => [
        'sqlite' => [
            'driver' => 'sqlite',
            xxx
        ],

.envを確認します。現在はSQLiteを使用しているのでDB接続用の環境変数がコメントアウトされています

# デフォルトではsqliteを指定している
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

config/database.phpにTiDB接続用の設定を作成し、環境変数DB_CONNECTIONでそれを指定すれば良さそうです。

TiDBに接続する設定を行う

config/database.php で以下のように修正します

# L19 あたり:環境変数を設定していない場合はtidbの設定を使用するようにする
'default' => env('DB_CONNECTION', 'tidb'),

# L32 あたり:connectionsの設定にtidbを設定を追加する
'connections' => [
        'sqlite' => [
            'driver' => 'sqlite',
            xxx
        ],
        'tidb' => [
            'driver' => 'mysql',
            'url' => env('TIDB_URL'),
            'host' => env('TIDB_HOST', '127.0.0.1'),
            'port' => env('TIDB_PORT', '4000'),
            'database' => env('TIDB_DATABASE', 'sample_db'),
            'username' => env('TIDB_USERNAME', 'root'),
            'password' => env('TIDB_PASSWORD', ''),
            'unix_socket' => env('TIDB_SOCKET', ''),
            'charset' => env('TIDB_CHARSET', 'utf8mb4'),
            'collation' => env('TIDB_COLLATION', 'utf8mb4_unicode_ci'),
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => false,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('TIDB_ATTR_SSL_CA'),
            ]) : [],
        ],

.envを以下のように修正します。今回、データベース名はsample_dbとしました。

DB_CONNECTION=tidb

# 以下の環境変数を追加
TIDB_HOST=127.0.0.1
TIDB_PORT=4000
TIDB_DATABASE=sample_db
TIDB_USERNAME=root
TIDB_PASSWORD=

マイグレーションの実行

マイグレーション用のコマンドを実行します。
(注:Laravelは自動的に.envファイルを読み込みます)

$ php artisan migrate               

   WARN  The database 'sample_db' does not exist on the 'tidb' connection.  

 ┌ Would you like to create it? ────────────────────────────────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘

   INFO  Preparing database.  

  Creating migration table .............................. 531.89ms DONE

   INFO  Running migrations.  

  0001_01_01_000000_create_users_table ........................ 3s DONE
  0001_01_01_000001_create_cache_table ........................ 1s DONE
  0001_01_01_000002_create_jobs_table ......................... 2s DONE

mysqlクライアントでTiDBに接続し、マイグレートできていることを確認します

$ mysql -h 127.0.0.1 -P 4000 -u root
(TiDBであることを確認する)
Server version: 8.0.11-TiDB-v8.5.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 8.0 compatible


mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| INFORMATION_SCHEMA |
| METRICS_SCHEMA     |
| PERFORMANCE_SCHEMA |
| mysql              |
| sample_db          |
| sys                |
| test               |
+--------------------+
7 rows in set (0.01 sec)

mysql> use sample_db;

mysql> show tables;
+-----------------------+
| Tables_in_sample_db   |
+-----------------------+
| cache                 |
| cache_locks           |
| failed_jobs           |
| job_batches           |
| jobs                  |
| migrations            |
| password_reset_tokens |
| sessions              |
| users                 |
+-----------------------+
9 rows in set (0.01 sec)

mysql> desc users;
+-------------------+-----------------+------+------+---------+----------------+
| Field             | Type            | Null | Key  | Default | Extra          |
+-------------------+-----------------+------+------+---------+----------------+
| id                | bigint unsigned | NO   | PRI  | NULL    | auto_increment |
| name              | varchar(255)    | NO   |      | NULL    |                |
| email             | varchar(255)    | NO   | UNI  | NULL    |                |
| email_verified_at | timestamp       | YES  |      | NULL    |                |
| password          | varchar(255)    | NO   |      | NULL    |                |
| remember_token    | varchar(100)    | YES  |      | NULL    |                |
| created_at        | timestamp       | YES  |      | NULL    |                |
| updated_at        | timestamp       | YES  |      | NULL    |                |
+-------------------+-----------------+------+------+---------+----------------+
8 rows in set (0.01 sec)

マイグレーションが実行できていることを確認できました。
これでローカル環境に起動したTiDBに接続するアプリが完成しました。

Laravelアプリからデータを更新できることを確認する

デフォルトではアプリに機能がないため、データ更新できるページを作成します。
今回はロジックやUIにはこだわらず、Cursorに以下のように依頼してコントローラーやビューファイルを作成してもらいました。

今のDBテーブルを活用できるページを作って下さい

追加された機能は以下の通りです。
一言依頼するだけでこれだけの機能が作成されるのは便利です。

1. コントローラー (UserController.php)
一覧表示 (index)
作成 (create, store)
詳細表示 (show)
編集 (edit, update)
削除 (destroy)
2. ルーティング (web.php)
リソースルートで全CRUD操作を定義
トップページをユーザー一覧にリダイレクト
3. ビューファイル
index.blade.php - ユーザー一覧(ページネーション対応)
create.blade.php - 新規ユーザー作成フォーム
edit.blade.php - ユーザー編集フォーム
show.blade.php - ユーザー詳細表示
機能
ユーザー一覧表示(ページネーション)
新規ユーザー作成
ユーザー編集(パスワードは任意)
ユーザー詳細表示
ユーザー削除(確認ダイアログ付き)
バリデーションエラー表示
成功メッセージ表示

ソースコードは今回の主旨ではないので割愛させていただきます

ブラウザ上からユーザー作成操作を行ってみます。
一覧画面は下のようになりました
image.png
image.png
下のように登録できました。
image.png

TiDB側を確認すると、データが登録されていることがわかります。
※ パスワードの箇所は念の為マスクしてあります

mysql> select * from users;
+----+--------------+---------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
| id | name         | email               | email_verified_at | password                                                     | remember_token | created_at          | updated_at          |
+----+--------------+---------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
|  1 | さとけん     | sample@example.test | NULL              | xxx  | NULL           | 2025-12-22 04:00:30 | 2025-12-22 04:00:30 |
+----+--------------+---------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
1 row in set (0.03 sec)

補足ですが、EXPLAINを実行してみると、TiDB Cloudで実行した時と同様にTaskのところにcop[tikv]と表示され、TiDBであることを実感できます。

mysql> explain select * from users;
+-----------------------+---------+-----------+---------------+--------------------------------+
| id                    | estRows | task      | access object | operator info                  |
+-----------------------+---------+-----------+---------------+--------------------------------+
| TableReader_5         | 1.00    | root      |               | data:TableFullScan_4           |
| └─TableFullScan_4     | 1.00    | cop[tikv] | table:users   | keep order:false, stats:pseudo |
+-----------------------+---------+-----------+---------------+--------------------------------+
2 rows in set (0.01 sec)

また、2人目のユーザーを作成すると、新たに作成されたユーザーのid列の値が30001になっており、TiDBの分散環境におけるAUTO_INCREMENTの特性を実感できます。

mysql> select * from users;
+-------+--------------+---------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
| id    | name         | email               | email_verified_at | password                                                     | remember_token | created_at          | updated_at          |
+-------+--------------+---------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
|     1 | さとけん     | sample@example.test | NULL              | xxx | NULL           | 2025-12-22 04:00:30 | 2025-12-22 04:00:30 |
| 30001 | sasa         | sasas@example.test  | NULL              | xxx| NULL           | 2025-12-22 05:18:32 | 2025-12-22 05:18:32 |
+-------+--------------+---------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
2 rows in set (0.00 sec)

まとめ

TiDBをDockerで起動し、ローカルで起動したアプリと接続する手順を紹介しました。
docker-compose.ymlファイルは構成面でもう少し改善の余地があると思います。
データベース設定ファイルを少し変更するだけでTiDBに問題なく接続できるのは、MySQL互換性の高さを実感できる点ですね。

TiDBはMySQL互換とはいえ、パフォーマンスを十分に発揮してもらうためには、アーキテクチャを意識したテーブル設計が不可欠です。
トライアンドエラーのイテレーションを短時間で回せるよう、今回紹介したようなローカル環境を活用していきたいと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?