SQL Server查询卡顿?5个索引优化技巧提速3倍
|
去年十一月,凌晨两点十七分,我蹲在IDC机房第三排冷通道里改执行计划——空调漏水,一滴一滴砸在SQL Server那台DELL R740的机箱盖上,咚、咚、咚。王磊在对面工位啃冷掉的葱油饼,渣子掉进键盘缝里,他伸手抠了三下没抠出来,最后用嘴吹。我原以为是统计信息陈旧,结果揪出个没加INCLUDE的非聚集索引,查120万行,IO读了八百多万页。 第一个技巧?别急着建索引。先看那个“WHERE a = @p AND b > @q ORDER BY c DESC” —— 你建(a,b,c)覆盖索引?错。得是(a,c,b),因为ORDER BY c DESC才是驱动排序的关键路径,b只是过滤项,放后面才能复用索引seek+sort。我上周还见个哥们儿在生产库跑CREATE INDEX IX_XXX ON T (a,b,c) INCLUDE (d,e,f),查得比全表扫描还慢——为什么?因为他压根没看实际执行计划里的“Index Seek Predicate”那一栏,只盯了“Index Seek”四个字就飘了。这个我敢打包票:predicate没走key range,你建再厚的INCLUDE也是给磁盘表演缓存穿透。 当时窗外在下冻雨,刮着西北风,机房新换的负压风机嗡嗡响得像拖拉机挂了二档。李工推门进来,左手拎着两瓶红牛,右手攥着张A4纸,上面手写画了个索引树的B+节点分裂示意图,边画边说:“叶哥你看啊,这层高一升高,跳指针就多一次……”我打断他:“停,你上次调参把max degree of parallelism设成0之后,sp_configure查出来还是0,但查询跑着跑着就锁住Resource Semaphore,你记得吗?”他愣了半秒,把红牛瓶盖拧开又拧紧,没说话。我们都没提那天他改完参数后,整套报表服务瘫了四小时十七分钟的事。
文章配图,仅供参考 第二个技巧?干掉“隐式转换”。客户传过来的varchar(50)参数去比nvarchar(50)字段——看着一样,SQL Server悄悄给你转成Unicode,索引直接失效。不是提示警告,是静默跳过。我原以为是参数嗅探问题,抓包一看,SP_EXECUTESQL里参数定义是N'xxx',但业务系统发的是'xxx',中间隔了三层Spring JDBC拦截器。你去查sys.dm_exec_query_plan,ExecutionMode写着“Row Mode”,但Actual Rows是0,Estimated Rows却是382——它连基数估计都懒得估了,直接摆烂。这种地方删掉一个N,QPS从23冲到68,不靠索引,靠不瞎折腾。第三个?合并重复索引。你们也见过那种表吧——IX_T_A_B_C、IX_T_A_B、IX_T_A,全是非聚集,列顺序完全重叠。DBA小陈前两天在群里发截图,说“刚清理了7个冗余索引,空间省了三百七十二MB”。我说你查过sys.dm_db_index_usage_stats吗?他回:“没查,我看名字像。”我点开他贴的索引定义,顺手翻了下last_user_seek时间:一个2022年9月,两个去年4月,还有一个……创建时间是去年11月2号凌晨三点,正是我改完执行计划那天。哦,是他半夜自己建的。他手机屏保是个猫打哈欠的GIF,动来动去,烦人。 第四个?拒绝“全字段覆盖”。有同事非要INCLUDE所有SELECT字段,美其名曰“避免Key Lookup”。查单行?可以。查五万行?反而慢。因为页面分裂更频繁,内存中缓存命中率掉得比奶茶店门口排队还快。我试过同一张表两种写法:一种INDEX (a) INCLUDE (b,c,d,e,f,g,h,i,j),另一种INDEX (a) INCLUDE (b,c,d) + SELECT只取这四列——后者跑三次才用掉前者的三分之一时间。为什么?缓存页里挤满了不需要的数据,SQL Server要扫更多页才能凑齐你要的500行。这个我说不准是内存压力还是预读逻辑作祟,反正结果就是慢。 第五个?定期杀“幽灵索引”。就是那些CREATE DATE早于LAST_USER_SEEK为NULL的索引。有些是历史项目留的,有些是开发自动生成的ORM索引。我们上周干掉一个叫IX_Report_OrderDetail_FkOrderId_CreateTime_Status的索引——37列,INCLUDE了14个字段,占空间两百多MB。删完一周,报表作业平均延迟从十八秒降到六秒。但你知道最讽刺的是啥?上线前三天,测试环境没人跑报表,这索引的LAST_USER_SEEK一直是NULL,没人敢删。直到运维老周说:“要不咱先删开发库的?”我才敢动生产——他讲这句话的时候,正用指甲刮着桌沿一块翘起来的胶皮,刮了整整四十三下。 顺便说句题外话:昨天看到DBCC SHOW_STATISTICS返回结果里有个“Average key length: 42.6 bytes”,忽然想起大学时候教数据结构的赵老师,总爱在黑板右下角写一行小字,“B+树不是树,是链表穿起来的数组”。我那时觉得他装,现在才懂,他是怕我们真拿二叉树那套去想SQL Server的页面拆分。 优胜劣汰不是选最快的,是让慢的先死透。我昨天把索引优化清单发给架构组,群里沉默了二十分钟。后来产品总监发了个“????”,后面跟了个括号(刚和客户开完会)。行吧,我不说了。今晚回去再看看tempdb的日志文件是不是又长到三十个文件了。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |





