[SQL]Partition Table 的實作筆記

[SQL]Partition Table 的實作筆記

最近為了讓一些大型資料庫的效能能夠變得更好,因此希望配合 SQL Server Enterprise 版本來測試相關 Partition Table 的功能,因此把相關實驗環境整理下來。

 

1. 環境預備

image

-- 建立測試資料庫
create database [PartitionTest]
GO

-- 建立 4 個 FILEGROUP 
alter database [PartitionTest] add filegroup [PartitionTestFG201201];
alter database [PartitionTest] add filegroup [PartitionTestFG201202];
alter database [PartitionTest] add filegroup [PartitionTestFG201203];
alter database [PartitionTest] add filegroup [PartitionTestFG999999];

use [PartitionTest]
GO
declare @path nvarchar(128);
declare @PTR int ;
declare @ID  varchar(2)
declare @SQL VARCHAR(1000)

select @path=physical_name from sys.database_files where file_id =1

-- 每個 FileGroup 至少要指定一個檔案
set @PTR = 1
while @PTR < 4
begin
  set @ID = RIGHT('00'+LTRIM(STR(@PTR)),2 )
  set @SQL = 'alter database [PartitionTest] add file ( name=[PartitionTestFile2012'+@ID+'_1], filename="'+@path+'PartitionTestFile2012'+@ID+'_1.ndf", size=10MB ) to filegroup [PartitionTestFG2012'+@ID+']' ;
  exec( @SQL )
  set @PTR += 1
end 
set @SQL = 'alter database [PartitionTest] add file ( name=[PartitionTestFile999999_1], filename="'+@path+'PartitionTestFile'+@ID+'_1.ndf", size=10MB ) to filegroup [PartitionTestFG999999]' ;
exec( @SQL )

 

2. 以日期區間建立 Partition Function,並建立 Partition Schema 根據 Partition Function 的值放到對應的檔案群組上

-- 按照日期區分, 懶得考慮每月最後一天是哪一天, 先用字串混過去囉
create partition function pfPartitionTest(nvarchar(8)) 
    as range left for values (N'20120131', N'20120231', N'20120331');

-- 依據 Partition Function 建立 Partition Schema
create partition scheme psPartitionTest
as partition pfPartitionTest
TO ([PartitionTestFG201201], [PartitionTestFG201202], [PartitionTestFG201203], [PartitionTestFG999999]);

 

3. 為了方便後續檢查 Partition Table 的資料是否有按照 Partition 存放,因此建立一個用來查詢 Partition 使用狀況的 UDF

-- 建立一個檢查用的 Function
create function PartitionInfo( @tablename sysname ) returns table
as return
select 
OBJECT_NAME(p.object_id) as TableName
,p.partition_number as PartitionNumber
,prv_left.value as LowerBoundary
,prv_right.value as  UpperBoundary
,ps.name as PartitionScheme
,pf.name as PartitionFunction
,fg.name as FileGroupName
,CAST(p.used_page_count * 8.0 / 1024 AS NUMERIC(18,2)) AS UsedPages_MB
,p.row_count as Rows
from sys.dm_db_partition_stats p
inner join sys.indexes i ON i.object_id = p.object_id AND i.index_id = p.index_id
inner join sys.partition_schemes ps ON ps.data_space_id = i.data_space_id
inner join sys.partition_functions pf ON ps.function_id = pf.function_id
inner join sys.destination_data_spaces dds ON dds.partition_scheme_id = ps.data_space_id AND dds.destination_id = p.partition_number
inner join sys.filegroups fg ON fg.data_space_id = dds.data_space_id
left  join sys.partition_range_values prv_right ON prv_right.function_id = ps.function_id AND prv_right.boundary_id = p.partition_number
left  join sys.partition_range_values prv_left  ON prv_left.function_id = ps.function_id AND prv_left.boundary_id = p.partition_number - 1
where
 p.object_id = OBJECT_ID(@tablename) and p.index_id < 2

 

4. 建立測試用的 Table,這裡要特別注意幾點,如果我們的 Table 要建立 Primary Key 或者是 Unique Index 的時候,一定要包含 Partition 分類的欄位;而如果我們的 Table 又有 Cluster Index 的時候,則後續如果要將資料做 Switch 的時候,則目的 Table 也要有 Cluster Index,因此這裡我先把這兩個狀況合併,直接建立一個 CLUSTER Index 的 Primary Key。

USE [PartitionTest]
GO

-- 建立 Table 一開始就使用 Partition Schema
CREATE TABLE [dbo].[A](
    [A01] [nvarchar](4) NOT NULL,
    [A02] [nvarchar](8) NOT NULL,
    [A03] [nvarchar](10) NULL,
    [A04] [nvarchar](10) NULL,
 CONSTRAINT [PK_A] PRIMARY KEY CLUSTERED 
(
    [A02] ASC,
    [A01] ASC
)
) ON psPartitionTest(A02)

 

5. 放入測試資料,透過 UDF 的資料表,我們可以看到資料有放在三個 Partition 上各有兩筆

-- 放些 Demo 資料
insert into A values
( '0001', '20120101' , '1', '1' ),
( '0002', '20120102' , '2', '2' ),
( '0003', '20120201' , '3', '3' ),
( '0004', '20120201' , '4', '4' ),
( '0005', '20120301' , '5', '5' ),
( '0006', '20120302' , '6', '6' )


-- 利用 UDF 來檢查 Partition 資料存放狀況
select * from dbo.PartitionInfo('dbo.A')

 

6. 接下來我們要模擬在切割出一個 Partition 出來單獨放 2012/04/01 ~2012/04/31 的資料,因此我們要建立一個新的檔案群組,並且調整 Partition Function,讓 Partition Schema 會將資料放到這個新的檔案群組上

declare @path nvarchar(128);
declare @ID  varchar(2)
declare @SQL VARCHAR(1000)

select @path=physical_name from sys.database_files where file_id =1

-- 產生一個四月的檔案群組
set @ID = '04'
set @SQL = 'alter database [PartitionTest] add filegroup [PartitionTestFG2012'+@ID+']' ;
exec( @SQL )
-- 產生一個四月的資料檔案
set @SQL = 'alter database [PartitionTest] add file ( name=[PartitionTestFile'+@ID+'_1], filename="'+@path+'PartitionTestFile'+@ID+'_1.ndf", size=10MB ) to filegroup [PartitionTestFG2012'+@ID+']' ;
exec( @SQL )
-- 變更 Partition Schema 指定新的資料群組是 PartitionTestFG201204
set @SQL = 'alter partition scheme [psPartitionTest] next used [PartitionTestFG2012'+@ID+']' ;
-- 變更 Partition Function 
exec( @SQL )
set @SQL = 'alter partition function [pfPartitionTest]() split range ( 2012'+@ID+'31 )';
exec( @SQL )

 

7. 接著我們測試放入四月的資料看是否會放到正確的檔案群組上

-- 放入四月份資料
insert into A values
( '1001', '20120401' , 'X', 'Y' ),
( '1002', '20120402' , 'X', 'Y' ),
( '1003', '20120403' , 'X', 'Y' ),
( '1004', '20120404' , 'X', 'Y' )

-- 檢查資料
select * from dbo.PartitionInfo('dbo.A')

 

8. 接下來就是比較麻煩的刪除資料,我們預計使用 Alter Table Switch 的方式,但因為我們原本建立的 Table 有建立 Cluster Index,因此這裡我們可以用兩個方式

A. 單獨建立一個 Table 跟要移出資料的 Partition 在同一個檔案群組上, EX:

-- 建立一個同樣跟三月份資料同在一個檔案群組的 Table
CREATE TABLE MOCK_A_PartitionTestFG201203(
    [A01] [nvarchar](4) NOT NULL,
    [A02] [nvarchar](8) NOT NULL,
    [A03] [nvarchar](10) NULL,
    [A04] [nvarchar](10) NULL,
    PRIMARY KEY CLUSTERED (    [A02] ASC,    [A01] ASC)
) ON PartitionTestFG201203
GO

-- 把資料搬移到仿造的 Table 上
alter table A switch partition 3 to MOCK_A_PartitionTestFG201203

B. 或者是建立一個使用同樣  Partition Schema 的另外一個 Table,此時就可以對照的 Partition ID 相互移轉了

-- 建立一個假的 A Table 來刪除資料搬移用,但此時是原 Table 使用相同的 Partition Schema
CREATE TABLE MOCK_A(
    [A01] [nvarchar](4) NOT NULL,
    [A02] [nvarchar](8) NOT NULL,
    [A03] [nvarchar](10) NULL,
    [A04] [nvarchar](10) NULL,
    PRIMARY KEY CLUSTERED (    [A02] ASC,    [A01] ASC)
) ON psPartitionTest(A02)
GO

-- 既然兩個 Table 都有相同的 Schema , 那麼就相同的 Partition ID 去相互搬移
alter table A switch partition 1 to MOCK_A partition 1

 

9. 如果按照上述的方式,假設我們已經把 2012/01 月份的資料都移出了,此時我們可能想要回收之前所建立的檔案群組,則要注意處理的先後順序,一定要先刪除資料檔案之後,才可以刪除檔案群組。

use [PartionTest]
GO
-- 把 Partition 1 給取消
alter partition function pfPartitionTest() merge range ( 20120131 )

select * from [dbo].[PartitionInfo]('dbo.A')

-- 依序刪除,先刪除檔案再刪除 FG
use [master]
GO
alter database [PartitionTest] remove file [PartitionTestFile201201_1]
alter database [PartitionTest] remove filegroup PartitionTestFG201201

 


 

後記:應該一般會使用到的狀況和基本功能都應該涵蓋了,但因為手邊的設備還不夠有力作壓力測試和相關效能測試,或許等以後有比較多資料的時候,再來提供相關測試數據跟大家來做分享。