从一个订单系统说起
公司要做个内部采购系统,最开始大家图省事,直接在用户表里加了个字段叫 last_order_id,想着能快速上线。结果没过两个月,领导说要查每个员工近半年的采购明细,还要按部门统计金额。这时候才发现,订单数据根本没法关联,临时改表结构,接口全得跟着动,加班加到凌晨。
这就是典型的数据库设计没考虑长远。服务端开发里,数据库是地基,地基歪了,楼盖再高也撑不住。
别把数据库当垃圾桶
见过不少项目,一张表十几个字段,text类型乱用,索引随便加。一查慢就加缓存顶着,最后缓存崩了,数据库直接被压垮。数据库不是用来堆数据的,得讲究结构和关系。
比如用户信息和地址信息,别一股脑塞进 user 表。拆成 user 和 user_address 两张表,用 user_id 关联。以后用户要管理多个收货地址,改起来也方便,代码逻辑更清晰。
主键别用业务字段
有人喜欢拿手机号或者工号当主键,觉得唯一又方便。可一旦业务调整,工号规则变了,或者用户换了号码,外键关联全得重做。老老实实用自增 ID 或者 UUID 当主键,简单稳定。
适当冗余,但要有节制
订单表里要不要存用户姓名?完全不存,查一次得连三张表;全存下来,用户改名后历史订单显示不对。折中方案:下单时把当时的用户名快照存进去。这样既保证查询效率,又不影响历史数据。
索引不是越多越好
给每个字段都加索引,写入速度会明显下降。重点关注高频查询条件,比如订单状态、创建时间这种常用于 where 和 order by 的字段。可以用 explain 看执行计划,别靠猜。
举个简单的例子
假设要做个任务管理系统,至少需要三张表:
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE
);
CREATE TABLE tasks (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
status TINYINT DEFAULT 0, -- 0待处理 1进行中 2完成
assignee_id BIGINT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (assignee_id) REFERENCES users(id)
);
CREATE INDEX idx_tasks_status ON tasks(status);结构清晰,扩展性强。以后加优先级、截止时间、评论功能,都能在现有基础上平滑演进。
上线前多问几个问题
这张表一年后数据量大概多少?关键查询能不能走索引?字段改了会不会影响其他模块?有没有可能被误删?这些问题想清楚了,比写十篇文档都管用。
数据库设计没有标准答案,但有基本章法。别总想着一步到位,也别完全不管。在快速迭代和长期维护之间找平衡,才是实际工作中最常用的姿势。