具有大量记录的表的分区视图和性能

Posted

技术标签:

【中文标题】具有大量记录的表的分区视图和性能【英文标题】:Partitioned view and performance for the table with huge records 【发布时间】:2013-01-15 09:25:01 【问题描述】:

目前我面临查询和性能问题 存储过程。以下是场景:

我们在一个数据库 (SQL Server 2000 SP4) 中有 3-4 个表 有大量的记录。其中一张表超过2500万 记录。这些表保存着销售记录和成千上万的 每天添加到其中的记录。每当执行存储过程时,它需要 15-30分钟完成。表上有 3-4 个连接。用户是 经常抱怨它。索引是正确的。为了提高性能 我们已经实现了分区视图。该解决方案由 引用以下article on MSDN

我们按年度和业绩划分了销售记录 已改进,查询/存储过程现在需要 3-5 分钟才能运行。改善 进一步,我们按月划分销售记录。我们在维护 4 年的数据,现在我们接近 48 个销售数据表(之后 按月拆分销售数据)。我期待这可以提高性能。但 这没有发生。查询的执行速度比前一个慢得多 (数据的年度拆分)这让我感到惊讶。也看过之后 查询计划我发现它正在对所有 48 个销售表进行索引扫描 只扫描相关表。例如。当被查询存储过程时 期间19-NOV-201220-DEC-2012,应该只考虑2个表NOV-2012DEC-2012。但它正在考虑所有 48 张桌子。所以我的问题是:

    为什么要考虑所有表而不是只考虑 相关表格。例如。在上面的例子中NOV-2012DEC-2012

    为什么年度逻辑(按年份拆分销售记录)是 表现优于按月逻辑(按月拆分销售记录)

以下是分区视图的代码。 例如年份 其他年份省略。

    SELECT * FROM tbl_Sales_Jan2010
UNION ALL
SELECT * FROM tbl_Sales_Feb2010
UNION ALL
SELECT * FROM tbl_Sales_Mar2010
UNION ALL
SELECT * FROM tbl_Sales_Apr2010
UNION ALL
SELECT * FROM tbl_Sales_May2010
UNION ALL
SELECT * FROM tbl_Sales_Jun2010
UNION ALL
SELECT * FROM tbl_Sales_Jul2010
UNION ALL
SELECT * FROM tbl_Sales_Aug2010
UNION ALL
SELECT * FROM tbl_Sales_Sep2010
UNION ALL
SELECT * FROM tbl_Sales_Oct2010
UNION ALL
SELECT * FROM tbl_Sales_Nov2010
UNION ALL
SELECT * FROM tbl_Sales_Dec2010

以下是表结构。

CREATE TABLE [dbo].[tbl_Sales_Jan2010](
    [SalesID] [numeric](10, 0) NOT NULL,
    [StoreNumber] [char](3) NOT NULL,
    [SomeColumn1] [varchar](15) NOT NULL,
    [Quantity] [int] NOT NULL,
    [SalePrice] [numeric](18, 2) NOT NULL,
    [SaleDate] [datetime] NOT NULL,
    [DeptID] [int] NOT NULL,
    [CatCode] [char](3) NOT NULL,
    [AuditDate] [datetime] NOT NULL CONSTRAINT [DF_tbl_Sales_Jan2010_EditDate]  DEFAULT (getdate()),
    [SomeColumn2] [varchar](15) NULL,
    [SaleMonthYear] [int] NULL CONSTRAINT [DF__tbl_Sales__SaleY__Jan2010]  DEFAULT (12010),
    [SaleDateInIntFormat] [int] NULL,
 CONSTRAINT [PK_tbl_Sales_Jan2010] PRIMARY KEY CLUSTERED 
(
    [SalesID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[tbl_Sales_Jan2010]  WITH CHECK ADD CHECK  (([SaleMonthYear] = 12010))

下面是查询

SELECT     SUM(C.Quantity) as total
    FROM         Productdatabase.dbo.tbl_Product A , Productdatabase.dbo.tbl_Product_Category B, XDatabase.dbo.vw_Sales_Test C, tbl_Store D
    WHERE     A.ProductID = B.ProductID AND B.CategoryID = @CateID
    AND C.SomeColumn = A.PRoductCode
    AND D.StoreCode = C.StoreNumber
    AND D.country = @country
    AND D.status = 0
    And C.SaleMonthYear between @BeginMonthYear and @EndMonthYear               
    AND C.SalDate between @FromSaleDate and @ToSaleDate     

【问题讨论】:

表设计、分区视图详细信息、索引详细信息和实际查询真的很有帮助 2500 万可能比你习惯的要多,但也不是巨大 我想看看查询计划。我看不出分区消除在这里不起作用的原因。但是,您已经交换了月份和年份,这可能会让您相信它不起作用...... 【参考方案1】:

设置分区的人并没有真正想到他在做什么。除了不使用分区(这是一个 SQL Server 功能)之外,很可能是为了成本......

SELECT * FROM tbl_Sales_Jan2010

在联合中添加 WHERE 条件,然后查询分析器可以排除由于错误的 where 子句而不相关的表。 IE。添加:

(([SaleMonthYear] = 12010

就在那儿。

其次,解决您的其他问题。真的。要点是:

我们在一个数据库 (SQL Server 2000 SP4) 中有 3-4 个表,它们有巨大的 记录的数量。其中一张表超过2500万 记录。

让我笑一笑。 2500万不算小,不算小,但“休”是什么?我的意思是,我使用的表格每天添加数亿行并将数据保留 2 年。 2500 万是中端服务器可以轻松处理的。我建议你要么硬件不好(我的意思是坏的),要么真的发生了其他事情。

设计问题,例如:

[SaleMonthYear]

这不应该存在 - 它应该是 SaleYearMonth,因此您可以进行范围测试(在 201005 和 201008 之间),而您现在无法有效地执行该测试,并且如果您曾经使用它,那么您将完全破坏任何索引排序。

这太荒谬了,因为这是一个你完全没有收获的数字。

Whenever a stored procedure is executed it takes 15-30 minutes to complete

让我在这里说清楚。在可接受的中档硬件上(即适当的服务器、32-64gb 内存、一打到 24 个高速磁盘),这不可能需要 15 到 30 分钟。不是你在那里写的代码。

除非您遇到锁拥塞(糟糕的应用程序设计)或服务器过载等其他问题(糟糕的应用程序设计/糟糕的管理)。我希望这样的查询具有适当的索引,以低于一分钟的方式返回。

无论如何,分区通过快速消除大量检查来工作 - 并且在您的情况下也是/主要是删除优化(您可以只删除表,不需要删除语句进行硬索引更新)。但是,您实现它的方式不是 MS sasys 应该完成的方式,不是逻辑所说的应该完成的方式,并且不会给出任何结果,因为您的分区未集成到查询中。

如果你查看表和查询,它仍然必须检查每个表。

【讨论】:

非常感谢您的详细回复。我已经实施了您的建议(在视图中添加 where 子句并修复 SaleMonthYear 列)。性能肯定有所提高。但是查询计划仍然显示它正在扫描所有销售表。所以我认为肯定有改进的余地。正如您提到的,分区未正确实施可能是因为成本。您能否提供有关分区的建议以及它对成本的影响? SQL Server Enterprise License 支持分区功能。检查文档。许可成本开销是 - 啊 - 陡峭。在您的方法中,查询计划将始终显示所有表 - 问题是它们是否发生了真正的 IO。逻辑上它们被评估(只是错误的,所以没有真正的 IO)。在 management studio 中运行查询,检查 REAL(未评估)查询计划的实际开销。 是的。我已经检查了前面提到的查询的实际执行计划。以下是实际执行计划中的一些值。对于 tbl_Sales_Jan2012 估计 I/O 成本 - 3.27397744 执行次数 - 4 CPU 成本 -0.1980018 对于 tbl_Sales_Feb2012 估计 I/O 成本 - 5.8109117 执行次数 - 0.3508584 CPU 成本 - 4 @parag 这些数字非常低。 3POINT2 io 不是很多。我会假设“真实”表格的表格要高得多?喜欢 - 一百次或更多? @parag Btw.,感谢您告诉我我的回答很有帮助,然后直接转到另一个答案,这只是一个文档引用并使其正确,因此他得到了破坏。干得好。【参考方案2】:

来自您引用的同一篇 MSDN 文章:

分区视图不需要检查约束来返回正确的结果。但是,如果尚未定义 CHECK 约束,则查询优化器必须搜索所有表,而不是仅搜索那些覆盖分区列上的搜索条件的表。如果没有 CHECK 约束,视图的运行方式与任何其他带有 UNION ALL 的视图一样。查询优化器不能对存储在不同表中的值做出任何假设,也不能跳过搜索参与视图定义的表。

在您的问题中,您指定了一个日期范围为 2012 年 11 月 19 日至 2012 年 12 月 20 日的查询。我假设这将是 SaleDate 列中包含的值,但您的约束是 SaleMonthYear 列。

您确定定义的约束是正确的吗?您能否也发布您的查询?

拉吉

【讨论】:

我认为您打算将该块作为引用,而不是作为代码(作为代码,它都在一行中,产生了一个巨大的滚动条,并且它也试图在语法上突出显示它。跨度> 是的,我确实想引用。很抱歉混淆了。感谢修复。 -1。完全不相关,不解释重点,也不是问题的答案。 TomTom - 在 OP 发布实际查询之前发布了响应。感谢您的反对

以上是关于具有大量记录的表的分区视图和性能的主要内容,如果未能解决你的问题,请参考以下文章

MySQL分区和分表

处理具有数百万条记录更新和大量读数的 MySQL 表的最佳方法

mysql分表和表分区详解

mysql分表和表分区详解

如何针对具有记录字段的表创建视图?

mysql分表和表分区详解