table-designlisted
Install: claude install-skill 1398281322-a11y/java-backend-guardrails
# 表设计
## When to Invoke
建表、改表、加状态、加大表归档/分区。需求阶段就把唯一键和状态机定下来。
## When NOT
怎么建二级索引 → `mysql-index`。要不要拆库拆表 → `sharding`。本文件只决定表长什么样、何时归档/分区。
## 风险(面试考点)
没有唯一键导致��发双写;状态用字符串随意改导致乱跳;大表只靠「以后分库」而从不归档;把分区和分库分表混为一谈。分区仍在同一实例,分库分表才跨实例。
## 适用场景
- 订单、支付单、核销单、分佣流水
- 流水型大表(日志、回调原始报文)
- 需要幂等的业务表
## 方案选型(轻量优先)
| 问题 | 默认 | 升级(不要跳级) |
|------|------|------------------|
| 幂等 | UNIQUE 业务键 | 见 `idempotent` |
| 并发改同一行 | `version` 字段或条件更新 | 见 `optimistic-lock` / `concurrency-sell` |
| 表变大 | 索引 + 冷热分离/归档 | 分区(按月) |
| 分区仍不够 | 才考虑 `sharding` | — |
禁止:新建业务表就按用户分 256 表。先估算:有索引的 InnoDB 单表千万级仍常见。
## 默认方案
订单/核销表必备:
- 主键:雪花 ID(见 `distributed-id`),不要无序 UUID 当聚簇主键
- 业务唯一键:`out_trade_no+channel`、`voucher_id`
- 状态:tinyint/枚举,代码里枚举单向跳转
- `created_at` / `updated_at`
- 需要乐观锁才加 `version`
- 回调原始报文另表存储,避免撑爆主表行
```sql
CREATE TABLE t_order (
id BIGINT PRIMARY KEY,
order_no VARCHAR(32) NOT NULL,
user_id BIGINT NOT NULL,
merchant_id BIGINT NOT NULL,
status TINYINT NOT NULL,
pay_amount BIGINT NOT NULL,
version INT NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
UNIQUE KEY uk_order_no (order_no),
KEY idx_user_created (user_id, created_at),
KEY idx_merchant_status (merchant_id, status)
);
```
大表:先按时间归档到历史表(应用双写查询:近期主表,远期历史表)。单机时间范围查询再考虑 `PARTITION BY RANGE (TO_DAYS(created_at))`。还不够再读 `sharding`。
## 反例
错误:状态只靠应用判断,表无唯一键。
正确:唯一键是最后一道闸。
错误:把索引策略和分片键写进这张建表稿当同一段「优化」。
正确:索引走 `mysql-index`,分片走 `sharding`。
错误:流