首页/新闻资讯/正文详情

SQL Server索引碎片整理:原理、阈值与REBUILD实战

发布时间:2026/11/3 13:15:59 来源:云帆数科 栏目:资讯中心
SQL Server索引碎片整理:原理、阈值与REBUILD实战
1. 碎片到底是个什么东西——先把物理层看清楚凌晨两点值班的同事把一条告警截图丢进群里某核心业务的一张订单查询从 80 毫秒涨到了 1.2 秒执行计划没变、索引没缺、统计信息刚更新过。翻到最后发现问题不在语句上而在磁盘上那张表的主键索引逻辑碎片已经接近 90%扫描一段连续区间要跳几百个不连续的区。这就是 SQL Server 碎片整理这件事最典型的发生场景——它不产生报错不进错误日志只是慢慢地把你的 IO 变成随机 IO然后某一天在业务高峰把你按在地上摩擦。这篇东西写给已经在跟数据库打交道、但碎片整理一直靠维护计划定时跑一跑的朋友。我会把页和区的物理结构、两种碎片的区别、REORGANIZE 与 REBUILD 的真实代价、阈值怎么定、脚本怎么写、大表和分区表怎么处理、以及那些只有踩过才知道的坑一条条摊开讲。新手能照着抄老手可以拿去对一下自己的判断标准有没有偏差。1.1 页、区与 B 树碎片从哪里长出来SQL Server 存数据的最小单位是页Page固定 8KB所有读写都以页为单位你哪怕只查一行引擎也得把那一页 8KB 整个读进内存。页往上组合成区Extent一个区 8 个页、64KB区是空间分配的单位。表如果是堆没有聚集索引数据页之间没有任何顺序关系一旦建了聚集索引数据页就被组织成一棵B 树叶子层按索引键排序页与页之间用双向链表串起来每个页头里记着上一页和下一页的页号。关键就在这个链表上。B 树要求叶子层的物理顺序和逻辑顺序尽量一致这样你WHERE OrderDate BETWEEN 2024-01-01 AND 2024-01-31的时候引擎顺着链表往后读就行预读机制能一次性把连续的几十个区抓进缓冲池磁盘上基本是顺序 IO。但插入、更新、删除会不断打乱这个秩序一个页写满了新数据插不进去引擎就做页分裂把原来一页的数据拆成两页新页从空间分配器那里随便找个空位置放下去。分裂次数多了逻辑上相邻的页在物理上就天各一方链表变成了到处乱跳的指针。这就是外部碎片。删除更直接。删掉一批行之后页里的空闲空间散落在中间页本身还占着位置B 树的密度掉下来同样的数据量要占更多的页、更多的区扫描同样的数据要读更多的 IO。这就是内部碎片量化指标是avg_page_space_used_in_percent。1.2 外部碎片与内部碎片两码事很多人把这两个混成一个碎片然后一刀切地去重建其实它们的成因、影响和处理手段都不一样得分清楚。外部碎片看的是逻辑相邻的页在物理上是不是也相邻量化指标是avg_fragmentation_in_percent。对聚集索引和非聚集索引它算的是逻辑碎片叶子层页面的顺序与物理顺序不一致的比例对堆Heap它算的是区碎片区与区之间不连续的比例。这个区别很重要堆的碎片率再高也不代表数据本身乱只代表区分配得散。内部碎片看的是每个页里塞了多少数据指标是avg_page_space_used_in_percent。这个值低意味着页里有大量空洞同样的行数要占更多页缓冲池里能缓存的行变少命中率下降扫描同样的数据量要读更多页。内部碎片高的典型原因是大批量删除、变长列更新导致行迁移、以及FILLFACTOR设得过低。两种碎片的处理方向甚至有点矛盾为了压低外部碎片你会希望页填得满一点FILLFACTOR高为了减少未来页分裂你又希望页留点余量FILLFACTOR低。这个取舍在第三节会专门算账。1.3 碎片真正拖慢的是哪一类查询这里必须泼一盆冷水碎片并不总是性能问题的元凶很多团队把碎片整理当成万能药每个月跑一次全库重建跑完发现慢查询一点没变。碎片主要伤害的是范围扫描类的访问。原因在于预读机制引擎做表扫描或索引范围扫描时会根据区的不连续性向前预读一批数据进缓冲池物理上连续的区能一次性抓一大把物理上散落的区只能一个一个去要顺序 IO 退化成随机 IO机械盘上差距是数量级的全闪存上差距小一些但依然存在因为每次 IO 都有固定延迟成本。反过来说点查Seek为主的负载受影响很小。你去查一行主键B 树只需要从根一路走三层就到叶子页中间跳几次对总耗时的影响可能在微秒级。这也是为什么 OLTP 系统里碎片率很高但业务感觉良好的情况非常常见——缓冲池把热数据全兜住了磁盘根本没被碰到。所以判断要不要整理第一条纪律是先看这条索引有没有范围扫描扫描的页数大不大再去谈碎片率。一张两万页、只做点查的配置表碎片率 95% 也不用管一张两千万页、每天被报表全扫一遍的交易流水表碎片率 40% 就该动手了。2. 动手前先量化碎片检测的 DMV 与阈值设定整理碎片的动作本身很简单几条ALTER INDEX就能跑完难的是判断哪些该整、整到什么程度、什么时候整。这三件事全靠数据说话而数据的来源就是sys.dm_db_index_physical_stats这个动态管理函数。它是我每次接手一个新库时第一个跑的东西比看备份策略还早。2.1 sys.dm_db_index_physical_stats 的四个关键字段这个函数返回的列不少但真正决定你下一步动作的只有四个。字段含义怎么看page_count索引或堆占用的页数约等于行数除每页行数小于 1000 页基本不值得动收益抵不上开销avg_fragmentation_in_percent外部碎片率索引看逻辑碎片堆看区碎片两者含义不同avg_page_space_used_in_percent页密度内部碎片的反面低于 70% 说明页里空洞多考虑重建压缩fragment_count碎片片段数片段数多但碎片率不高时说明是小范围乱序index_level这个字段要特别提醒一句B 树有根节点、中间层、叶子层index_level 0才是叶子层也就是真正存数据的那一层。中间层和根节点页数很少碎片率经常会显示一个很高的值但对性能几乎没影响。过滤条件里务必加上index_level 0我第一次写脚本时漏了这个结果生成了一堆针对根节点的重建语句跑起来毫无意义还占了维护窗口。page_count的阈值是很多人忽略的重点。一个只有 50 页的小表碎片率 100% 又怎样总共就 50 个页全部重排也就几百微秒的事而重建动作本身的元数据开销、日志开销、锁开销反而是固定的。业界常见经验是把门槛设在 1000 页激进一点设 100 页我认为1000 页是个稳妥的起点特别是当你打算把脚本做成定时任务的时候。2.2 采样模式LIMITED、SAMPLED、DETAILED 怎么选这个函数的第四个参数是扫描模式三种取值差异非常大。LIMITED模式最快它只扫 B 树的父层页不扫叶子层。代价是对堆它只返回区碎片对索引它返回的碎片率是从父层推算出来的近似值页密度字段直接返回 NULL。适合在几百个库上做粗筛几十秒就能扫完全实例。SAMPLED模式按 1% 的采样率随机读叶子页。数据量大的时候速度可以接受碎片率是估算值页密度也是估算值。日常巡检我基本都用这个。DETAILED模式全扫叶子层结果最准但代价是把这个索引的每一页都读一遍。一张 5000 万行的表跑一次可能要几分钟到十几分钟而且会把大量冷数据灌进缓冲池把热数据挤出去——这个副作用经常被忽略在业务高峰期跑 DETAILED 扫描本身就是一次性能事故。我的习惯是分两步走先用SAMPLED出一版清单对其中页数特别大、碎片率处于临界值比如 25%~35%的那几个索引再单独用DETAILED复核一次。临界值附近的估算误差最容易导致误判要么白重建要么漏掉。2.3 阈值不是固定值按页数和访问模式分档网上流传最广的一套阈值是小于 10% 不管10%~30% 用 REORGANIZE大于 30% 用 REBUILD。这套数字有它的道理——10% 以下的碎片对预读影响很小30% 以上 REORGANIZE 反复挪页的成本已经逼近重建——但它只是一条默认线不是铁律。我实际用的是一张分档表把页数和碎片率交叉起来看页数量级碎片率 10%10%~30%30% 1000 页忽略忽略忽略收益太低1000 ~ 5 万页忽略观察REORGANIZE 或低峰 REBUILD5 万 ~ 100 万页忽略REORGANIZEREBUILD联机优先 100 万页观察趋势REBUILD联机分批必须联机考虑分区或可续做方案这张表的逻辑很简单页数越多碎片的绝对成本越高就越值得为它付出重建代价页数越小重建的固定开销占比越大越应该放过。另外还有一层如果这张索引从不被范围扫描只有 Seek那么无论多大、碎片多高都可以直接忽略。你要在脚本里把访问模式也纳进来可以关联sys.dm_db_index_usage_stats看user_scans user_seeks的比例。3. REORGANIZE 还是 REBUILD把代价算清楚这两条命令在语法上只差几个字母在引擎内部的执行方式却是完全不同的两套逻辑。选错了不会报错但会在你不希望的时间点给你制造阻塞或者日志爆盘。3.1 两种方式在引擎内部的差别REORGANIZE是就地整理。它从叶子层最左边开始逐页往后走遇到页里有空洞就把它后面的行往前填填满了就释放这一页同时修正前后页的链表指针。整个过程中页的物理位置基本不动——它只是把页内的数据压实、把空页摘掉所以外部碎片的改善有限内部碎片的改善很明显。它是小事务每一步都提交随时可以按 CtrlC 中断中断后已完成的部分保留。REBUILD是重造。引擎按索引定义重新建一棵 B 树把数据全部搬过去最后把旧结构整个丢掉。这个过程会申请新的空间通常和原索引差不多大如果SORT_IN_TEMPDB ON还要占 tempdb完成后释放旧空间。结果是外部碎片和内部碎片同时清零页密度回到FILLFACTOR设定值。代价是它在大表上是一个或几个长事务日志量巨大。放在一起对比维度REORGANIZEREBUILD外部碎片改善有限页位置不动彻底重新分配内部碎片改善明显彻底是否更新统计信息不更新需单独处理全扫描时按等价方式更新并发影响小事务一般不停业务离线时持 Sch-M 锁阻塞所有访问事务日志分摊到小事务可控集中爆发可能几倍于索引大小可中断性随时中断保留进度传统模式下中断即回滚附加空间需求极小约等于索引大小可能更多一个特别容易被忽略的点REORGANIZE 不更新统计信息。很多团队把整理做完了查询还是慢原因就是优化器手里的还是老统计。所以只要跑了 REORGANIZE就该顺手补一次统计信息更新或者至少对关键表补。3.2 联机与离线锁、阻塞和可用版本离线重建不带ONLINE ON在开始和结束阶段会申请架构修改锁Sch-M这个锁和所有访问都冲突。对大表来说中间的执行过程虽然不一定全程持锁但收尾阶段那个锁窗口足够把一票请求堵在门口。我见过最惨的一次是某公司给一张两亿行的表做离线重建收尾锁等了四十多秒前面的连接全部超时业务侧直接起了一波工单。联机重建ONLINE ON的做法是先建新结构边建边把并发修改记下来最后用一个极短的窗口做切换。切换阶段依然需要短暂的 Sch-M 锁但窗口从几十秒压缩到毫秒级。需要注意的是这个能力在不同版本上的可用性不一样而且对包含某些数据类型如大对象列的索引有限制。动手前先在自己实例上做一次小表验证别拿文档当准绳版本、补丁、索引类型都可能让结果不同。较新的版本还提供了两个很实用的能力一是可续做的联机重建允许你把重建暂停、释放资源回头再继续这让在业务低峰做不完就停下来变成可行策略二是低优先级锁等待WAIT_AT_LOW_PRIORITY让重建过程中申请的锁在冲突时自动让路避免把业务堵死。这两个特性对 7×24 的系统价值很大值得单独测一测。3.3 FILLFACTOR 与页分裂的长期账FILLFACTOR是这整套事情里最需要长远眼光的一个参数。它决定重建后每个叶子页留多少空闲空间。设成 100 意味着页塞满设成 80 意味着每页留 20% 余量。关键在于FILLFACTOR 只对新页生效不会自动应用到已有页上。你今天把一张表的 FILLFACTOR 从 100 改成 80不重建的话一页都不会变。这也解释了为什么很多人改了参数看不到效果。那么该设多少看索引键的增长模式单调递增的键自增 ID、按时间递增的单号作为聚集索引插入永远发生在 B 树最右端不会往中间分裂FILLFACTOR 100是合理的页填满能减少页数和 IO。随机分布的键如 GUID、哈希值作为聚集索引插入会落在树中任意位置页填满必然频繁分裂FILLFACTOR设 70~85 能显著降低分裂频率。非聚集索引要看它包含的键如果是宽键、或者对应列更新频繁留余量有价值如果是窄键且基本只读填满更好。真正常见的错误是反过来的给自增主键的表设了FILLFACTOR 70结果表凭空大了 40%缓冲池里能装的行少了三成扫描性能反而下降。这是典型的为了防碎片而制造碎片。4. 一套可以直接抄的整理流程说了这么多原理落到执行层面我给你一套我自己在用的四步流程采集、决策、执行、验证。每一步都有对应的脚本可以直接改成你的环境参数。4.1 第一步跨库采集碎片清单先做一次全实例粗筛用SAMPLED模式单库版本长这样SELECT DB_NAME(ips.database_id) AS db_name, OBJECT_SCHEMA_NAME(ips.object_id, ips.database_id) AS schema_name, OBJECT_NAME(ips.object_id, ips.database_id) AS table_name, i.name AS index_name, i.type_desc, ips.page_count, ips.avg_fragmentation_in_percent AS ext_frag_pct, ips.avg_page_space_used_in_percent AS page_density_pct, ips.fragment_count, ips.record_count FROM sys.dm_db_index_physical_stats(NULL, NULL, NULL, NULL, SAMPLED) AS ips JOIN sys.indexes AS i ON i.object_id ips.object_id AND i.index_id ips.index_id WHERE ips.index_level 0 AND ips.page_count 1000 AND i.is_disabled 0 AND i.is_hypothetical 0 AND ips.avg_fragmentation_in_percent 10 ORDER BY ips.page_count DESC;第一个参数传NULL表示扫当前实例所有数据库这是最省事的写法。如果你只想扫当前库换成DB_ID()。注意跨库扫的时候某些数据库可能处于不可访问状态离线、单用户、正在还原函数会报错所以更稳妥的做法是用游标逐个库跑把异常库跳过并记下来。我自己那版脚本就是游标版的跑完会把出错的库名列出来方便后续补跑。另外把is_disabled和is_hypothetical过滤掉很重要。禁用状态的索引重建会直接把它重新启用这个副作用很多人不知道一跑脚本发现某个索引莫名其妙活了还以为是别人的操作。4.2 第二步按规则生成语句并干跑拿到清单后不要手工一条条写让脚本按分档规则生成。下面这段是核心逻辑把碎片率、页数、是否为分区表都考虑进去SELECT QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id)) . QUOTENAME(OBJECT_NAME(ips.object_id)) AS object_name, i.name AS index_name, ips.page_count, ips.avg_fragmentation_in_percent, CASE WHEN ips.page_count 1000 THEN -- 跳过页数太小 WHEN ips.avg_fragmentation_in_percent 30 THEN ALTER INDEX QUOTENAME(i.name) ON QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id)) . QUOTENAME(OBJECT_NAME(ips.object_id)) REBUILD WITH (ONLINE ON, MAXDOP 4, SORT_IN_TEMPDB ON, STATISTICS_NORECOMPUTE OFF); WHEN ips.avg_fragmentation_in_percent 10 THEN ALTER INDEX QUOTENAME(i.name) ON QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id)) . QUOTENAME(OBJECT_NAME(ips.object_id)) REORGANIZE; ELSE -- 跳过碎片率在阈值内 END AS command_text FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, SAMPLED) AS ips JOIN sys.indexes AS i ON i.object_id ips.object_id AND i.index_id ips.index_id WHERE ips.index_level 0 AND ips.page_count 1000 AND i.is_disabled 0 ORDER BY ips.page_count DESC;生成的语句不要直接全选执行。先把结果集导到一张临时表或者贴进编辑器人工过一遍重点关注三类一是页数超过 50 万的大表它们要单独排窗口二是有外键依赖或被视图引用的索引三是名字里带PK但你其实不确定它是不是被业务高频写入的。干跑这一步省掉的麻烦比你想象的要多得多。4.3 第三步分批执行与资源限流执行阶段的核心原则是永远不要一条语句跑完整个库。具体做法有几条。第一按页数排序从小到大跑先拿小表热身观察日志增长和阻塞情况确认没问题再动大表。第二大表单独执行并加上限流参数。MAXDOP控制并行度默认让引擎自己决定在大表上可能开到很高把 CPU 吃满一般 OLTP 系统给 4 或者 2 就够了。SORT_IN_TEMPDB ON把排序放到 tempdb 做能减少用户库的数据文件增长前提是 tempdb 有空间——这个前提你得先确认。第三重建和重组分开跑。我一般把 REORGANIZE 那一批放在业务时段做它影响小把 REBUILD 那一批集中到维护窗口。这样能最大程度利用时间又不至于把窗口拖长。第四控制单次执行时长。可以给脚本加一个时间预算比如超过 40 分钟就停下剩下的记下来下次继续。较新版本的数据库支持可续做的联机重建直接用它更省事暂停之后资源立即释放下次从断点继续。-- 大表重建的推荐写法 ALTER INDEX [IX_Order_OrderDate] ON [dbo].[Order] REBUILD WITH ( ONLINE ON, MAXDOP 4, SORT_IN_TEMPDB ON, STATISTICS_NORECOMPUTE OFF );STATISTICS_NORECOMPUTE OFF这个参数是显式声明重建完要更新统计信息。默认行为就是这样但写出来能防止有人后来在实例级别把自动统计关掉了导致行为变化。4.4 第四步统计信息补刀与效果验证跑完不验证等于白跑。验证要看三样东西。碎片率是否真的降下来了拿同一套 DMV 再查一遍对比前后。如果某个索引重建完碎片率还是 30%可能是并行业务在里面持续写入导致的也可能是索引本身键值太宽、页分裂太快这种情况要回头去考虑调整FILLFACTOR或者改索引设计。统计信息是否新鲜查sys.dm_db_stats_properties看last_updated时间特别关注那些只做了 REORGANIZE 的表。这一步我经常发现漏网之鱼。业务查询是否真的变快这个最实在。挑几个已知的慢查询对比执行前后的逻辑读SET STATISTICS IO ON和实际耗时。碎片整理对逻辑读的影响应该能看到——同样的查询页密度提高后读的页数变少。如果逻辑读没变、耗时也没变那说明碎片本来就不是瓶颈下次这张表可以直接排除在维护清单外。这里补一句常见误区很多人重建完看到执行计划变了以为是碎片的影响其实是统计信息更新导致的计划重编译。两者要分开看。5. 大表、分区表和特殊索引的处理思路标准流程能覆盖八成场景剩下两成是大表和特殊结构需要单独处理。5.1 超大表别指望一次重建搞定页数过百万甚至千万的表离线重建基本不可行联机重建的资源消耗也很可观而且一旦中断不管是主动还是被动传统的重建模式要整体回滚回滚时长可能和执行时长差不多甚至更久——回滚本身也要写日志。应对思路有这么几条。优先做可续做的联机重建。较新版本提供的可续做能力允许你把一个大表的重建拆成几个时间片每片结束就暂停资源立即释放第二天接着做。前提是表结构没有中途变更否则续做会失败。实践下来这是超大表最省心的方案。考虑重建非聚集索引 不动聚集索引。聚集索引通常最大重建成本最高。如果慢查询主要走非聚集索引的范围扫描而聚集索引本身是点查为主那完全可以把精力放在小的那个上性价比高得多。别看到聚集索引碎片率高就条件反射式地去重建。极端情况下用新表 切换。建一张同样结构的新表把数据按索引顺序导过去然后在一个极短窗口里做重命名切换。这个做法能绕开长事务但要处理好外键、权限、依赖对象和增量数据同步复杂度高只在确实必要时用。还有一条经常被忽略的路先问这张表为什么会有这么多碎片。如果一张表每周都要整理说明它的写入模式在持续制造分裂。是不是用了随机 GUID 当聚集索引是不是有频繁的全列更新是不是FILLFACTOR设得太满解决成因比反复整理更有效。5.2 分区表按分区整理分区表的重建和普通表有个本质差别如果所有分区一起重建等于把整张表重造一遍分区带来的管理优势一点没用上。正确做法是按分区逐个处理只收拾真正有碎片的那几个分区。可以用PARTITION n语法ALTER INDEX [IX_Trade_Date] ON [dbo].[Trade] REBUILD PARTITION 12 WITH (ONLINE ON, MAXDOP 4);碎片清单也要按分区维度取。把sys.dm_db_index_physical_stats的partition_number加进结果集你会发现通常只有最新写入的那几个分区碎片高历史分区基本是干净的——因为它们不再写入页分裂早就停止了。这种结构下日常维护只需要盯着当前热分区和上一个分区工作量能降一个数量级。分区还有一个免整理的杀手锏分区切换SWITCH。对于需要整体归档或整体删除的场景直接切出去比任何整理都高效。同样地如果你有一个已经写好、结构完全一致的新分区准备切进来那这个新分区的数据在写入时就该保证有序切进来之后是不需要整理的。5.3 列存与特殊索引的注意事项列存索引的内部结构和行存完全不一样它按列分块存储、块内做压缩avg_fragmentation_in_percent这个指标对它的意义很有限。对列存来说真正影响性能的是压缩后的行组质量和删除位图的比例。大量删除之后行组里会有很多已删除标记扫描时要跳过这些位置效率下降。这种情况的处理手段不是 REORGANIZE而是考虑重建该列存索引或者做行组整理。别把行存那套阈值直接套上去。全文索引、空间索引、XML 索引这些特殊类型的维护逻辑也各有不同重建代价通常比普通索引高而且有些索引的重建会带来额外副作用比如全文索引重建期间全文检索性能下降。这类索引上手前建议单独查一下该类型的维护建议别混在批量脚本里一起跑。还有一个容易被遗忘的内存优化表的索引结构在内存里和页、区没关系碎片这套概念不适用。脚本里要把这类表排除掉否则会生成一堆无意义的语句。6. 现场踩坑记录与排查速查前面讲的是应该怎么做这一节讲做的时候会出什么事。这些都是我在实际环境里撞过的写下来供你提前规避。6.1 阻塞、锁等待和执行窗口选择症状维护脚本跑起来之后业务侧大量超时监控上一条明显的阻塞链头节点是ALTER INDEX ... REBUILD。原因离线重建的收尾阶段要 Sch-M 锁或者联机重建在切换阶段也要短暂拿锁而这个锁在排队时会被后面的一堆长查询堵住——注意锁的排队是公平的重建在等锁后面的查询在等重建一条慢查询就能引发雪崩。处理一是确认重建是ONLINE ON二是给重建语句加上低优先级等待选项让它在遇到冲突时主动退让等不到就放弃下次再来三是把重建放到真正的低峰而不是看起来低峰的时间——有些系统凌晨两点的批处理比白天还猛得看自己系统的实际负载曲线。预防执行前先查一下当前有哪些长事务、有哪些大查询在跑确认没有长时间占用资源再动手。也可以在测试环境先跑一遍同样规模的重建记录耗时和锁等待心里有数。6.2 事务日志暴涨与空间告急症状重建跑到一半报事务日志已满或者数据文件所在磁盘空间告急。原因离线重建是单个或少数几个长事务日志在事务提交前不能截断一张 200GB 的表重建日志可能涨到几十 GB 甚至更多。另外重建需要额外的数据空间通常是原索引大小的 1 到 2 倍这个临时膨胀经常被忽略。处理执行前先查三样东西——日志可用空间、数据文件可用空间、tempdb 可用空间如果开了SORT_IN_TEMPDB。做大表前按索引大小 × 1.5估算额外空间需求。日志模式下可以考虑分批执行、缩短单次事务或者在确认不影响其他业务的前提下咨询 DBA 侧的恢复策略要求。预防把空间检查写进脚本的第一步空间不达标直接跳过并记录而不是跑到一半失败。这个检查五秒钟的事能省掉一次事故。6.3 常见问题速查表现象可能原因排查动作处理建议重建后碎片率没降中途有并发写入或键值分布导致再分裂重建后立即复测碎片率调整FILLFACTOR或改索引键设计重建后查询更慢缓冲池被冷数据挤占或统计信息剧变导致计划变化看缓冲池命中率、看新执行计划重建后让缓存自然淘汰观察 1~2 天脚本执行中断后无法继续离线模式的重建中断即回滚查当前是否有回滚进行中等回滚完成后改用可续做模式某个索引凭空活了脚本没过滤is_disabled查sys.indexes的禁用状态脚本加is_disabled 0过滤重建后表空间没释放旧结构的空间释放是异步的查sys.dm_db_file_space_usage稍等或检查是否有其他因素占用大表重建超时被杀执行窗口不够或锁等待看重建耗时与锁等待统计拆批处理或改用可续做方案堆表碎片率总是很高堆没有聚集索引区分配本就散确认该表是否真的需要聚集索引考虑建聚集索引或接受堆的碎片最后再分享一条个人习惯我会在维护清单里留一列上次处理时间和处理前后碎片率连续几个周期观察同一张表的碎片增长速度。增长快的表说明它的写入模式有问题该去查索引设计而不是反复整理增长慢的表可以逐步降低维护频率。这套历史数据比任何阈值表都准因为它是针对你自己系统的。碎片整理这件事最怕的不是做错而是机械地做——定时、全库、无脑跑跑完不看结果。真正有价值的部分从来不在那两条命令里而在跑之前的那几个判断这条索引有没有被范围扫描、碎片涨得快不快、重建的代价和窗口够不够。把这几个问题回答清楚你甚至会发现有一半的索引根本不需要动。

相关推荐

专病数据库如何实现数据不出院的多中心研究?联邦学习+区块链架构解析
专病数据库如何实现数据不出院的多中心研究?联邦学习+区块链架构解析

简介:这份PDF面向消化疾病领域临床研究者、医院信息化建设者及中医药循证研究相关人员,系统梳理了优势病种专病数据库的建设思路与实践路径,可帮助读者理解专病数据平台从规划到落地的关键环节。资源共1个PDF文件,压缩包约3.06MB&… · 2026/11/3 13:15:59

Vue 3 实现无缝循环滚动公告栏:requestAnimationFrame 与 CSS 动画方案对比
Vue 3 实现无缝循环滚动公告栏:requestAnimationFrame 与 CSS 动画方案对比

文字公告横向循环滚动这个需求,几乎每个做后台系统或者门户页面的前端都躲不开。产品经理的原话通常是“就一行字,让它自己慢慢滚,鼠标放上去停一下”,听起来像五分钟的活,真动手写的时候才发现坑一个接一个&#xff1… · 2026/11/3 13:14:58

u-center 配置 u-blox GPS 模块:波特率修改与配置保存避坑指南
u-center 配置 u-blox GPS 模块:波特率修改与配置保存避坑指南

简介:这份文档面向使用 u-blox GPS 模块的嵌入式开发者与测试人员,系统讲解 u-center 专业测试软件的常用操作与模块配置方法,帮助读者解决波特率修改、参数保存及与单片机联调等实际问题。资源包共 1 个 docx 文件,约 256KB&… · 2026/11/3 13:14:58

xlsx与xlsm:宏检测、格式转换与批量处理实践
xlsx与xlsm:宏检测、格式转换与批量处理实践

1. 先把 xlsx 和 xlsm 的差别掰开揉碎标题里这个问题的出现频率,比很多人想象的高得多。我这些年帮人处理过的表格里,十份"打不开""格式不对""宏没了"的文件,有六七份本质上是同一件事:文件后缀和里… · 2026/11/3 13:59:51

Redis 8.0拥抱AI:向量搜索+RAG+语义缓存全解析
Redis 8.0拥抱AI:向量搜索+RAG+语义缓存全解析

最近这阵子,技术圈里最热闹的话题之一,就是 Redis 正式官宣全面拥抱 AI 能力。如果你还停留在“Redis 就是一个缓存数据库”的印象,那今天这篇文章建议认真看完。我从 Redis 8.0 的向量搜索(Vector Set)聊到官方 AI 助… · 2026/11/3 13:59:51

计算机进制转换:原理、方法与应用实践
计算机进制转换:原理、方法与应用实践

1. 进制转换的基础概念与计算机应用 在计算机科学和电子工程领域,进制转换是一项基础但至关重要的技能。不同于日常生活中使用的十进制系统,计算机内部主要采用二进制(基数为2)、八进制(基数为8)和十六进制… · 2026/11/3 13:59:51

universal-modder:用大语言模型自动化单机游戏模组修改
universal-modder:用大语言模型自动化单机游戏模组修改

我最早接触改模组这件事,是从给一个像素风小游戏换贴图开始的。那时候没有教程,全靠把游戏目录翻个底朝天,用十六进制编辑器一个个文件对结构,改坏一次就重装一次。后来做多了,慢慢总结出一套流程:先备份&a… · 2026/11/3 13:59:51

逆向学习法:从交付物反推技能树,告别低效自学
逆向学习法:从交付物反推技能树,告别低效自学

我先说一个可能很多人都有过的经历:收藏夹里躺着几十个"从入门到精通"的教程,课程买了好几套,电脑里堆着各种学习笔记,但真正让你独立做出一个东西的时候,你还是会打开搜索引擎从"Hello World"开始… · 2026/11/3 13:59:51

IDEA新版UI问题分析与旧版还原指南
IDEA新版UI问题分析与旧版还原指南

1. 项目背景与需求分析JetBrains家的IDEA作为Java开发者最常用的IDE工具,每次UI更新都会引发广泛讨论。2023年推出的新UI设计采用了更扁平化的风格,虽然视觉上更现代,但不少老用户反馈操作效率反而下降。根据我的实际体验和社区调研&#xff… · 2026/11/3 13:58:50

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码