【问题标题】:What is the purpose of PAD_INDEX in this SQL Server constraint?此 SQL Server 约束中 PAD_INDEX 的用途是什么?
【发布时间】:2011-10-14 23:32:24
【问题描述】:

我的一张表应用了以下约束,但我不知道 PAD_INDEX 是什么意思。

谁能启发我?

CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED 
(
    [EmployeeId] ASC
) WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
        ^--------------^
         this part here

【问题讨论】:

  • 您好,欢迎来到 Stack Overflow。请查看How to Askfaq,了解如何在 Stack Overflow 上写出好的/适当的问题。我冒昧地清理了您的问题,以减少引起大量反对票的可能性。
  • 提示:如果您发现这个问题,您可能正在尝试使用 PAD_INDEX。需要注意的是,一旦启用 PAD_INDEX=ON,它将保持打开状态。如果您随后要重建索引但未指定 FILL_FACTOR,它仍然会打开。如果您打算将其关闭,请确保明确设置 PAD_INDEX=OFF。

标签: sql sql-server indexing


【解决方案1】:

基本上,如果您希望定期对索引进行大量随机更改,您可以设置 PAD_INDEX = ON。

这有助于避免索引页面拆分。

当我预计索引中包含 30% 以上的随机记录会被定期删除时,我将其设置为开启。

【讨论】:

  • 在实际用例中总是喜欢 cmets。
  • 很好,这解释了事情。如果我们不定期进行删除,请不要设置它
  • 我希望你只会在插入显着超过删除时才填充。删除释放空间,从而减少填充索引以防止拆分的需要。
【解决方案2】:

SQL Server 中的索引是B-Tree

  • FILLFACTOR 应用于底层
    这是下图中的叶子节点/数据层

  • PAD_INDEX ON 表示“将 FILLFACTOR 应用于所有层”
    这是下图中的中间层(根和数据之间)

这意味着 PAD_INDEX 只有在设置了 FILLFACTOR 时才有用。 FILLFACTOR 确定数据页中有多少可用空间(大致)

A picture from MSDN:

【讨论】:

  • 在此页面 msdn.microsoft.com/en-us/library/ms186869.aspx 上,当 pad_index 开启时,“FILLFACTOR 指定的可用空间百分比应用于索引的中间级别页面”。它也适用于根级别吗?也许这只是在线图书的疏忽。
  • 没有。根级别的填充取决于在给定填充请求的情况下保存索引中所有行所需的“根旁边”块的数量。如果您要显式设置根填充,您将无法控制中间块的填充,因为它们的计数将由根中的条目数(以匹配您设置的填充)和中间块填充决定由索引覆盖的行数。所以你只能控制一个或另一个,不能同时控制。
【解决方案3】:

来自MSDN

PAD_INDEX = { 开 |关闭}

指定索引填充。默认为关闭。

开启: 由 fillfactor 指定的可用空间百分比应用于索引的中间级别页面。

未指定关闭或填充因子: 考虑到中间页面上的键集,中间级页面被填充到接近容量,为索引可以拥有的最大大小的至少一行留出足够的空间。

PAD_INDEX 选项仅在指定 FILLFACTOR 时有用,因为 PAD_INDEX 使用 FILLFACTOR 指定的百分比。如果为 FILLFACTOR 指定的百分比不足以容纳一行,则数据库引擎会在内部覆盖该百分比以允许最小值。中间索引页上的行数永远不会少于 2,无论 fillfactor 的值有多低。

在向后兼容的语法中,WITH PAD_INDEX 等价于 WITH PAD_INDEX = ON。

【讨论】:

  • 投反对票,因为它纯粹是来自微软的副本,没有额外的阐述。
  • 实际上,我赞成这一点,因为这是最有意义的解释......即使它是从其他地方 C&P 的。
  • 投反对票,因为问题要求目的。纯粹的技术解释什么是什么没有说明什么时候使用它的目的和案例。
  • 在其他答案中包含这个非常有帮助。这个问题非常笼统,目的字面意思是“做某事的原因”。
【解决方案4】:

这实际上是一个非常复杂的主题。 开启 PAD_INDEX 会对大型表中的读取性能和内存压力产生显着影响。表越大,效果越大。作为一项规则,我会说你想把它关掉,除非你属于一些不常见的类别。然后,仔细遵循此建议。正如我在下面的示例中所示,当 PAD_INDEX 为 ON 时调整 FILLFACTOR 可能会产生指数效应,需要仔细平衡。

  1. PAD_INDEX 总是对读取产生不利影响! FILLFACTOR 越低效果越大,所以在开启时需要密切注意 FILLFACTOR 的值。在大型表上,您基本上停止考虑 FILLFACTOR 减少叶分裂,并开始考虑它对中间膨胀与中间分裂的影响
  2. PAD_INDEX 很少对少于 100,000 行的索引产生有用的影响,并且永远不会对覆盖标识或插入时间类型列的索引产生积极影响,因为插入总是到表的末尾。
  3. 从上面您应该看到,如果您打开 PAD_INDEX,您必须仔细平衡负面影响与正面影响。

经验法则:PAD_INDEX 很少用于:

  1. 非聚集索引 - 除非它们很宽。
  2. 关于非常窄的表的聚集索引。
  3. 在行数少于 100K 的表上 - 除非插入是高度聚集的,即使这样也可能存在问题。

您必须了解它的工作原理: 当您插入索引时,该行必须适合包含适当范围的键的叶块。聚集索引的行通常比非聚集索引宽得多,因此它们的叶块包含更少的行。 FillFactor 为叶子中的新行创建空间,但在非常宽的行或大量插入聚集在一起而不是均匀分布的情况下,创建足够的松弛(1-pct 填充)以防止分裂通常是不切实际或不可能的。

当发生拆分时,将创建一个新的中间行以指向新块,并且该行必须适合其相应的块。如果该中间块已满,则必须首先对其进行拆分。如果你特别不走运,分裂可以一直运行到根。当根分裂时,您最终会创建一个新的索引级别。

PAD_INDEX 的目的是在中间级别的块中强制使用最少的可用空间。

重建后,较低级别的空间可能很少或没有。因此,如果您有很多叶子分裂并且 PAD_INDEX 没有打开,那么您可以在整个地方大量分裂中间体!

但大多数情况下,可以使用 FILLFACTOR 管理拆分。更大的拆分问题发生在插入模式上,它几乎可以保证您没有足够的可用空间,然后打开 PAD_INDEX 通过提供更深层次的空间来帮助缓解这种情况,因此当确实发生拆分时,您不太可能招致大量的多级拆分。

示例案例

我有一个包含 100K 行的客户表。在任何一天,我的客户中约有 5% 是活跃的。我有一张按时间记录客户活动的表格。客户平均执行 20 次操作,而描述平均需要 1K。所以我收集了 100MB 的数据,并假设我已经有一年在表中 - 所以 36GB。

该表插入了 1Kb 行,其中 key 列的 customer_number 和 insert_time(按此顺序)。显然,普通客户会在插入他们预期的 20 行时多次拆分 8K 叶块,因为每一行将立即插入同一块中的前一行之后,直到它拆分、拆分和拆分(使人们认为只有非集群的堆索引...)。如果指向适当叶子的中间块没有足够的空间容纳至少 4 行(实际上可能是 8 行但是......),则中间块将需要拆分。鉴于此示例的密钥占用 22 个字节,一个中间块可以容纳 367 个条目。这意味着我的中间块需要 6% 的可用空间或 94% 的填充空间来容纳 4 个条目。

请注意,即使是 1% 的 FILLFACTOR 也不会阻止叶块拆分,因为一个块只能容纳 8 行。将 FILLFACTOR 设置为 80% 将只允许在叶拆分之前添加 1 行,但如果 PAD_INDEX 开启,则每个中间块将注入超过 800 字节的可用空间。当我只需要 88 个时,每个中间块大约有 800 个空字节。

这真的很重要!:所以如果我的表中已经有 36M 行,使用 80% 意味着每个中间块有 294 行,这意味着 122K 块,这意味着我已经将 98MB 注入到我的中间块中块结构,当 94% 让每个块适合 345 行时,因此只有 104K 中间块(是的,为了简单起见,我省略了较低的级别)。将 88 字节添加到 104K 块中的每个块只会增加 9.2MB,而不是 98MB。

现在考虑一下,我的客户中只有 5% 做了任何事情。有些做了超过 20 件事情,有些做的更少,所以无论如何都会拆分一些块,因为实际上只需要 275KB 来保存当天的索引行 (100k/8*22),最好的情况是我的 9.2MB 中只有 8.9MB 是死气沉沉的.如果防止分裂很重要,那么 9mb 非常值得,但我会更加努力地考虑 98mb。

因此,通过打开 PAD_INDEX,我应该完全放弃控制叶分裂并转向控制中间分裂。

除了第一个中级,别担心!任何聚类(在本例中是 customer_number 的聚类)都会引发蝴蝶效应,这会将您所做的任何计算抛出窗外。除非你的插入是完全一致的,否则你在找到合适的数字来平衡膨胀和分裂的误差通常远大于较低级别块空间的影响。

【讨论】:

  • 我想我会提到我并没有真正强调多少非常宽的索引加上宽行可以增加管理中间块拆分的需求。例如,如果一个索引在 500+ 字节范围内并且行是 2K+ 并且您有大量集群插入突发,您可能会发现自己正在考虑使用 Pad_Index。
  • 另外,不要忘记考虑不使用聚集索引。在删除很少且更新通常不会改变行大小的表中,堆的缺点几乎完全消失了,并且与使用聚集索引相比,它们往往允许在具有窄键的非常宽的表上实现更高的插入率。
【解决方案5】:

@bielawski 您仅描述 PAD_INDEX=ON 且 FILLFACTOR 介于 1 到 99 之间的情况。 您正在考虑设置 PAD_INDEX=ON 和 FILLFACTOR=0 或 100 以防我插入有序行,这些行总是比前一个更新。

CREATE CLUSTERED INDEX [IX_z_arch_export_dzienny_pre] ON [dbo].[z_arch_export_daily_pre]
(
    [Date] ASC,
    [Object Code] ASC,
    [From date] ASC,
    [Person_role] ASC,
    [Departure] ASC,
    [Room code] ASC,
    [period_7_14] ASC
)WITH (PAD_INDEX = ON, FILLFACTOR=100)


insert into z_arch_export_daily_pre
select * from export_daily_pre
order by [Date] ASC,[Object Code] ASC,[From date] ASC,[Person_role] ASC,[Departure] ASC,[Room code] ASC,[period_7_14] ASC

我有 100% 的保证,所有新行都将插入索引的“末尾”,并且只有使用此选项(PAD_INDEX = ON,FILLFACTOR=100)我才能在插入后实现 0.01% 的碎片索引。 在这种假设下,这个设置有什么危险吗?

【讨论】:

  • 这应该是评论,而不是答案:)
  • 是的,我知道,但是当我点击@bielawski 答案下的添加评论时,我得到的信息是,我至少有 50 名声望 :(
  • 不知道我是怎么错过这个答案的,但现在我已经看到了......没有理由重建一个只有在表末尾插入的索引。它永远不应该碎片化。鉴于您这样做(可能是因为某些维护随机删除了许多行),当 FILLFACTOR=100 时,我真的认为 ON 与 OFF 的结果没有任何区别。如果您始终可以看到一个,请更新您的帖子,以展示您在这种狭窄但非常常见的情况下发现的情况。
猜你喜欢
  • 1970-01-01
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-25
相关资源
最近更新 更多