Vac*_*ano 3 sql-server partitioning sql-server-2012
假设我有一个分区orders.Order
表(日期时间列设置为创建行时)。 CreatedWhen
GetDate()
该表有超过 1000 万行,分区是每季度一次(或一些这样的固定时间间隔)。
如果我运行如下查询:
SELECT ord.OrderId, ord.ClientId, ord.ReceptId, ord.Cost, agt.State
FROM orders.Order ord
join personnel.Agent agt
on ord.AgentId = agt.AgentId
WHERE agt.FirstName = 'Bob'
and LastName = 'Whiely'
and ord.Cost > 135
Run Code Online (Sandbox Code Playgroud)
我会从这个查询的分区中看到任何好处吗?
我看不出有什么好处,因为我没有在查询中使用分区值,并且仍然需要搜索所有表。
注意:查询的结果都会在最近的分区中。但我看不出这会有什么帮助。
第二个注意事项:分区都在同一个 SAN 驱动器上(系统管理员不会让它以任何其他方式存在),因此磁盘 IO 不会并行。
有没有更好的分区方法,以便像上面那样的随机查询性能更好?(也许在主键上?)
第三个注意:上面的查询是组成的。我用它来表明我有许多查询命中了许多随机列(已正确编入索引)。
分区通常不是性能增强器。通常,分区用于实现高效的数据管理。例如,您可以使用分区来轻松地将表中的旧行交换到存档表中,一次一个分区。
查询不包含分区键的分区表会强制 SQL Server 查看整个表。这比简单地扫描或查找非分区表效率低,因为每个分区本质上都是一个表,需要UNION ALL
对所有分区进行样式查询。在查看分区表的查询计划时,这并不是特别明显,但如果仔细查看索引或表查找或扫描的弹出属性,您可以看到它:
为了使这一点更明显,让我们在 tempdb 中设置一个简单的测试设备,使用基于日期的分区键:
USE tempdb;
GO
IF OBJECT_ID(N'dbo.Tab', N'U') IS NOT NULL
BEGIN
DROP TABLE dbo.Tab;
DROP PARTITION SCHEME PartScheme;
DROP PARTITION FUNCTION PartFun;
END
GO
CREATE PARTITION FUNCTION PartFun (datetime)
AS RANGE LEFT
FOR VALUES (
N'2012-01-01T00:00:00'
, N'2012-04-01T00:00:00'
, N'2012-07-01T00:00:00'
, N'2012-10-01T00:00:00'
, N'2013-01-01T00:00:00'
, N'2013-04-01T00:00:00'
, N'2013-07-01T00:00:00'
, N'2013-10-01T00:00:00'
, N'2014-01-01T00:00:00'
, N'2014-04-01T00:00:00'
, N'2014-07-01T00:00:00'
, N'2014-10-01T00:00:00'
);
CREATE PARTITION SCHEME PartScheme
AS PARTITION PartFun
ALL TO ([PRIMARY]);
IF OBJECT_ID(N'dbo.Tab', N'U') IS NOT NULL
DROP TABLE dbo.Tab;
CREATE TABLE dbo.Tab
(
TabID int NOT NULL
, CreateDate datetime NOT NULL
, Data1 varchar(100) NOT NULL
, Data2 varchar(10) NOT NULL
, Data3 varchar(1000) NOT NULL
, CONSTRAINT PK_Tab
PRIMARY KEY CLUSTERED
(CreateDate, TabID)
) ON [PartScheme](CreateDate);
Run Code Online (Sandbox Code Playgroud)
这将用 1,000,000 行填充表,其中随机生成的数据均匀分布在所有分区上:
;WITH Ten AS
(
SELECT v.Num
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))v(Num)
)
, Million AS
(
SELECT Num = (t6.Num * POWER(10, 5))
+ (t5.Num * POWER(10, 4))
+ (t4.Num * POWER(10, 3))
+ (t3.Num * POWER(10, 2))
+ (t2.Num * POWER(10, 1))
+ (t1.Num)
FROM Ten t1
CROSS JOIN Ten t2
CROSS JOIN Ten t3
CROSS JOIN Ten t4
CROSS JOIN Ten t5
CROSS JOIN Ten t6
)
INSERT INTO dbo.Tab (TabID, CreateDate, Data1, Data2, Data3)
SELECT m.Num, DATEADD(DAY, m.Num % 1000, N'2012-01-01T00:00:00')
, CONVERT(varchar(100), CRYPT_GEN_RANDOM(100))
, CONVERT(varchar(10), CRYPT_GEN_RANDOM(10))
, CONVERT(varchar(1000), CRYPT_GEN_RANDOM(1000))
FROM Million m;
Run Code Online (Sandbox Code Playgroud)
让我们继续使用我们的分区方案创建一个非聚集索引:
CREATE NONCLUSTERED INDEX IX_Tab
ON dbo.Tab(Data1)
ON [PartScheme](CreateDate);
Run Code Online (Sandbox Code Playgroud)
现在,让我们看一下您可能执行的三个“典型”查询(第一个查询只是从表中获取一个实际值,然后在WHERE
三个查询的子句中使用该值):
DECLARE @d varchar(100);
SELECT TOP(1) @d = t.Data1
FROM dbo.Tab t
WHERE t.CreateDate > N'2013-08-24T00:00:00';
SELECT Data1
FROM dbo.Tab
WHERE Tab.Data1 = @d;
SELECT Data1
FROM dbo.Tab
WHERE Tab.Data1 = @d
AND tab.CreateDate >= N'2013-08-23T00:00:00'
SELECT Data1
FROM dbo.Tab
WHERE Tab.Data1 = @d
AND tab.CreateDate >= N'2013-08-23T00:00:00'
AND tab.CreateDate <= N'2013-08-29T00:00:00';
Run Code Online (Sandbox Code Playgroud)
这是执行计划的快速屏幕截图,因此您可以看到将分区键添加到WHERE
子句可以产生的差异:
“相对于批次的查询成本”数字在这里很有启发性。第一个查询消耗 50% 的批处理成本,因为它执行了所有 13 个分区的扫描。它这样做是因为它不知道表中的特定值Data1
存在于何处。第二个查询凭借“开始日期”参数仅消耗23%,可以消除一半的分区。最后,第三个查询只消耗 4%,因为它只需要查看单个分区,因为我们包含了“开始日期”和“结束日期”。
3 个查询的“统计信息”是:
表“标签”。扫描计数 13,逻辑读取 36,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“标签”。扫描计数 6,逻辑读取 16,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“标签”。扫描计数 1,逻辑读取 4,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
如果我们将其他列之一添加到这些查询中会怎样:
DECLARE @d varchar(100);
SELECT TOP(1) @d = t.Data1
FROM dbo.Tab t
WHERE t.CreateDate > N'2013-08-24T00:00:00';
SELECT Data1
, Data2
FROM dbo.Tab
WHERE Tab.Data1 = @d;
SELECT Data1
, Data2
FROM dbo.Tab
WHERE Tab.Data1 = @d
AND tab.CreateDate >= N'2013-08-23T00:00:00'
SELECT Data1
, Data2
FROM dbo.Tab
WHERE Tab.Data1 = @d
AND tab.CreateDate >= N'2013-08-23T00:00:00'
AND tab.CreateDate <= N'2013-08-29T00:00:00';
Run Code Online (Sandbox Code Playgroud)
执行计划:
这些人的统计数据:
表“标签”。扫描计数 13,逻辑读取 39,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“标签”。扫描计数 6,逻辑读取 19,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“标签”。扫描计数 1,逻辑读 7,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
因此,让我们取消分区,然后重新运行所有这些测试:
IF OBJECT_ID(N'dbo.TabU', N'U') IS NOT NULL
DROP TABLE dbo.TabU;
CREATE TABLE dbo.TabU
(
TabID int NOT NULL
, CreateDate datetime NOT NULL
, Data1 varchar(100) NOT NULL
, Data2 varchar(10) NOT NULL
, Data3 varchar(1000) NOT NULL
, CONSTRAINT PK_TabU
PRIMARY KEY CLUSTERED
(CreateDate, TabID)
) ON [PRIMARY];
;WITH Ten AS
(
SELECT v.Num
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))v(Num)
)
, Million AS
(
SELECT Num = (t6.Num * POWER(10, 5))
+ (t5.Num * POWER(10, 4))
+ (t4.Num * POWER(10, 3))
+ (t3.Num * POWER(10, 2))
+ (t2.Num * POWER(10, 1))
+ (t1.Num)
FROM Ten t1
CROSS JOIN Ten t2
CROSS JOIN Ten t3
CROSS JOIN Ten t4
CROSS JOIN Ten t5
CROSS JOIN Ten t6
)
INSERT INTO dbo.TabU (TabID, CreateDate, Data1, Data2, Data3)
SELECT m.Num, DATEADD(DAY, m.Num % 1000, N'2012-01-01T00:00:00')
, CONVERT(varchar(100), CRYPT_GEN_RANDOM(100))
, CONVERT(varchar(10), CRYPT_GEN_RANDOM(10))
, CONVERT(varchar(1000), CRYPT_GEN_RANDOM(1000))
FROM Million m;
CREATE NONCLUSTERED INDEX IX_TabU
ON dbo.TabU(Data1)
ON [PRIMARY];
Run Code Online (Sandbox Code Playgroud)
现在,第一组查询:
DECLARE @d varchar(100);
SELECT TOP(1) @d = t.Data1
FROM dbo.TabU t
WHERE t.CreateDate > N'2013-08-24T00:00:00';
SELECT Data1
FROM dbo.TabU
WHERE TabU.Data1 = @d;
SELECT Data1
FROM dbo.TabU
WHERE TabU.Data1 = @d
AND TabU.CreateDate >= N'2013-08-23T00:00:00'
SELECT Data1
FROM dbo.TabU
WHERE TabU.Data1 = @d
AND TabU.CreateDate >= N'2013-08-23T00:00:00'
AND TabU.CreateDate <= N'2013-08-29T00:00:00';
Run Code Online (Sandbox Code Playgroud)
和计划:
和统计数据:
表'TabU'。扫描计数 1,逻辑读取 4,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表'TabU'。扫描计数 1,逻辑读取 4,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表'TabU'。扫描计数 1,逻辑读取 4,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
所有三个查询现在都具有相同的“成本”,大大低于分区版本。
带有Data1
和Data2
列的查询:
DECLARE @d varchar(100);
SELECT TOP(1) @d = t.Data1
FROM dbo.TabU t
WHERE t.CreateDate > N'2013-08-24T00:00:00';
SELECT Data1
, Data2
FROM dbo.TabU
WHERE TabU.Data1 = @d;
SELECT Data1
, Data2
FROM dbo.TabU
WHERE TabU.Data1 = @d
AND TabU.CreateDate >= N'2013-08-23T00:00:00'
SELECT Data1
, Data2
FROM dbo.TabU
WHERE TabU.Data1 = @d
AND TabU.CreateDate >= N'2013-08-23T00:00:00'
AND TabU.CreateDate <= N'2013-08-29T00:00:00';
Run Code Online (Sandbox Code Playgroud)
和统计数据:
表'TabU'。扫描计数 1,逻辑读 8,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表'TabU'。扫描计数 1,逻辑读 8,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
表'TabU'。扫描计数 1,逻辑读 8,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
显然,如果您不能通过包含分区键以正确的方式进行查询,那么您将看到使用分区的性能比不使用时更差。
归档时间: |
|
查看次数: |
235 次 |
最近记录: |