小王做电商后台,上线三个月后订单查询越来越慢。他查日志发现,一个简单查用户最近5条订单的SQL要跑8秒——而数据库里才不到2万条记录。问题不在服务器,也不在代码逻辑,就在建表那会儿没想清楚几件事。
名字别凑合,字段要有“身份证”
很多人起字段名图省事:name、type、flag……结果加到第7张表时,status在会员表里是0/1,在订单表里是1~5,在售后表里又变成字符串。后期写关联查询像猜谜。
建议统一加前缀或后缀:user_status、order_status、refund_status;枚举值直接用英文单词:pending、shipped、delivered,别用数字硬编码。
主键不是“必须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) NULL 和 phone 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增量,脚本写好、路径配好、定时任务拉起来,比事后恢复省三天。