【问题标题】:SQL Server table partitioning based on a modulus function?基于模数函数的 SQL Server 表分区?
【发布时间】:2011-01-11 20:14:09
【问题描述】:

我有一个非常大的表(10+ 百万行),它开始显示查询性能下降的迹象。由于该表的大小可能很快就会增加一倍或三倍,因此我正在考虑对表进行分区以挤出一些查询性能。

表格如下所示:

CREATE TABLE [my_data] (
    [id] [int] IDENTITY(1,1) NOT NULL,
    [topic_id] [int] NULL,
    [data_value] [decimal](19, 5) NULL
)

因此,任何给定主题的一堆值。对该表的查询将始终按主题 ID,因此 (id, topic_id) 上有一个聚集索引。

无论如何,由于主题 ID 没有限制(可以添加任意数量的主题),我想尝试在主题 ID 的模函数上对该表进行分区。所以像:

topic_id % 4 == 0 => partition 0
topic_id % 4 == 1 => partition 1
topic_id % 4 == 2 => partition 2
topic_id % 4 == 3 => partition 3

但是,我还没有看到任何方法告诉“创建分区函数”或“创建分区方案”在决定分区时执行此操作。

这甚至可能吗?我们如何根据对输入值执行的操作来制作分区函数?

【问题讨论】:

  • "此表上的查询将始终按主题 ID,因此 (id, topic_id) 上有一个聚集索引。" (topic_id, id) 上的聚集索引不是更有意义吗?
  • 嗨,马克,抱歉,索引确实在 (id, topic_id) 上

标签: sql-server sql-server-2005


【解决方案1】:

您只需将模数列创建为 PERSISTED 计算列。

Blue Peter 风格,这是我之前制作的风格(虽然我不能 100% 确定我的分区值条款是正确的):

CREATE PARTITION FUNCTION [PF_PartitonFour] (int)
AS RANGE RIGHT
FOR VALUES (
  0,
  1,
  2)
GO

CREATE PARTITION SCHEME [PS_PartitionFourScheme]
AS PARTITION [PF_PartitonFour]
TO ([TestPartitionGroup1],
    [TestPartitionGroup2],
    [TestPartitionGroup3],
    [TestPartitionGroup4])
GO

CREATE TABLE [my_data] (
  [id] [int] IDENTITY(1,1) NOT NULL,
  [topic_id] [int] NULL,
  [data_value] [decimal](19, 5) NULL
  [PartitionElement] AS [topic_id] % 4 PERSISTED,
) ON [PS_PartitionFourScheme] (PartitionElement);
GO

【讨论】:

  • 谢谢!这正是我想要的。
  • 技术上是正确的,但这不会错过 OP 的问题(性能)吗?
  • 不,它只是回答被问的问题,而不是我认为应该问的问题。另外,从谷歌找到这个问题的人不会想找到大量的说教,他们会想找到如何根据模函数对表进行分区,如果我错了,请纠正我,这就是我给出的答案。
  • 好的,很公平。我读到“......我正在研究对表进行分区以挤出一些查询性能。”并直接说,“好吧,分区不是你想要解决这个问题的方法。” :-)
  • 不起作用,因为创建索引时 SQL 会出现错误:“重复的键值是...”,因为模函数会生成重复值,所以这是真的。有没有办法解决并创建真正的模分区?
【解决方案2】:

哈希分区在 SQL Server 2005/2008 中不可用。您必须使用范围分区。

话虽如此,您应该知道分区主要是一种存储选项,请参阅Partitioned Table and Index Concepts

分区使大表或 索引更易于管理,因为 分区使您能够管理和 快速访问数据子集 有效地,同时保持 数据收集的完整性。经过 使用分区,这样的操作 作为加载数据从 OLTP 到 OLAP 系统只需几秒钟, 而不是分钟和小时 操作需要早期版本的 SQL 服务器。 维护操作 对数据子集执行的 也更有效地执行 因为这些操作只针对 所需的数据,而不是 整张桌子。

如您所见,MSDN 中对分区的介绍侧重于维护、可管理性和数据负载。根据我的经验,分区最多只能提供 0 的性能提升。特别是在 SQL 2005 中。通常它会降低性能。为了提高性能,您应该使用正确的聚集索引和正确设计的非聚集索引。

在 SQL 2008 中,如果从 IO 的角度来看,并行运算符在分区方面得到了适当的分布,则它们会得到改进,请参阅Designing Partitions to Improve Query Performance。尽管它们的好处是微不足道的,并且被正确设计的一组聚集和非聚集索引的好处所掩盖。例如,(id, topic_id) 中的聚集索引,其中 id 是一个身份,仅用于通过 id 查找单个项目。另一方面,(topic_id,id)的聚集索引将有利于任何查找特定主题的查询。我不知道你的系统要求和你运行的查询,但是在这么窄的表上 10M 行的性能问题闻起来像索引和查询问题,没有分区问题。

【讨论】:

  • 谢谢!将索引从 (id, topic_id) 更改为 (topic_id, id) 显着加快了查询速度。我希望我能在这里给出两个公认的答案,但马特的答案正是我想要的。
【解决方案3】:

从文档来看,您似乎必须为函数赋值:

创建 4 个分区...

CREATE PARTITION FUNCTION myRangePF1 (int)
AS RANGE LEFT FOR VALUES (1, 100, 1000);

难道您不能在此调用之上进行计算并找到要拆分的正确值吗?将值替换为调用?还是我错过了为什么要使用模数?根据您的 ID 存在差距的可能性,您可能需要使用一些统计数学来找出分区的位置。

CREATE PARTITION FUNCTION myRangePF1 (int)
AS RANGE LEFT FOR VALUES (@low, @Med, @High);

【讨论】:

    【解决方案4】:

    1000 万行对于 SQL 服务器来说并不算多;常规索引设计可能无需分区即可解决此问题。如前所述,尝试在不同的列集上进行聚类;在 topicid 上进行聚类,id 似乎需要测试,特别是如果大多数查询都以 topicid 作为标准。像这样的聚集索引与分区的效果大致相同,至少因为它将相关的数据行组合在磁盘上,并允许范围扫描快速获取它们。

    如果该设计有效,您只需要担心插入的碎片,但这是可以管理的。正确建立索引后,请确保您有足够的 RAM,并且没有磁盘瓶颈。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-24
      • 2018-10-12
      • 2014-04-05
      • 1970-01-01
      • 2019-12-24
      • 1970-01-01
      • 1970-01-01
      • 2019-02-18
      相关资源
      最近更新 更多