理解SQL SERVER中的分割槽表
簡介
分割槽表是在SQL SERVER2005之後的版本引入的特性。這個特性允許把邏輯上的一個表在物理上分為很多部分。而對於SQL SERVER2005之前版本,所謂的分割槽表僅僅是分散式檢視,也就是多個表做union操作.
分割槽表在邏輯上是一個表,而物理上是多個表.這意味著從使用者的角度來看,分割槽表和普通表是一樣的。這個概念可以簡單如下圖所示:
而對於SQL SERVER2005之前的版本,是沒有分割槽這個概念的,所謂的分割槽僅僅是分散式檢視:
本篇文章所講述的分割槽表指的是SQL SERVER2005之後引入的分割槽表特性.
為什麼要對錶進行分割槽
在回答標題的問題之前,需要說明的是,表分割槽這個特性只有在企業版或者開發版中才有,還有理解表分割槽的概念還需要理解SQL SERVER中檔案和檔案組的概念.
對錶進行分割槽在多種場景下都需要被用到.通常來說,使用表分割槽最主要是用於:
- 存檔,比如將銷售記錄中1年前的資料分到一個專門存檔的伺服器中
- 便於管理,比如把一個大表分成若干個小表,則備份和恢復的時候不再需要備份整個表,可以單獨備份分割槽
- 提高可用性,當一個分割槽跪了以後,只有一個分割槽不可用,其它分割槽不受影響
- 提高效能,這個往往是大多數人分割槽的目的,把一個表分佈到不同的硬碟或其他儲存介質中,會大大提升查詢的速度.
分割槽表的步驟
分割槽表的定義大體上分為三個步驟:
- 定義分割槽函式
- 定義分割槽構架
- 定義分割槽表
分割槽函式,分割槽構架和分割槽表的關係如下:
分割槽表依賴分割槽構架,而分割槽構架又依賴分割槽函式.值得注意的是,分割槽函式並不屬於具體的分割槽構架和分割槽表,他們之間的關係僅僅是使用關係.
下面我們通過一個例子來看如何定義一個分割槽表:
假設我們需要定義的分割槽表結構如下:
第一列為自增列,orderid為訂單id列,SalesDate為訂單日期列,也就是我們需要分割槽的依據.
下面我們按照上面所說的三個步驟來實現分割槽表.
定義分割槽函式
分割槽函式是用於判定資料行該屬於哪個分割槽,通過分割槽函式中設定邊界值來使得根據行中特定列的值來確定其分割槽,上面例子中,我們可以通過SalesDate的值來判定其不同的分割槽.假設我們想定義兩個邊界值(boundaryValue)進行分割槽,則會生成三個分割槽,這裡我設定邊界值分別為2004-01-01和2007-01-01,則前面例子中的表會根據這兩個邊界值分成三個區:
在MSDN中,定義分割槽函式的原型如下:
CREATE PARTITION FUNCTION partition_function_name ( input_parameter_type ) AS RANGE [ LEFT | RIGHT ] FOR VALUES ( [ boundary_value [ ,...n ] ] ) [ ; ]
通過定義分割槽函式的原型,我們看出其中並沒有具體涉及具體的表.因為分割槽函式並不和具體的表相繫結.上面原型中還可以看到Range left和right.這個引數是決定臨界值本身應該歸於“left”還是“right”:
下面我們根據上面的引數定義分割槽函式:
通過系統檢視,可以看見這個分割槽函式已經建立成功
定義分割槽構架
定義完分割槽函式僅僅是知道了如何將列的值區分到了不同的分割槽。而每個分割槽的儲存方式,則需要分割槽構架來定義.使用分割槽構架需要你對檔案和檔案組有點了解.
我們先來看MSDN的分割槽構架的原型:
CREATE PARTITION SCHEME partition_scheme_name AS PARTITION partition_function_name [ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] ) [ ; ]
從原型來看,分割槽構架僅僅是依賴分割槽函式.分割槽構架中負責分配每個區屬於哪個檔案組,而分割槽函式是決定如何在邏輯上分割槽:
基於之前建立的分割槽函式,建立分割槽構架:
定義分割槽表
接下來就該建立分割槽表了.表在建立的時候就已經決定是否是分割槽表了。雖然在很多情況下都是你在發現已經表已經足夠大的時候才想到要把表分割槽,但是分割槽表只能夠在建立的時候指定為分割槽表。
為剛建立的分割槽表PartitionedTable加入5萬條測試資料,其中SalesDate隨機生成,從2001年到2010年隨機分佈.加入資料後,我們通過如下語句來看結果:
select convert(varchar(50), ps.name) as partition_scheme, p.partition_number, convert(varchar(10), ds2.name) as filegroup, convert(varchar(19), isnull(v.value, ''), 120) as range_boundary, str(p.rows, 9) as rows from sys.indexes i join sys.partition_schemes ps on i.data_space_id = ps.data_space_id join sys.destination_data_spaces dds on ps.data_space_id = dds.partition_scheme_id join sys.data_spaces ds2 on dds.data_space_id = ds2.data_space_id join sys.partitions p on dds.destination_id = p.partition_number and p.object_id = i.object_id and p.index_id = i.index_id join sys.partition_functions pf on ps.function_id = pf.function_id LEFT JOIN sys.Partition_Range_values v on pf.function_id = v.function_id and v.boundary_id = p.partition_number - pf.boundary_value_on_right WHERE i.object_id = object_id('PartitionedTable') and i.index_id in (0, 1) order by p.partition_number
可以看到我們分割槽的資料分佈:
分割槽表的分割
分割槽表的分割。相當於新建一個分割槽,將原有的分割槽需要分割的內容插入新的分割槽,然後刪除老的分割槽的內容,概念如下圖:
假設我新加入一個分割點:2009-01-01,則概念如下:
通過上圖我們可以看出,如果分割時,被分割的分割槽3內有內容需要分割到分割槽4,則這些資料需要被複制到分割槽4,並刪除分割槽3上對應資料。
這種操作非常非常消耗IO,並且在分割的過程中鎖定分割槽三內的內容,造成分割槽三的內容不可用。不僅僅如此,這個操作生成的日誌內容會是被轉移資料的4倍!
所以我們如果不想因為這種操作給客戶帶來麻煩而被老闆爆菊的話…最好還是把分割點建立在未來(也就是預先建立分割點),比如2012-01-01。則分割槽3內的內容不受任何影響。在以後2012的資料加入時,自動插入到分割槽4.
分割現有的分割槽需要兩個步驟:
1.首先告訴SQL SERVER新建立的分割槽放到哪個檔案組
2.建立新的分割點
可以通過如下語句來完成:
如果我們的分割構架在定義的時候已經指定了NEXT USED,則直接新增分割點即可。
通過文中前面檢視分割槽的長語句..再來看:
新的分割槽已經加入!
分割槽的合併
分割槽的合併可以看作分割槽分割的逆操作。分割槽的合併需要提供分割點,這個分割點必須在現有的分割表中已經存在,否則進行合併就會報錯
假設我們需要根據2009-01-01來合併分割槽,概念如下:
只需要使用merge引數:
再來看分割槽資訊:
這裡值得注意的是,假設分割槽3和分割槽4不再一個檔案組,則合併後應該存在哪個檔案組呢?換句話說,是由分割槽3合併到分割槽4還是由分割槽4合併到分割槽3?這個需要看我們的分割槽函式定義的是left還是right.如果定義的是left.則由左邊的分割槽3合併到右邊的分割槽4.反之,則由分割槽4合併到分割槽3:
總結
本文從講解了SQL SERVER中分割槽表的使用方式。分割槽表是一個非常強大的功能。使用分割槽表相對傳統的分割槽檢視來說,對於減少DBA的管理工作來說,會更勝一籌!
--建立分割槽函式create partition function fnPartition(Date)as range right
for values('2004-01-01','2007-01-01')
--查詢分割槽函式select * from sys.partition_functions
--基於之前分割槽函式建立分割槽架構create partition scheme schemaforpartition
as partition fnpartition
to(filegroup1,[primary],filegroup1)
--檢視分割槽架構select * from sys.partition_schemes
create table PartitionedTable
(
id int,
orderid int,
salesdate date
)
on SchemaForPartition(SalesDate)
--declare @i int=0;
--while(@i<50000)
-- begin
-- set @[email protected]+1;
-- insert into PartitionedTable(orderid,salesdate) values(@i,CAST(CHECKSUM(newID())%40357 AS DATETIME)+RAND())
-- end
select convert(varchar(50), ps.name) as partition_scheme,
p.partition_number,
convert(varchar(10), ds2.name) as filegroup,
convert(varchar(19), isnull(v.value, ''), 120) as range_boundary,
str(p.rows, 9) as rows
from sys.indexes i
join sys.partition_schemes ps on i.data_space_id = ps.data_space_id
join sys.destination_data_spaces dds
on ps.data_space_id = dds.partition_scheme_id
join sys.data_spaces ds2 on dds.data_space_id = ds2.data_space_id
join sys.partitions p on dds.destination_id = p.partition_number
and p.object_id = i.object_id and p.index_id = i.index_id
join sys.partition_functions pf on ps.function_id = pf.function_id
LEFT JOIN sys.Partition_Range_values v on pf.function_id = v.function_id
and v.boundary_id = p.partition_number - pf.boundary_value_on_right
WHERE i.object_id = object_id('PartitionedTable')
and i.index_id in (0, 1)
order by p.partition_number
select count(1) from dbo.PartitionedTable where salesdate<'2004-1-1'
select count(1) from dbo.PartitionedTable where salesdate>='2004-1-1' and salesdate<'2007-1-1'
select count(1) from dbo.PartitionedTable where salesdate>='2007-1-1' and salesdate<'2009-1-1'
select count(1) from dbo.PartitionedTable where salesdate>='2009-1-1'
--首先需要知道分割後多出來的分割槽應該儲存在哪個檔案組alter partition scheme schemaforpartition next used 'filegroup1'
--在新增分割點alter partition function fnpartition()
split range('2009-01-01')
--提供分割點,合併分割槽alter partition function fnPartition()
merge range('2009-01-01')