DEPLOY.md 11.7 KB

parking-cloud · 数据库部署包

一句话:在一台干净的机器上,只用 sql/ 下这几个 .sql,就能建出一个 能把 parking-server 跑起来的库。不追求数据一致性,表结构齐、种子数据在,就够了。


1. 文件分工

文件 内容 表数 体积 必须? 来源
00_create_database.sql 建库 parking_cloud(utf8mb4 / general_ci),幂等且自纠 0 2 KB ✅ 手写
01_system_schema.sql 13 张 system_* 表结构 13 25 KB ✅ 手写(P1)
02_system_seed.sql system_* 种子数据(管理员、菜单、字典、OAuth2 客户端) — 14 KB ✅ 手写(P1)
03_legacy_schema.sql 679 张存量表结构(六库合一后的全部) 679 1.5 MB ✅ 生成物
04_legacy_sample.sql 679 张存量表的抽样数据(595 张有行) — 56 MB ⬜ 可选 生成物

00 + 01 + 02 + 03 = 692 张表(679 存量 + 13 新建)。 03 / 04 都不含 system_*,与 01 / 02 零重叠。

只用前四个文件就能跑起来;04 的作用只是"让列表页有东西可看"。


2. 前置条件

项 要求
MySQL 5.7 或 8.0 均可(见 §5)
Redis 必须。登录令牌与缓存都走 Redis
磁盘 脚本本身约 1.5 MB(04 另占 56 MB)。建出来的库:只建结构 30.2 MB,加 04 后 87.2 MB(实测)
账号权限 建库 + 建表 + 写数据的普通账号即可,不需要 SUPER / PROCESS

3. 执行

# ① 建库(注意:这一步不指定默认库,因为库还不存在)
mysql -h<host> -u<user> -p < 00_create_database.sql

# ② 建 13 张 system_* 表 + 灌种子
mysql -h<host> -u<user> -p parking_cloud < 01_system_schema.sql
mysql -h<host> -u<user> -p parking_cloud < 02_system_seed.sql

# ③ 建 679 张存量表
mysql -h<host> -u<user> -p parking_cloud < 03_legacy_schema.sql

# ④ 可选:抽样数据
mysql -h<host> -u<user> -p parking_cloud < 04_legacy_sample.sql

本机实测总耗时 8 秒(03 用时 3 s / 04 用时 5 s)。

⚠️ 03 每条都是 DROP TABLE IF EXISTS + CREATE TABLE,所以它可重复执行, 但重跑会清掉这些表里的数据。对着已有数据的库执行前先想清楚。


4. ⚠️ 04 里有真实生产数据

04 是真实生产库抽出来的行——含客户手机号、车牌、姓名。

  • 不随仓库提交:.gitignore 已排除该文件,git status 里不该出现它
  • 只放开发/测试机:不要往生产或对外环境灌
  • 不需要它也能跑:02 给了 system_* 种子,04 只影响存量业务表有没有数据
  • 密钥类表天然是空的:21 张含密钥/口令的表(struct_only_tables.txt)在本地镜像里 本来就只搬结构,所以 04 里它们一行都没有——不存在密钥泄漏

5. 为什么脚本里写死了排序规则 utf8mb4_general_ci

不写死会出事。 DEFAULT CHARSET = utf8mb4 而不带 COLLATE 时,MySQL 取的是 「该字符集在本服务端的默认排序规则」:

目标服务端 utf8mb4 默认排序规则
MySQL 5.7 utf8mb4_general_ci
MySQL 8.0 / 本地新服务端 utf8mb4_0900_ai_ci

于是同一套脚本会在不同的机器上建出两种不同排序规则的库。更糟的是同一个库内混用: 03 的存量表是写死的 general_ci,01 如果只写 CHARSET 就会落到 0900_ai_ci, 两者一旦放在一起比较(跨表 JOIN、字符串 =),可能直接 Illegal mix of collations。本地镜像就真实踩中过:663 / 692 张表、6278 个列的排序规则 当时是 0900_ai_ci(存量表 650 张 / 6197 列,system_* 13 张 / 81 列),已于 2026-10-10 按本包重建修掉。

所以:

  • 03 由生成器统一写成 COLLATE=utf8mb4_general_ci
  • 01 的 13 张表逐张显式写 COLLATE = utf8mb4_general_ci
  • 00 除了 CREATE DATABASE IF NOT EXISTS,还补了一条 ALTER DATABASE ... COLLATE—— 因为 IF NOT EXISTS 在库已存在时是空操作,不会修正它的排序规则

⚠️ 顺带记一条看着改了其实没改的坑:ALTER TABLE … DEFAULT CHARACTER SET … COLLATE … 只改表默认值、不动已有列——跑完那个 Illegal mix 照样报(实测)。即已经脏掉的库 改不回来,只能重建(或逐表 CONVERT TO)。本次处理成"整库重建"而不是打补丁,就是因为这个。

utf8mb4_0900_ai_ci 是 MySQL 8.0 专有排序规则,5.7 上会直接 Unknown collation 建表失败。 生成器里有硬守卫:产出里出现 0900、AUTO_INCREMENT=、utf8mb3、latin1、 CHARSET=utf8(少了 mb4)等任一特征,就拒绝落盘。

⚠️ 诚实说明:本机只有 MySQL 26.7,没有 5.7 实例,所以"能在 5.7 上建"这一条 是静态守卫(扫 8.0 专有语法/排序规则)得出的,没有在真 5.7 上实测过。 首次往 5.7 目标机部署时,建议先在测试库跑一遍 03,看有没有 Unknown collation。


6. 启动与验收

数据库建好后,用环境变量把 parking-server 指过去(不改任何配置文件):

export DB_HOST=127.0.0.1 DB_PORT=3306 DB_NAME=parking_cloud
export DB_USERNAME=root DB_PASSWORD=''        # 生产环境务必用真实账号
export REDIS_HOST=127.0.0.1 REDIS_PORT=6379

java -jar parking-server-<timestamp>.jar --spring.profiles.active=local

验收三连:

curl -s http://127.0.0.1:48080/actuator/health      # 期望 {"status":"UP"},且 db/redis 都是 UP
python3 tools/api_smoke.py                          # 期望「合计 66 条断言,通过 66,失败 0」
curl -s -X POST http://127.0.0.1:48080/admin-api/system/auth/login \
     -H 'Content-Type: application/json' \
     -d '{"username":"admin","password":"admin123"}'  # 期望 code=0

api_smoke.py 覆盖 /admin-api 全部 55 个接口:读接口给数据、写接口空 body 必须回 400、 6 个导出回真 xlsx、错误路径一律 4xx。判据是响应体里的 code,不是 HTTP 状态码。 (它会把你从浏览器里挤下线——本项目同一账号只允许一个会话,属既定语义。见脚本头部注释。)


7. ⚠️ 部署到别的服务器时的已知副作用

定时任务会自动启动,并主动访问外部系统。 ParkingServerApplication 上挂着 @EnableScheduling,且没有任何配置开关。启动日志里能直接看到:

SchedulerTaskPool-1 - 西城半小时刷新token:
SchedulerTaskPool-3 - 西城停设备状态20分钟上报一次

带来两件事:

  1. 会真的对外发请求(太极/西城平台、云快充、MQ…)。如果目标机不是该业务的运行环境, 这些请求是多余的,甚至可能干扰外部系统的状态
  2. 在只建表结构的库上,它们会持续报错刷日志(查存量表无数据、外部地址不通)

这不影响 API 可用——实测:结构-only 的库上,66 条断言照样全过。 若必须静默,两个办法(都不改共享仓库的代码):

  • 网络层:目标机不放行这些外部地址的出网
  • 代码层:在那个部署专用的构建里注掉 ParkingServerApplication 的 @EnableScheduling (⚠️ 属于业务改动,需单独评估,不要提交进主干)

8. 这个包已经验到什么程度

不是"导进去没报错"就算数——做的是逐列逐索引的等价性比对:

检查 方法 结果
表数 information_schema 计数 692 = 679 + 13 ✅
列 14418 行(表/序号/列名/类型/可否空/默认值/extra/注释/字符集/排序规则)逐行 diff 差异 0 ✅
索引 1928 行(索引名/序号/列/唯一性/类型/前缀长度)逐行 diff 差异 0 ✅
表选项 692 行(引擎/行格式/排序规则/建表选项/注释)逐行 diff 差异 0 ✅
分区 146 个分区定义(表/分区名/方式/表达式/边界)逐行 diff 差异 0 ✅
行数(精确 COUNT(*)) 679 张存量表逐张比对 逐张一致 ✅
启动 + 全接口 结构-only 库 与 结构+数据 库,各跑 api_smoke.py 66/66 × 2 ✅
往返(包 → 库 → 包) 用本包建出库,再拿该库跑一次生成器,与发布版逐字节比对 表数、679 条表选项行、04 完全相同;03 只多出 6150 个列级 COLLATE(由隐式继承改为显式书写),剥掉后逐字节相同 ✅

那些 diff 是唯一有意的改动:源库镜像里 663 张表 / 6278 列的 0900_ai_ci → 目标 general_ci(§5)。 比对时把源库侧做了同样的归一,归一之外一行差异都没有。

system_* 里有 5 张表的行数对不上(system_login_log 53→0、system_operate_log 71→0、 两个 token 表 6→0、system_dict_type 6→5),这是对的:那些是源库里运行期累积的 登录/操作日志与令牌行,全新部署本就不该有。种子意义上的表(用户/角色/菜单/组织/字典数据) 行数分毫不差。

顺带确认的几件事:存量表外键 0 个(所以 04 的导入顺序无所谓)、 触发器/视图/存储过程/事件均为 0、5 张分区表(共 146 个分区)的分区定义原样保留。


9. 重新生成 03 / 04

03 / 04 是生成物,源头是本地那个 679 张表的镜像库 parking_cloud。 改了镜像就必须重跑生成器(不要手改生成物——下次重跑就没了):

bash sql/tools/gen_deploy_sql.sh              # 只出 03
bash sql/tools/gen_deploy_sql.sh --with-data  # 03 + 04

生成器自带自检,任一不满足就拒绝落盘(宁可不出文件,也不出去坏文件)。包括: 表数与库内总数对得上、无 system_* 混入、无 8.0 专有排序规则、无 AUTO_INCREMENT=、 字符集声明已归一、分区表仍是 5 张、两次生成 md5 一致(产出可复现)。

可用环境变量覆盖连接参数:DEPLOY_DB_HOST / DEPLOY_DB_PORT / DEPLOY_DB_USER / DEPLOY_DB_PASSWORD(走 MYSQL_PWD,不上命令行)/ DEPLOY_DB / MYSQL_BIN / DUMP_BIN。

本地镜像已于 2026-10-10 用本包重建(00→04,692 张表、0 处 0900_ai_ci), 所以现在重跑生成器是幂等的:03 的 md5 稳定为 7f28357dacfb522a1b283a9280b163f5。 重建前后 03 的差别只是列级 COLLATE 从隐式继承改成显式书写(干净镜像上 mysqldump 会把每个列的排序规则都打出来),语义不变——验证方式是「剥掉全部列级 COLLATE 后两份文件逐字节相同」。


10. 已知限制

限制 说明
库名只能是 parking_cloud 01 / 02 里有 USE parking_cloud; 写死。要换库名,得同时改 00 的建库名和这两个文件里的 USE(或把那两行删掉、由命令行指定默认库)
抽样口径 = 每表最多 500 行 这是本地镜像的抽样上限,04 只是把它导出。想改口径要重跑 sql/migration/10_dump_legacy.sh(SAMPLE=N)重建镜像后,再重跑生成器
没有一致性保证 表间无外键,04 是各表独立抽样——够"页面有数据",不够跑对账类逻辑
04 含 PII 见 §4。它没进版本库,但你要手动拷到别的机器时得自己把关
生产切换用的不是这套 本包面向"开发/测试/演示环境"。生产全量切换走 sql/migration/ 的 10 / 20 / 98 / 99,判据是行数相等,别混用(见 sql/migration/README.md)