3. 数据库环境搭建与工程初始化

在进入 SQL 工程化开发之前,第一步不是写查询,而是把“数据库运行环境”和“项目工程骨架”同时立起来。环境搭建解决的是“数据库能不能稳定跑、能不能连上、字符集与时区是否正确”;工程初始化解决的是“项目数据库、账号权限、版本化脚本、初始化数据是否可复现”。这两件事如果一开始没做扎实,后面写再多 SQL 都会在协作、迁移和排障时反复付出代价。

一、安装与配置:MySQL 与 PostgreSQL 的差异点

MySQL 与 PostgreSQL 都是主流关系型数据库,但初始化时的关注点不同。MySQL 常见安装方式包括官方 APT/YUM 仓库、Docker 镜像和二进制包;PostgreSQL 同样支持包管理器与 Docker。无论哪种方式,安装后都要确认三件事:服务是否随系统启动、监听地址与端口是否正确、默认字符集与排序规则是否符合项目要求。

以 MySQL 8.0 为例,安装后建议在 my.cnf 中显式设置字符集与排序规则,避免建库时依赖默认值:

[mysqld]
character-set-server = utf8mb4
collation-server     = utf8mb4_0900_ai_ci
default-time-zone    = '+08:00'
bind-address         = 0.0.0.0
port                 = 3306

PostgreSQL 的配置集中在 postgresql.conf 与 pg_hba.conf。前者控制监听地址、端口、时区,后者控制客户端认证方式。一个常见的初始化片段如下:

# postgresql.conf
listen_addresses = '*'
port = 5432
timezone = 'Asia/Shanghai'
# pg_hba.conf
host    all    all    0.0.0.0/0    scram-sha-256

配置完成后,MySQL 用 systemctl restart mysql,PostgreSQL 用 systemctl restart postgresql 使配置生效。随后用 mysql -u root -p 或 psql -U postgres -h 127.0.0.1 验证连接。若连接失败,优先检查端口监听、防火墙和认证方式,而不是直接改密码。

二、创建项目数据库与最小权限账号

工程化项目不应让应用直接使用 root 或 postgres 超级用户。正确做法是:为项目创建独立数据库,再创建只拥有该库权限的应用账号。以 MySQL 为例:

CREATE DATABASE shop_dev
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_0900_ai_ci;

CREATE USER 'shop_app'@'%' IDENTIFIED BY 'Shop_Dev_2024!';
GRANT SELECT, INSERT, UPDATE, DELETE,
      CREATE, ALTER, INDEX, REFERENCES
ON shop_dev.* TO 'shop_app'@'%';
FLUSH PRIVILEGES;

PostgreSQL 中数据库与 schema 是两层概念,通常先建角色再建库:

CREATE ROLE shop_app LOGIN PASSWORD 'Shop_Dev_2024!';
CREATE DATABASE shop_dev OWNER shop_app
  ENCODING 'UTF8' LC_COLLATE 'zh_CN.UTF-8'
  LC_CTYPE 'zh_CN.UTF-8' TEMPLATE template0;

这里的关键判断是:应用账号只应拥有业务库的 DML 与必要 DDL 权限,不应拥有 SUPER、CREATEDB 或跨库访问权限。这样即使应用被注入,攻击面也被限制在单个库内。

三、版本化脚本:让数据库结构可追溯

版本化脚本是工程初始化的核心。推荐目录结构如下:

db/
├── migrations/
│   ├── V1__init_schema.sql
│   ├── V2__add_order_index.sql
│   └── V3__create_user_profile.sql
├── seed/
│   └── R__seed_dict_data.sql
└── README.md

命名规则建议采用 V{版本号}__{描述}.sql,版本号单调递增,已执行的脚本不再修改,只能新增。这样任何环境都能按顺序重放,得到一致结构。初始化脚本 V1__init_schema.sql 应包含建表、主键、外键、唯一约束和基础索引,例如:

CREATE TABLE customer (
  id          BIGINT PRIMARY KEY AUTO_INCREMENT,
  name        VARCHAR(64)  NOT NULL,
  phone       VARCHAR(20)  NOT NULL UNIQUE,
  created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE orders (
  id           BIGINT PRIMARY KEY AUTO_INCREMENT,
  customer_id  BIGINT NOT NULL,
  amount       DECIMAL(12,2) NOT NULL,
  status       VARCHAR(16) NOT NULL DEFAULT 'CREATED',
  created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_orders_customer
    FOREIGN KEY (customer_id) REFERENCES customer(id)
);

执行迁移时,可用 Flyway、Liquibase 等工具,也可用脚本按文件名排序依次执行。无论哪种方式,都要在数据库中记录已执行版本,避免重复执行导致报错。

四、初始化数据:可重复、可回滚、可校验

初始化数据分为两类:字典/配置类基础数据,以及开发调试用的样例数据。基础数据应使用幂等写法,例如 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或 INSERT ... ON CONFLICT DO NOTHING(PostgreSQL),保证重复执行不产生重复行。样例数据则建议单独放在 seed/ 目录,仅在开发环境执行。

INSERT INTO dict_status (code, label)
VALUES ('CREATED', '已创建'),
       ('PAID',    '已支付'),
       ('SHIPPED', '已发货')
ON DUPLICATE KEY UPDATE label = VALUES(label);

执行完成后,用一组校验查询确认结果:表数量、关键表行数、字符集、时区、账号权限。例如 MySQL 中可执行 SHOW VARIABLES LIKE 'character_set_server'; 与 SELECT COUNT(*) FROM dict_status;。只有这些检查通过,环境初始化才算真正完成。

数据库环境搭建与工程初始化链路 ① 安装与配置 MySQL / PostgreSQL 字符集 utf8mb4 / UTF8 时区 +08:00 / Asia/Shanghai 端口 3306 / 5432 ② 建库与建号 CREATE DATABASE shop_dev CREATE USER shop_app 最小权限 GRANT 禁止 SUPER / CREATEDB ③ 版本化脚本 V1__init_schema.sql V2__add_order_index.sql 版本号单调递增 已执行脚本不可修改 ④ 初始化数据 字典 / 配置 幂等 INSERT seed 样例数据 仅开发环境 校验与验证(初始化完成的判断依据) SHOW VARIABLES LIKE 'character_set_server' SELECT COUNT(*) FROM dict_status SHOW GRANTS FOR 'shop_app'@'%' 表数量、关键行数、字符集、时区、账号权限全部符合预期 工程目录结构(可复现的初始化骨架) db/migrations/ V1__init_schema.sql V2__add_order_index.sql seed/ 按文件名排序执行 → 记录已执行版本 → 新环境重放得到一致结构 开发 / 测试 / 生产环境使用同一套迁移脚本,差异仅体现在 seed 数据

五、常见初始化问题与判断依据

初始化阶段最容易踩的坑集中在四类。第一类是字符集不一致:MySQL 服务端为 latin1 而客户端为 utf8mb4,导致中文乱码;判断方法是执行 SHOW VARIABLES LIKE 'character_set%';,确认 character_set_server、character_set_client、character_set_connection 三者一致。第二类是时区偏差:应用写入时间与数据库 NOW() 相差 8 小时,需检查 default-time-zone 或 timezone 参数。

第三类是权限过大或过小:权限过大带来安全风险,过小则导致迁移脚本执行失败。建议在初始化完成后,用应用账号实际执行一次建表、插入、查询、删除,确认权限边界正确。第四类是脚本重复执行:如果迁移脚本没有版本记录,第二次执行会因表已存在而中断。解决方式是在迁移工具中维护 schema_version 表,或使用 CREATE TABLE IF NOT EXISTS 等幂等写法。

初始化完成后,建议把“环境检查”固化为一条命令或一个脚本,例如 make db-check,让每位新成员在拉取代码后能一键确认环境是否就绪,而不是靠口头约定。

当数据库能稳定连接、项目库与最小权限账号就位、迁移脚本可按版本重放、初始化数据幂等可校验时,工程初始化才算真正完成。此时再进入建表规范、索引设计与 SQL 编写,才有可靠的工程底座。