SQL Server数据库设计避坑指南:12个实战关键点
|
跟老板拍桌子那天是去年3月,会议室玻璃还结着雾气——空调刚修完第三遍,制冷剂漏得像SQL Server的tempdb日志文件一样不可控。我手边那杯咖啡凉了十九分钟,PM正把“用户行为埋点表加个jsonb字段”写进需求文档第7版,而我在草稿纸上划掉第4个主键设计,笔尖戳破纸,墨水渗到下一页——那页记着上个月生产库凌晨三点的锁升级死循环。 先扔结论:所有“规范文档里没写的”,才是真坑。比如IDENTITY + NORECOVERY做迁移,看起来很稳;结果某天凌晨二点,DBA发现镜像端的LSN连续断了七次,重搭花了四小时十七分钟。不是备份链问题,是那个被所有人忽略的“IDENTITY_INSERT ON”在切换时没清。我敢打包票,这事儿在三个不同客户环境复现过,连错误码都一样——15600。 但这个我说不准:到底该不该把历史快照表按季度分区?去年Q2试过自动脚本建分区函数,结果ALTER PARTITION FUNCTION在某个周六上午十一点十七分卡住,后台SPID挂了十八毫秒才释放资源。后端蹲在服务器机柜前查IO队列,前端在群里发Lighthouse跑分截图,没人回。桌面那个没关的录屏,光标还在一闪一闪,录着SQL Server Management Studio右下角的时间跳变。 勾选“合并请求”之前,得先确认三件事:第一,CHECK约束里的子查询有没有嵌套UDF(有就删);第二,外键引用的列是不是全走了索引(哪怕你只用WHERE id= ?);第三,timestamp列别叫timestamp——它不是时间戳,是行版本标记,但每次改表结构时都有人把它当成datetime2塞进去。我们去年3月上线前最后一刻,在测试库删掉一个叫[timestamp]的列,改名成[row_version],结果部署脚本里漏掉了视图定义里那一处硬编码,凌晨两点十一分,报表服务吐出三百七十二 KB的XML错误堆栈,全是关于“无法绑定多部分标识符”的废话。 我原以为用GUID作聚集索引+NEWSEQUENTIALID()能解碎片问题,结果数据量上来后,叶级分裂比IDENTITY还狠。因为NEWSEQUENTIALID()在分布式节点生成时根本不同步——你本地开发机和预发布机之间差六百多毫秒的时钟漂移,UUID的字节序直接乱套。最后改成bigint自增,加了个sequence控制分库逻辑。PM下午三点来问“为什么不能支持跨库关联查询”,我没抬头,只说:“您试试让Azure SQL Data Warehouse自己join两个不同region的tempdb。”她转身去倒水,杯子是那种带硅胶圈的,拧开时发出一声轻响。 再说那个永远没人修的CDN账单。上个月忽然涨了四倍,运维说是缓存穿透触发了回源风暴,其实根源在咱们的用户状态查询里——写了SELECT FROM [User] WHERE email = @p,但email字段没建唯一索引,导致执行计划用了非SARGable扫描,进而把下游Redis缓存雪崩。这件事儿我没管,因为已经过了KPI折算周期,但心里记得清:那个SQL语句的执行计划XML里,EstimatedRows是3,ActualRows是89213。你信吗?
文章配图,仅供参考 NVARCHAR(MAX)当默认值?不,它是陷阱开关。去年3月有个新业务上线,产品说“留个备注字段以后再扩展”,我点了头。两周后发现所有insert都带隐式转换,varchar(50)往NVARCHAR(MAX)里塞,CPU占用率曲线就像心电图——特别是凌晨一点零七分那波峰值。后来查到,是因为查询提示里加了个OPTION (RECOMPILE),结果优化器对MAX类型做了更激进的内存估算,分配缓冲区的时候直接占满buffer pool。对面工程师换了一根充电线,从Type-C换成Micro-USB,就为了调试旧版SSMS 18.12里一个不显示统计信息更新时间的bug。还有那个“无害”的触发器。不是审计用的,是产品经理要求的“当订单状态变成已完成时,自动同步ERP系统”。它写得干净利落,EXEC dbo.SyncToERP @order_id,可里面调了WAITFOR DELAY '00:00:02'——为了等下游服务冷启动。上线后第一个周末,支付失败率飙升至12%,不是代码错了,是transaction日志膨胀了八百倍,VLF数冲到三千四百一十六个。DBCC OPENTRAN显示一堆悬挂事务,每个都卡在WAITFOR。我截了图发给后端,他看了两分钟,回了个表情包,然后切出去开了个会议,声音从隔壁传来:“…所以这次我们要用Serverless Function重写同步链路…” 索引视图?好东西。但WITH (SCHEMABINDING)之后,连改个字段注释都要DROP再CREATE。有一次为改个列名,临时停服四十分钟,而真正的性能提升只是把报表查询从九秒压到八点六秒。这事儿值不值得?我不知道。我知道的是,那个晚上我盯着Management Studio的状态栏,看“正在执行批处理”字样闪了整整六百三十二次。旁边PM手机亮起,弹出一条钉钉消息:“老板说可以先灰度,不用全量。”我没回。桌面上未关闭的录屏还在继续,光标闪烁节奏变慢了。 这些坑没一个在白皮书里明写。它们长在交接时漏掉的口头备注里,在Code Review漏掉的一行hint里,在“反正测试跑通了”这句话的尾音里。我现在每接到新项目,第一件事不是画ER图,而是翻Git历史,搜“NOLOCK”、“WITH (INDEX=…)”、“ISNULL(@p, '')”,以及最要命的那个——“--TODO:加索引”。如果超过七处,我就去约老板喝咖啡。不聊技术,聊工位朝向、茶水间热水器换新品牌的事儿。 下个项目?我想试试用SQL Server 2022的GENERATED ALWAYS AS ROW START/END建系统版本控制,但得先确认客户的Always On可用性组能不能扛住时钟偏移——毕竟上次校时服务器崩了,是靠手动同步Windows事件日志里的Logon时间戳做的补偿。这事我不打算现在动手。先把手头这张订单明细表的复合索引重建一遍。昨天看到,它的填充因子还是默认的0。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


ASP脚本避坑指南:5个新手常漏的语法细节
嵌入式Linux开发者Unix环境搭建避坑指南