## 模块 3：将 Laravel 应用程序从 Aurora (MySQL) 迁移到 TiDB Cloud

在本模块中，你将完成从 Aurora (MySQL) 数据库到 TiDB Cloud Starter 数据库的表定义和数据迁移。

### 步骤 1. 更新 `.env` 文件中的 TiDB Cloud Starter 连接参数

你在启动实验时已经授权了 TiDB Cloud Starter，其连接参数已保存在 `.env` 文件中。现在我们需要更新 `.env` 文件以使用 TiDB Cloud Starter 的连接参数。

+ 对 `.env` 文件进行以下更改：
  - 删除本地 Aurora (MySQL) 连接参数
  - 取消注释 TiDB Cloud 集群连接参数
  - 保存文件

  ```properties
  # tidb cloud cluster
  DB_CONNECTION=mysql
  DB_PORT=${LAB:SERVERLESS_CLUSTER_PORT}
  DB_HOST=${LAB:SERVERLESS_CLUSTER_HOST}
  DB_USERNAME='${LAB:SERVERLESS_CLUSTER_USERNAME}'
  DB_PASSWORD='${LAB:SERVERLESS_CLUSTER_PASSWORD}'
  DB_DATABASE=${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  MYSQL_ATTR_SSL_CA="/etc/ssl/certs/ca-certificates.crt"
  ...
  ```

### 步骤 2. 在 TiDB Cloud 中准备表定义

+ 在 VS Code 终端中，使用 `php artisan migrate` 命令在 TiDB Cloud 中创建所需的表。

   ```bash
   php artisan migrate
   ```

   ```output
   INFO  Preparing database.

   Creating migration table ........................................................................................................... 843.87ms DONE

   INFO  Running migrations.

   0001_01_01_000000_create_users_table .................................................................................................... 15s DONE
   0001_01_01_000001_create_cache_table ..................................................................................................... 1s DONE
   0001_01_01_000002_create_jobs_table ...................................................................................................... 8s DONE
   2025_12_20_112907_create_transactions_table .............................................................................................. 7s DONE
   2025_12_20_120002_create_products_table .................................................................................................. 3s DONE
   2026_01_03_040247_create_product_type_table ........................................................................................ 547.59ms DONE
   2026_01_03_040331_create_pay_types_table ........................................................................................... 651.96ms DONE
   ```

### 步骤 3. 将表的 `AUTO_INCREMENT` 属性修改为 `AUTO_RANDOM` 以防止数据热点

+ 在 VS Code 终端中，使用 MySQL 客户端连接到 TiDB Cloud。

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

   ```output
   Your MySQL connection id is 61317775
   Server version: 8.0.11-TiDB-v7.5.2-serverless TiDB Server (Apache License 2.0) Community Edition, MySQL 8.0 compatible

   Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

   Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]>
   ```

+ 在 VS Code 终端中，使用 MySQL 客户端查看表 `shop.products` 和 `shop.transactions` 的定义。VS Code 会弹出一个窗口询问 "Are you sure you want to paste 2 lines of text into the terminal?"，请点击 "Paste"。

   ![204_3_3_1.png 85%](https://lab-static.pingcap.com/images/2026/2/4/e5c082347f448be9f8db4c13b4952a204feb5346.png)

   ```sql
   SHOW CREATE TABLE products\G
   SHOW CREATE TABLE transactions\G
   ```

   ```output
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]> SHOW CREATE TABLE products\G
   *************************** 1. row ***************************
         Table: products
   Create Table: CREATE TABLE `products` (
   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `status` varchar(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'C',
   `name` varchar(80) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   `introduction` text COLLATE utf8mb4_unicode_ci NOT NULL,
   `photo` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   `price` decimal(10,2) NOT NULL DEFAULT '0',
   `remain_count` int(11) NOT NULL DEFAULT '0',
   `created_at` timestamp NULL DEFAULT NULL,
   `updated_at` timestamp NULL DEFAULT NULL,
   `product_type` int(11) NOT NULL DEFAULT '1',
   PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
   KEY `idx_name_product` (`name`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
   1 row in set (0.163 sec)

   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]>
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]> SHOW CREATE TABLE transactions\G
   *************************** 1. row ***************************
         Table: transactions
   Create Table: CREATE TABLE `transactions` (
   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `user_id` bigint(20) unsigned NOT NULL,
   `product_id` bigint(20) unsigned NOT NULL,
   `price` decimal(10,2) NOT NULL,
   `buy_count` int(11) NOT NULL,
   `total_price` decimal(18,2) NOT NULL,
   `created_at` timestamp NULL DEFAULT NULL,
   `updated_at` timestamp NULL DEFAULT NULL,
   `pay_type` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1',
   PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
   KEY `user_transaction_idx` (`user_id`),
   KEY `product_transaction_idx` (`product_id`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
   1 row in set (0.163 sec)
   ```

> [!TIP]
>
> + TiDB Cloud Starter 是一个分布式数据库。建议将表 `SHOP`.`PRODUCTS` 和 `SHOP`.`TRANSACTIONS` 的主键 `AUTO_INCREMENT` 属性更改为 `AUTO_RANDOM`，以避免热点。
> + 请注意，此步骤是可选的。跳过此步骤不会影响迁移结果。
> + 此外，如果你选择修改，必须删除并重新创建这 2 个表，而不能使用文档中提到的 `ALTER TABLE t MODIFY COLUMN id BIGINT AUTO_RANDOM(5);` 方法。原因有两个：
> + 首先，如果 id 列的 BIGINT 类型的所有 64 位都是有效的，由于 PHP 语言中 `PHP_INT_MAX` 对整数的限制，后续应用程序中可能会出现整数溢出错误。
> + 其次，如果 id 列使用小于 64 位的设置（如 `AUTO_RANDOM(3, 32)`），`ALTER TABLE t MODIFY COLUMN` 命令会认为整数范围已缩小，可能导致数据丢失，从而产生错误。

+ 在 VS Code 终端中，使用 MySQL 客户端重建空表 `shop.products` 和 `shop.transactions`，并将表 `shop.products` 和 `shop.transactions` 的主键 `AUTO_INCREMENT` 属性更改为 `AUTO_RANDOM`。VS Code 会弹出一个窗口询问 "Are you sure you want to paste 33 lines of text into the terminal?"，请点击 "Paste"。

   ![204_3_3_2.png 85%](https://lab-static.pingcap.com/images/2026/2/6/55971cef685fd59926c137c5ebe555f6663fdcb0.png)

   ```sql
   DROP TABLE `products`;

   CREATE TABLE `products` (
   `id` bigint(20) unsigned NOT NULL /*T![auto_rand] AUTO_RANDOM(3, 32) */,
   `status` varchar(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'C',
   `name` varchar(80) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   `introduction` text COLLATE utf8mb4_unicode_ci NOT NULL,
   `photo` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   `price` decimal(10,2) NOT NULL DEFAULT '0',
   `remain_count` int(11) NOT NULL DEFAULT '0',
   `created_at` timestamp NULL DEFAULT NULL,
   `updated_at` timestamp NULL DEFAULT NULL,
   `product_type` int(11) NOT NULL DEFAULT '1',
   PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
   KEY `idx_name_product` (`name`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

   DROP TABLE `transactions`;

   CREATE TABLE `transactions` (
   `id` bigint(20) unsigned NOT NULL /*T![auto_rand] AUTO_RANDOM(3, 32) */,
   `user_id` bigint(20) unsigned NOT NULL,
   `product_id` bigint(20) unsigned NOT NULL,
   `price` decimal(10,2) NOT NULL,
   `buy_count` int(11) NOT NULL,
   `total_price` decimal(18,2) NOT NULL,
   `created_at` timestamp NULL DEFAULT NULL,
   `updated_at` timestamp NULL DEFAULT NULL,
   `pay_type` bigint(20) unsigned NOT NULL DEFAULT 1,
   PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
   KEY `user_transaction_idx` (`user_id`),
   KEY `product_transaction_idx` (`product_id`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
   ```

   ```output
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]>
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]> CREATE TABLE `products` (
      -> `id` bigint(20) unsigned NOT NULL /*T![auto_rand] AUTO_RANDOM(3, 32) */,
      -> `status` varchar(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'C',
      -> `name` varchar(80) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
      -> `introduction` text COLLATE utf8mb4_unicode_ci NOT NULL,
      -> `photo` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
      -> `price` decimal(10,2) NOT NULL DEFAULT '0',
      -> `remain_count` int(11) NOT NULL DEFAULT '0',
      -> `created_at` timestamp NULL DEFAULT NULL,
      -> `updated_at` timestamp NULL DEFAULT NULL,
      -> `product_type` int(11) NOT NULL DEFAULT '1',
      -> PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
      -> KEY `idx_name_product` (`name`)
      -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
   Query OK, 0 rows affected, 1 warning (0.407 sec)

   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]>
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]> DROP TABLE `transactions`;
   Query OK, 0 rows affected (0.559 sec)

   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]>
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]> CREATE TABLE `transactions` (
      -> `id` bigint(20) unsigned NOT NULL /*T![auto_rand] AUTO_RANDOM(3, 32) */,
      -> `user_id` bigint(20) unsigned NOT NULL,
      -> `product_id` bigint(20) unsigned NOT NULL,
      -> `price` decimal(10,2) NOT NULL,
      -> `buy_count` int(11) NOT NULL,
      -> `total_price` decimal(18,2) NOT NULL,
      -> `created_at` timestamp NULL DEFAULT NULL,
      -> `updated_at` timestamp NULL DEFAULT NULL,
      -> `pay_type` bigint(20) unsigned NOT NULL DEFAULT 1,
      -> PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
      -> KEY `user_transaction_idx` (`user_id`),
      -> KEY `product_transaction_idx` (`product_id`)
      -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
   Query OK, 0 rows affected, 1 warning (0.384 sec)

   ```

+ 在 VS Code 终端中，退出 MySQL 客户端以准备数据迁移。

   ```sql
   EXIT
   ```

   ```output
   MySQL [${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}]> EXIT
   Bye
   root@0c2c53eace8f:~/Mshop#
   ```

### 步骤 4. 数据迁移

+ 在 VS Code 终端中，使用 Dumpling 工具将 shop 数据库下所需表的数据导出为 SQL 文件。

   ```bash
   tiup dumpling -u ${LAB:AWS_AURORA_SERVERLESS_MASTER_USERNAME} -p${LAB:AWS_AURORA_SERVERLESS_MASTER_PASSWORD} -h ${LAB:AWS_AURORA_SERVERLESS_ENDPOINT} -P 3306 -B {SERVERLESS_CLUSTER_DATABASE_NAME} \
   -T ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}.users \
   -T ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}.products \
   -T ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}.transactions \
   -T ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}.product_type \
   -T ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}.pay_type \
   --filetype sql \
   -o ./dumpling_output
   ```

   ```output
   Checking updates for component dumpling... Timedout (after 2s)
   The component `dumpling` version  is not installed; downloading from repository.
   ...
   [2026/01/21 05:10:11.018 +00:00] [INFO] [dump.go:276] ["All the dumping transactions have started. Start to unlock tables"]
   [2026/01/21 05:10:11.048 +00:00] [INFO] [collector.go:264] ["backup success summary"] [total-ranges=11] [ranges-succeed=11] [ranges-failed=0] [total-take=29.166482ms] [total-kv-size=3.933kB] [average-speed=134.8kB/s] [total-rows=35]
   [2026/01/21 05:10:11.049 +00:00] [INFO] [main.go:82] ["dump data successfully, dumpling will exit now"]
   ```

+ 在 VS Code 终端中，使用 TiDB Lightning 工具将从 Aurora (MySQL) 数据库导出的数据导入到 TiDB Cloud。

   ```bash
   cat > .lightning_config <<EOF
   [mydumper]
   data-source-dir = "/root/Mshop/dumpling_output"

   [tidb]
   host = "${LAB:SERVERLESS_CLUSTER_HOST}"
   port = ${LAB:SERVERLESS_CLUSTER_PORT}
   user = "${LAB:SERVERLESS_CLUSTER_USERNAME}"
   password = "${LAB:SERVERLESS_CLUSTER_PASSWORD}"
   tls = "cluster"

   [security]
   ca-path = "/etc/ssl/certs/ca-certificates.crt"
   EOF

   tiup tidb-lightning --backend=tidb --check-requirements=false --config .lightning_config
   ```

   ```output
   Starting component tidb-lightning: /root/.tiup/components/tidb-lightning/v8.5.5/tidb-lightning --backend=tidb --check-requirements=false --config /dev/fd/63
   Verbose debug logs will be written to /tmp/lightning.log.2026-01-18T05.20.02Z

   tidb lightning exit successfully
   ```

### 步骤 5. 启动基于 TiDB Cloud 的 Laravel 应用程序

+ 在 VS Code 终端中，运行命令 `composer run dev` 启动 Laravel 应用程序。

  ```bash
  composer run dev
  ```

  ```output
   > Composer\Config::disableProcessTimeout
   > @php artisan serve --host=0.0.0.0

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

   Press Ctrl+C to stop the server
  ```

+ 将弹出一个窗口提示 "Your application running on port 8000 is available."，请点击 "Open in Browser"。

  ![204_1_port_8000.png 85%](https://lab-static.pingcap.com/images/2026/2/5/4f681e3a7458c7631f2187c53100aa127e5bcaf4.png)

+ 继续下一个模块。
