文档目录
表结构优化

简介

本指南面向 DouPHP 项目的数据库表结构优化,聚焦以下目标:

  • 字段类型选择原则:INT、VARCHAR、TEXT、DECIMAL、DATETIME/DATE/TIME、ENUM 等类型的性能差异与适用场景。
  • 表分区策略:范围分区、列表分区、哈希分区的应用场景与落地方法。
  • 数据归档方案:历史数据处理、冷热分离、归档策略制定与执行路径。
  • 表结构设计最佳实践:范式化与反范式化的权衡、冗余字段设计、枚举值存储优化。
  • 结合项目现有表结构给出具体优化建议与可操作案例。

项目结构与数据层概览

DouPHP 的数据库定义集中在各模块的 SQL 备份文件中,并通过 ORM 模型进行读写封装;同时提供工具脚本用于历史数据迁移与一致性治理。

graph TB
A["业务模块<br/>订单/用户/商品"] --> B["SQL 建表脚本<br/>order.sql / user.sql / product.sql"]
B --> C["MySQL 引擎 InnoDB<br/>utf8mb4_unicode_ci"]
A --> D["ORM 模型<br/>Order.php / User.php"]
D --> E["ORM 基础能力<br/>Builder.php / Model.php"]
A --> F["运维与迁移工具<br/>generate_sql.php / upgrade.php"]

核心组件与数据流

  • 数据写入:控制器/服务通过 ORM 或 DB 门面写入,最终落到 InnoDB 表。
  • 数据读取:ORM Builder 链式构建查询,支持 with 预加载、聚合统计与分页。
  • 数据演进:通过 generate_sql.php 与 upgrade.php 完成字段类型统一、索引重建与兼容处理。
sequenceDiagram
participant App as "应用代码"
participant ORM as "ORM(Builder/Model)"
participant DB as "数据库连接"
participant TBL as "业务表"
App->>ORM : 构建查询/写入
ORM->>DB : 生成并执行SQL
DB->>TBL : 落盘/检索
TBL-->>DB : 结果集/影响行数
DB-->>ORM : 返回结果
ORM-->>App : 模型/集合/标量

架构总览

从“表结构—索引—查询—归档”的全链路视角,优化应围绕以下关键点展开:

  • 字段类型最小化且足够表达业务语义。
  • 索引覆盖高频查询与排序分组。
  • 大文本与日志表采用独立存储与归档策略。
  • 状态与分类使用 ENUM 或字典表,避免散落的字符串。
graph LR
Q["高频查询/统计"] --> I["索引设计"]
I --> T["表结构优化<br/>字段类型/冗余/枚举"]
T --> S["存储与分区"]
S --> A["归档与冷热分离"]
A --> M["监控与回归测试"]

详细组件分析

字段类型选择原则与实践

  • 主键与外键
    • 自增主键优先使用 INT/SMALLINT/MEDIUMINT/BIGINT 中满足范围的最小类型,减少索引体积与内存占用。
    • 关联 ID 使用无符号整型,避免负数误用。
  • 金额
    • 所有金额字段使用 DECIMAL(p,s),禁止 FLOAT/DOUBLE,保证精度一致性与计算正确性。
  • 时间
    • 统一使用 DATETIME/DATE/TIME,避免混合使用时间戳与日期类型;必要时通过迁移工具统一转换。
  • 短文本
    • 固定长度或上限明确的短文本使用 VARCHAR(n),n 尽量贴近实际最大长度,避免过度分配。
  • 长文本
    • 内容、备注、JSON 等使用 TEXT/LONGTEXT;对不常检索的大字段考虑拆分到扩展表或对象存储。
  • 布尔与状态
    • 二元状态使用 TINYINT(1) 或 ENUM;多态状态使用 ENUM 或字典表,便于约束与展示映射。
  • 唯一标识
    • 对外唯一编码(如订单号、会员编号)使用 VARCHAR,并建立 UNIQUE 索引。

索引设计与查询优化

  • 单列索引
    • 高频等值/范围查询列:如 status、created_at、paid_at、user_id。
  • 复合索引
    • 联合过滤与排序:如 (status, created_at)、(pay_id, status, paid_at)。
  • 前缀索引
    • 超长字符串仅使用前缀参与索引,降低索引大小。
  • 覆盖索引
    • 将常用 SELECT 列纳入索引,减少回表。
  • 去重与唯一性
    • 对外唯一码(订单号、支付流水号、会员编号)加 UNIQUE 索引。

表分区策略

  • 范围分区(RANGE)
    • 适用:按时间分区的日志、消息、统计表。例如按月份/季度对 dou_chat_message、dou_ai_usage_log 进行 RANGE 分区,便于按时间窗口清理与归档。
  • 列表分区(LIST)
    • 适用:有限枚举值的分区,如按 provider_id、chat_id 等维度做 LIST 分区,利于热点隔离与并行维护。
  • 哈希分区(HASH)
    • 适用:均匀打散写入压力,如按 user_id HASH 分区,提升高并发写入吞吐。
  • 注意事项
    • 分区键需与查询条件匹配,否则无法有效分区裁剪。
    • 分区后索引限制与跨分区聚合成本需评估。
    • 建议在 MySQL 5.7+ 环境验证后再上线。

数据归档方案设计

  • 归档目标
    • 日志类(AI 调用日志、聊天消息)、统计类(每日用量)、历史订单明细。
  • 归档策略
    • 冷数据按月/季度迁移至归档库或归档表,保留热数据在在线库。
    • 使用定时任务(cron/队列)批量导出/导入,配合事务与校验。
  • 实施步骤
    • 确定归档键(如 created_at、stat_date)。
    • 创建归档表结构(与源表一致或精简)。
    • 分批迁移(LIMIT/OFFSET 或基于游标),每批提交事务。
    • 校验行数与关键指标,确认无误后删除源表旧数据。
  • 与项目工具的衔接
    • 参考 generate_sql.php 的时间字段统一与索引重建流程,确保归档前后索引一致。
    • 参考 upgrade.php 中的字段迁移模式,保证兼容性。

表结构设计最佳实践

  • 范式化与反范式化
    • 范式化:保持数据一致性,适合低频更新、强一致场景。
    • 反范式化:为读多写少的报表/统计增加冗余字段,减少 JOIN 与聚合成本。
  • 冗余字段设计
    • 在订单/订单条目中冗余必要展示字段(如商品名称、类目ID),避免频繁 JOIN 商品主表。
    • 注意冗余字段的一致性维护(触发器/事务/事件)。
  • 枚举值存储优化
    • 小范围稳定枚举使用 ENUM,便于约束与展示映射。
    • 复杂或易变枚举使用字典表,便于扩展与维护。
  • 软删除与审计
    • 引入 is_deleted、deleted_at 等字段实现软删除。
    • 对关键变更记录审计日志表,便于追溯。

依赖关系分析

  • 模型与表
    • Order.php 对应 order 及相关子表;User.php 对应 user 及相关子表。
  • ORM 与底层
    • Builder.php 负责链式查询与预加载;Model.php 提供水合、关系与批量写入白名单。
  • 配置与环境
    • config.php 指定数据库连接、字符集与表前缀,影响全库行为。
classDiagram
class Order {
+table = "order"
+primary = "id"
+casts
+fillable
+items()
+scopeFilterByXxx()
}
class User {
+table = "user"
+primary = "id"
+casts
+fillable
+level()
+scopeFilterByXxx()
}
class Builder {
+where()
+with()
+get()/first()/paginate()
}
class Model {
+fill()->save()
+create()
+destroy()
}
Order --> Model : "继承"
User --> Model : "继承"
Order --> Builder : "使用"
User --> Builder : "使用"

性能考量

  • 字段尺寸
    • 合理缩小字段尺寸,减少行宽,提高缓冲池命中率。
  • 索引命中
    • 通过 EXPLAIN 分析慢查询,补充或调整复合索引。
  • 大字段
    • 将大文本/图片路径拆分到扩展表或对象存储,主表只保留引用。
  • 统计与报表
    • 使用物化视图或汇总表(如 dou_chat_daily_stats)替代实时聚合。
  • 分区与归档
    • 对超大表启用分区,结合归档策略控制在线库规模。

故障排查指南

  • 时间字段不一致
    • 使用 generate_sql.php 生成的迁移脚本统一时间字段类型,并重建索引。
  • 字段迁移失败
    • 参考 upgrade.php 的增量迁移模式,先新增临时列,回填数据,再替换原列。
  • 查询缓慢
    • 检查是否存在缺失索引、索引失效(函数包裹列)、回表过多等问题。
  • 归档异常
    • 分批迁移并校验行数与关键指标,出现异常立即回滚并定位问题批次。

结论

通过对字段类型、索引、分区与归档的系统性优化,结合项目现有的 ORM 与迁移工具,可在不破坏既有业务逻辑的前提下显著提升数据库性能与可维护性。建议以“订单/用户/商品”为核心先行试点,逐步推广至日志与统计类表。

附录:表结构示例与优化案例

订单表优化要点

  • 字段类型
    • 金额使用 DECIMAL(10,2);时间使用 DATETIME;唯一订单号使用 VARCHAR(20) 并 UNIQUE。
  • 索引
    • 复合索引 (status, created_at) 支撑列表与统计;(pay_id, status, paid_at) 支撑支付相关查询。
  • 冗余
    • 订单条目表冗余商品名称、类目ID等展示字段,减少 JOIN。
  • 归档
    • 历史订单明细可按月份归档至独立库或表,保留最近 N 个月在线。

用户表优化要点

  • 字段类型
    • 手机号/邮箱使用 VARCHAR(80/180) 并 UNIQUE;密码使用 VARCHAR(255)。
  • 索引
    • 对 mobile、email 建立唯一索引;登录失败次数与锁定时间用于安全控制。
  • 冗余
    • 等级信息可通过关联表获取,避免在主表冗余过多等级属性。
  • 归档
    • 用户日志表可按时间分区或归档,保留近 N 月在线。

商品表优化要点

  • 字段类型
    • 价格使用 DECIMAL(10,2);标题/描述使用 VARCHAR/TEXT;图片路径使用 VARCHAR(255)。
  • 索引
    • slug 建立唯一索引;operator_type/operator_id 建立复合索引便于管理端筛选。
  • 冗余
    • 促销价、促销时间等冗余字段便于前端快速展示。
  • 归档
    • 商品详情大字段可考虑拆分到扩展表或对象存储。

日志与统计表的分区与归档

  • 日志表(如 AI 调用日志、聊天消息)
    • 按 created_at 范围分区,定期归档旧数据。
    • 对 admin_id、app_id、created_at 建立合适索引以提升查询效率。
  • 统计表(如每日用量)
    • 按 stat_date 范围分区,便于按日聚合与清理。
    • 对 (stat_date, user_id, chat_id, model_id) 建立唯一索引以避免重复统计。
添加日期:2026-10-05