为什么分区范围右跳过删除/插入?

Vac*_*ano 2 sql-server partitioning sql-server-2012

我有两个示例查询(如下所示)。两者(删除和)都在其中创建一个数据库和一个分区表。该表有几个分区,每个分区都有数据(包括最后一个,无界的,一个)。分区数据从 1 到 3996。

然后每个脚本添加一个新分区。新分区从 4000 开始。

在第一个脚本中,事务日志显示最后一个分区(分区 4)中的每条记录都被删除并重新插入。

第二个脚本显示没有发生行的活动(没有删除和插入)。

只有两个脚本之间的区别是RANGE LEFTVSRANGE RIGHT的分区函数。 RANGE RIGHT导致不删除或插入,RANGE LEFT导致最后一个分区中的所有行被删除和重新插入。

我想RANGE LEFTRANGE RIGHT只是控制边界值是否与左侧或右侧分区一致。但它显然也做了其他事情。

是否有更多的RANGE LEFTRANGE RIGHT我不明白?

另外,我喜欢在不影响系统的情况下添加分区的想法。RANGE RIGHT如果它让我知道,我愿意使用。但是,我担心这可能是某种错误,我不应该依赖它(因为它可能会在以后的版本中“修复”)。

这是一个可以依赖的“功能”吗?


脚本:

Range Left
删除和插入

use master
GO
-- Comment this next line out for the first run
DROP database PartitionTest
go
create database PartitionTest
go
use PartitionTest
go
-- Add Filegroups
ALTER DATABASE [PartitionTest] ADD FILEGROUP [DatePartitionTest];
GO
Alter database PartitionTest set recovery simple
go
-- Add Files
ALTER DATABASE [PartitionTest] ADD FILE ( NAME = N'PartitionTest_1', FILENAME = N'D:\PartitionTest_1.ndf') TO FILEGROUP [DatePartitionTest]
GO

-- This is the ONLY part that is different between the two scripts.  LEFT vs RIGHT
CREATE PARTITION FUNCTION [Orders_Id_Function](bigint) AS RANGE LEFT FOR VALUES
(1000,2000,3000)
GO

-- Create partition Scheme
CREATE PARTITION SCHEME [Orders__Scheme] AS PARTITION [Orders_Id_Function] TO
([DatePartitionTest],[DatePartitionTest],[DatePartitionTest],[DatePartitionTest])
-- Create table
CREATE TABLE [dbo].[Orders](
       [OrdDate] [datetime] NOT NULL,
       [ID] [bigint] IDENTITY(1,1) NOT NULL,
       [Addr] varchar(100) NOT NULL)

-- Partition the table
CREATE UNIQUE CLUSTERED INDEX IX_Orders
ON [Orders](OrdDate asc,ID asc)
ON [Orders__Scheme] (ID);
GO
-- Insert rows into  partitions (partition 4 in this case)
Use PartitionTest
set nocount on
go
declare @i int
set @i = 1
declare @date Datetime
while (@i < 1000)
begin
    set @date = dateadd(mi,@i,'2012-11-01T10:17:01.000')
    --insert into testtable values (@date)
    insert into [Orders] values (@date, 'Denzil')
    insert into [Orders] values (dateadd(month,3,@date), 'Denzil')
    insert into [Orders] values (dateadd(month,6,@date), 'Denzil')
    insert into [Orders] values (dateadd(month,9,@date), 'Denzil')
    set @i = @i+1;
END

-- Check the rowcount in each partition
select $PARTITION.[Orders_ID_Function](ID) as PartionNum,COUNT(*) as CountRows from  Orders
Group by $PARTITION.[Orders_ID_Function](ID)

Alter partition scheme [Orders__Scheme] NEXT USED [DatePartitionTest]
GO

-- Set a check point for the log file.
CHECKPOINT
GO

-- Add the new partition
ALTER PARTITION FUNCTION Orders_ID_Function() SPLIT RANGE (4000)

select Operation,count(*) as NumLogRecords from fn_dblog(NULL,NULL)
where AllocUnitName= 'dbo.Orders.IX_Orders'
group by Operation
order by count(*) DESC


SELECT
t.name as TableName,i.name as IndexName,
p.partition_id as partitionID,
p.partition_number,rows, fg.name
FROM sys.tables AS t 
         INNER JOIN sys.indexes AS i ON (t.object_id = i.object_id)
     INNER JOIN sys.partitions AS p ON (t.object_id = p.object_id and i.index_id = p.index_id)
        INNER JOIN sys.destination_data_spaces dds ON (p.partition_number = dds.destination_id)
        INNER JOIN sys.filegroups AS fg ON (dds.data_space_id = fg.data_space_id)
WHERE (t.name = 'Orders') and (i.index_id IN (0,1))
Run Code Online (Sandbox Code Playgroud)

范围右
DOES NOT做插入或删除

use master
GO
-- Comment this next line out for the first run
DROP database PartitionTest
go
create database PartitionTest
go
use PartitionTest
go
-- Add Filegroups
ALTER DATABASE [PartitionTest] ADD FILEGROUP [DatePartitionTest];
GO
Alter database PartitionTest set recovery simple
go
-- Add Files
ALTER DATABASE [PartitionTest] ADD FILE ( NAME = N'PartitionTest_1', FILENAME = N'D:\PartitionTest_1.ndf') TO FILEGROUP [DatePartitionTest]
GO

-- This is the ONLY part that is different between the two scripts.  LEFT vs RIGHT
CREATE PARTITION FUNCTION [Orders_Id_Function](bigint) AS RANGE RIGHT FOR VALUES
(1000,2000,3000)
GO

-- Create partition Scheme
CREATE PARTITION SCHEME [Orders__Scheme] AS PARTITION [Orders_Id_Function] TO
([DatePartitionTest],[DatePartitionTest],[DatePartitionTest],[DatePartitionTest])
-- Create table
CREATE TABLE [dbo].[Orders](
       [OrdDate] [datetime] NOT NULL,
       [ID] [bigint] IDENTITY(1,1) NOT NULL,
       [Addr] varchar(100) NOT NULL)

-- Partition the table
CREATE UNIQUE CLUSTERED INDEX IX_Orders
ON [Orders](OrdDate asc,ID asc)
ON [Orders__Scheme] (ID);
GO
-- Insert rows into  partitions (partition 4 in this case)
Use PartitionTest
set nocount on
go
declare @i int
set @i = 1
declare @date Datetime
while (@i < 1000)
begin
    set @date = dateadd(mi,@i,'2012-11-01T10:17:01.000')
    --insert into testtable values (@date)
    insert into [Orders] values (@date, 'Denzil')
    insert into [Orders] values (dateadd(month,3,@date), 'Denzil')
    insert into [Orders] values (dateadd(month,6,@date), 'Denzil')
    insert into [Orders] values (dateadd(month,9,@date), 'Denzil')
    set @i = @i+1;
END

-- Check the rowcount in each partition
select $PARTITION.[Orders_ID_Function](ID) as PartionNum,COUNT(*) as CountRows from  Orders
Group by $PARTITION.[Orders_ID_Function](ID)

Alter partition scheme [Orders__Scheme] NEXT USED [DatePartitionTest]
GO

-- Set a check point for the log file.
CHECKPOINT
GO

-- Add the new partition
ALTER PARTITION FUNCTION Orders_ID_Function() SPLIT RANGE (4000)

select Operation,count(*) as NumLogRecords from fn_dblog(NULL,NULL)
where AllocUnitName= 'dbo.Orders.IX_Orders'
group by Operation
order by count(*) DESC


SELECT
t.name as TableName,i.name as IndexName,
p.partition_id as partitionID,
p.partition_number,rows, fg.name
FROM sys.tables AS t 
         INNER JOIN sys.indexes AS i ON (t.object_id = i.object_id)
     INNER JOIN sys.partitions AS p ON (t.object_id = p.object_id and i.index_id = p.index_id)
        INNER JOIN sys.destination_data_spaces dds ON (p.partition_number = dds.destination_id)
        INNER JOIN sys.filegroups AS fg ON (dds.data_space_id = fg.data_space_id)
WHERE (t.name = 'Orders') and (i.index_id IN (0,1))
Run Code Online (Sandbox Code Playgroud)

Dan*_*man 6

我认为 RANGE LEFT 或 RANGE RIGHT 只是控制边界值是否与左侧或右侧分区一致。但它显然也做了其他事情。

还有更多我不明白的 RANGE LEFT 和 RANGE RIGHT 吗?

是的,有到其他的意义RANGE LEFT,并RIGHT为您的脚本证明。范围规范确定新创建的分区是在现有拆分分区的左侧还是右侧。范围规范还确定由 删除MERGE的分区,即包含现有边界的分区。

在第一个RANGE LEFT脚本中,现有的第 4 个分区被分成 2 个相邻的分区,新的分区在左侧。新分区成为分区#4,现有分区重新编号为#5。来自现有分区(现在是 #5)的行必须移动到 #4 以适应新的边界,因此日志记录过多。分区SWITCH(或MERGE)期间的移动需要大约 4 倍的正常 DML 操作的日志记录,因此计划避免数据移动非常重要,尤其是在表分区方案中经常使用的大表。

RANGE RIGHT 脚本中不需要数据移动,因为新分区 (#5) 是在现有分区 4 的右侧创建的,并且现有分区 4 已经符合这些边界(数据 >= 3000 和 < 4000)。

就个人而言,我认为RANGE RIGHT更直观,尤其是增量分区列值。有关更多问题,请参阅http://www.dbdelta.com/category/table-partitioning/


Pau*_*ite 5

这是一个可以依赖的“功能”吗?

不,这是观察到的(而不是记录在案的)行为。

需要明确的是,观察到的行为是如果拆分值右侧没有数据,则是SPLIT使用RIGHT边界的元数据操作;对于LEFT边界,如果左侧没有数据,则操作仅是元数据(此处有示例的博客文章)。

据我了解,这是关于拆分的哪一侧应该成为“新”分区的(任意)决定的结果。可以说,SQL Server 可能足够聪明,可以选择导致数据移动最少的选项,但今天还没有实现。

安全(已记录和支持)选项是仅拆分空分区。在滑动窗口实现中,这意味着在最边缘(右侧或左侧)上保持一个空分区

在左边界值或右边界值之间进行选择有很多很好的理由。拆分或合并行为的未记录方面不应包含在该决定中。