数据库管理设计原则:别等表乱了才想起建索引

小王做电商后台,上线三个月后订单查询越来越慢。他查日志发现,一个简单查用户最近5条订单的SQL要跑8秒——而数据里才不到2万条记录。问题不在服务器,也不在代码逻辑,就在建表那会儿没想清楚几件事。

名字别凑合,字段要有“身份证”

很多人起字段名图省事:nametypeflag……结果加到第7张表时,status在会员表里是0/1,在订单表里是1~5,在售后表里又变成字符串。后期写关联查询像猜谜。

建议统一加前缀或后缀:user_statusorder_statusrefund_status;枚举值直接用英文单词:pendingshippeddelivered,别用数字硬编码。

主键不是“必须ID”,而是“唯一锚点”

见过太多人不管三七二十一全用 id BIGINT AUTO_INCREMENT。但用户表可以,日志表就未必——高频插入下自增ID可能成瓶颈;而订单号本身带业务含义(如 ORD202405210001),天然全局唯一,还能省掉一次JOIN查单号的步骤。

真正该问的是:这行数据,靠什么能被精准定位?是系统生成的序号,还是业务中真实存在的标识?选那个更稳、更易追溯的。

索引不是越多越好,而是“查哪建哪”

有位同事给用户表所有字段都建了单列索引,结果插入变慢3倍,磁盘多占40%。其实他90%的查询只走两个条件:WHERE city = ? AND status = ?

不如建一个联合索引:

CREATE INDEX idx_city_status ON users (city, status);
这样既覆盖查询,又避免冗余索引拖累写入。

别把NULL当“可有可无”

phone VARCHAR(20) NULLphone VARCHAR(20) NOT NULL DEFAULT '' 看似差不多,实际影响很大。前者查 WHERE phone IS NOT NULL 无法用索引(除非是覆盖索引);后者配合 WHERE phone != '' 更可控,也省得程序层天天判空。

除非业务上真存在“未知/不适用/暂未提供”三种状态,否则尽量用默认值+非空约束,让数据边界清晰。

外键不是摆设,但别让它卡住上线

开发阶段开外键,能立刻揪出关联删除遗漏、脏数据混入;但线上高并发场景下,外键检查会锁表或锁行,有时反而成性能雷区。

折中做法:开发库严格启用外键,上线前评估核心链路压力,对读多写少、强一致要求高的表保留,对日志类、统计类表可关掉,靠应用层逻辑兜底。

备份和权限,从第一天就要想好

新项目初始化数据库,第一件事不该是建表,而是建账号:

CREATE USER 'app_rw'@'%' IDENTIFIED BY 'pwd123';
GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app_rw'@'%';
FLUSH PRIVILEGES;
别让应用直接连 root,也别让所有服务共用一个账号。

备份策略同理——不是等被删库跑路才想起来 mysqldump。每天凌晨全量 + 每15分钟binlog增量,脚本写好、路径配好、定时任务拉起来,比事后恢复省三天。