SQL Server数据库设计避坑12实战要点
|
去年六月份那会儿,我原以为SQL Server的表设计就是画个ER图、定几个字段类型的事儿,结果被线上报警锤得满头包——那晚十点,运维突然在群里吼“订单表锁了,QPS掉到个位数”,我盯着屏幕上的阻塞链,发现罪魁祸首是个没建索引的“状态”字段,被一个批量更新语句扫了全表,扫了整整两百万行。 说白了,这十二个避坑要点,全是拿血泪换来的。比如第一条“别用TEXT/NTEXT存结构化数据”,我见过有人把JSON塞进TEXT字段,查询时用SUBSTRING+CHARINDEX解析,结果索引失效,单表查询从十毫秒飙到三秒——后来运维说那晚数据库CPU直接飚到90%,工单系统又弹了条消息,我边改代码边骂“这谁写的破玩意儿”。 第二条更坑:“自增主键别当业务ID用”。去年有个系统用订单号当主键,结果并发插入时死锁了——主键是聚簇索引,SQL Server生成自增值要加锁,高并发下直接堵成狗。后来改成GUID当主键,订单号单独建非聚簇索引,死锁没了,但存储空间涨了三分之一——产品还问我“为啥数据库变大了”,我指着他的需求文档说“您要的订单号必须全局唯一,这锅我不背”。 咖啡凉了,我接着说第三条:“慎用触发器”。之前有个老系统用触发器同步日志表,结果主表更新时触发器里又查了一遍主表,形成递归调用,直接把CPU干崩了。更绝的是,触发器里的错误没捕获,主表更新失败,业务还以为是网络问题——排查了两天才发现是触发器抛的异常被吞了,运维当时盯着监控图,脸绿得跟菠菜似的。 第四条“外键约束别乱加”,我深有体会。有个系统为了“保证数据一致性”,给所有关联表都加了外键,结果删除一条主表记录时,SQL Server要检查所有外键表,耗时从十毫秒变成两秒——更坑的是,有些外键表数据量超大,检查时直接锁表,导致其他查询超时。后来我偷偷把非核心表的外键删了,产品还问我“为啥数据能删掉”,我反问“您要的是强一致还是可用性”,他沉默了三秒说“还是可用性重要”。 窗外天黑了,键盘声还是噼里啪啦响。第五条“索引不是越多越好”,我见过有人给每个字段都建索引,结果写入性能暴跌——索引要维护啊!每次插入/更新/删除都要更新索引,数据量大时,索引维护的时间比业务逻辑还长。后来我删了三分之二的索引,只留高频查询的字段,写入速度直接翻了两倍,运维说“这优化比加机器管用”。 第六条“别用SELECT ”,这个看似简单,但坑人无数。有个接口用SELECT 查订单表,结果前端加了个字段,后端没改SQL,直接返回了所有字段——包括一个2MB的JSON备注字段。那晚接口超时率飙到30%,我查日志发现,90%的时间都花在序列化那个大字段上。后来我强制要求所有查询必须显式写字段名,产品还抗议“这样改起来麻烦”,我说“麻烦总比线上报警好”,他没再说话。 第七条“事务别太长”,我吃过大亏。有个批量导入功能,把所有数据塞进一个事务里,结果导入到一半网络断了,事务回滚时卡了十分钟——因为已经操作了几万行数据,回滚时要逐行撤销,数据库直接假死。后来我改成小批量提交,每1000行提交一次,就算中断也只回滚最后1000行,导入速度还快了三倍。运维当时盯着监控说“这优化神了”,我心想“神个屁,都是血泪教训”。 手机亮了一下,是测试发来的消息:“新版本又报锁超时了”。我叹了口气,第八条“别用NOLOCK提示”又得重提。之前有人为了“提高查询速度”,在所有SELECT语句里加WITH (NOLOCK),结果读到了未提交的数据,导致业务逻辑错乱——比如订单状态还没更新,就被查询出来显示为“已完成”,客户投诉说“钱扣了但没收到货”。后来我删了所有NOLOCK提示,产品还问我“为啥查询变慢了”,我说“慢总比错好”,他没反驳。 第九条“存储过程别写太复杂”,我见过有人把整个业务逻辑塞进一个存储过程,两千行代码,嵌套了十层IF-ELSE,结果改个字段类型要改二十处,调试时连错误堆栈都看不清。后来我强制要求存储过程不超过两百行,复杂的逻辑拆成多个小过程,调用时用事务保证一致性。虽然代码量多了,但维护起来轻松多了——至少改bug时不用盯着两千行代码发晕。 第十条“分区表不是银弹”,我原以为分区表能提高查询性能,结果有个系统按日期分区后,跨分区查询反而变慢了——因为SQL Server要扫描所有分区的索引,比单表查询还慢。后来我查了文档,发现分区表适合“按分区键查询”的场景,比如只查某天的数据,但跨分区查询反而更差。最后我删了分区表,改用普通表+日期索引,查询速度反而快了。 第十一条“别用动态SQL拼字符串”,我见过有人用字符串拼接生成SQL语句,结果被SQL注入攻击了——攻击者传了个“1; DROP TABLE Orders--”的参数,直接把订单表删了。后来我强制要求所有SQL必须用参数化查询,产品还问我“这样写起来麻烦”,我说“麻烦总比数据被删好”,他没再说话。
文章配图,仅供参考 第十二条“定期维护统计信息”,这个最容易被忽略。有个系统运行了半年,查询突然变慢,我查执行计划发现,SQL Server用了错误的索引——因为统计信息没更新,优化器以为某个字段的选择性很高,结果实际数据分布完全相反。后来我加了定期更新统计信息的作业,查询速度又恢复了。运维当时说“这操作神了”,我心想“神个屁,都是基础操作”。说白了,这十二个要点,优胜劣汰的结果——踩过的坑多了,自然知道哪些该做哪些不该做。但我也承认,有些点我也没完全搞懂,比如分区表的最佳实践,我查了好多资料还是一知半解。下一步我打算找个测试环境,把分区表再试一遍,看看能不能找到更适合的场景——毕竟,实践出真知嘛。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

