1. 程式人生 > 實用技巧 >【SqlServer】關於SqlServer中的表分割槽,看這一篇文章就夠了。

【SqlServer】關於SqlServer中的表分割槽,看這一篇文章就夠了。

目錄結構:

contents structure
  1. 什麼是分割槽
  2. 準備測試資料
  3. 如何進行水平分割槽
    1. 建立檔案組
    2. 建立分割槽函式
    3. 建立分割槽方案
    4. 建立分割槽表
    5. 使用分割槽嚮導建立分割槽表
    6. 秀一秀肌肉
  4. 關於表分割槽的常用管理
    1. 拆分分割槽
    2. 合併分割槽
    3. 檢視指定資料所在的分割槽

1. 什麼是分割槽

在sqlserver中,一般情況下所有的資料都是儲存到一個檔案上的(預設為.mdf檔案),這樣在資料非常多的時候效率肯定比較低。 而如果採用分割槽,資料就會按照我們指定的分割槽規則,儲存到不同的檔案,這樣一來,一個非常的大檔案就被分成了多個小檔案,這樣一來查詢效率也會大大提升。

如果我們不做任何分割槽,也就是說,所有的資料都是儲存在主資料檔案

(.mdf)中的。 如果進行了分割槽,那麼我們就可以指定次要資料檔案(.ndf)的數量,來分攤主資料檔案的壓力。除此之外,還有一個日誌資料檔案,也就是(.ldf)檔案。

分割槽分為兩種,一種是水平分割槽,另一種是垂直分割槽。

水平分割槽:對錶的行進行分割槽。每個物理區域儲存一定量的行資料,它們組合起來就是完整的表資料。進行水平分割槽,一定要指定某個屬性列進行資料分割。比如:一年的訂單表可以按照時間分四個區(這裡就是按照時間進行資料分割的。)

垂直分割槽:對錶的列進行分割槽。通過對錶的垂直劃分來減少目標表的寬度,使某些特定的列被劃分到特定的分割槽,每個分割槽都包含了其中的列所對應的行。

接下筆者和你一起揭開水平分割槽的神祕面紗,垂直分割槽會在以後單獨寫一篇文章。

2. 準備測試資料

在正式開始之前,我們需要先建立一些資料。筆者建立了一個數據庫(mydb),和一張訂單表(order),並且往訂單表中插入了1千萬條測試資料。

create database mydb;
GO
use mydb
GO
create table order_detail
(
order_id        bigint           not null    primary key    nonclustered     identity(1,1),
customer_id     bigint           not null,
goods_price     decimal
(10,2) not null, create_time datetime not null, ); GO create clustered index create_time_clustered_index on order_detail(create_time) GO execute sp_addextendedproperty 'MS_Description', '訂單編號', 'user', 'dbo', 'table', 'order_detail', 'column', 'order_id'; execute sp_addextendedproperty 'MS_Description', '使用者id', 'user', 'dbo', 'table', 'order_detail', 'column', 'customer_id'; execute sp_addextendedproperty 'MS_Description', '商品數量', 'user', 'dbo', 'table', 'order_detail', 'column', 'goods_price'; execute sp_addextendedproperty 'MS_Description', '建立時間', 'user', 'dbo', 'table', 'order_detail', 'column', 'create_time'; GO --插入1千萬條資料,大概需要15分鐘 declare @price_min Int=1 --測試資料最低價格 declare @price_max Int=1000000 --測試資料最高價格 declare @decimal Int=2 --價格保留小數點 declare @i int set @i = 1 while @i < 10000000 begin insert into order_detail(customer_id,goods_price,create_time) values( ABS(CHECKSUM(NEWID())) ,@price_min+round((@price_max-@price_min)*rand(),@decimal) --價格 ,GETDATE()-(0.01*@i)/15); --插入時間,時間長度大概是19年 set @i = @i + 1; end

3. 如何進行水平分割槽

在SQL Server中進行水平分割槽的過程不是一個簡單的SQL命令就可以搞定的,它涉及到資料檔案,檔案組,分割槽函式,和分割槽方案。 下面筆者將會把這個過程劃分成一個個的小步驟,並且為每個步驟都做了較為詳細的解釋。

3.1 建立檔案組

這個步驟的作用就是指定資料分割槽後要儲存的檔案。這裡有兩個概念,一個是資料檔案,另一個是檔案組,一個檔案組可以管理多個數據檔案,我們在建立分割槽方案的時候就需要指定這些檔案組。在建立分割槽完成後,分割槽表中的資料會按照我們指定的規則分散地儲存到各個資料檔案中。

既可以在建立資料庫的時候建立檔案組,也可以在資料庫建立完成後再建立檔案組。

在建立完成資料庫後,可以看到檔案組可以被分為PRIMARY和你自定義的檔案組兩種。主資料檔案(mydb)是屬於PRIMARY檔案組,並且不能被改變。myfile1,2,3,4,5是屬於次要資料檔案,次要資料檔案的歸組就比較隨意,既可以是PRIMARY,也可以是你自定義的檔案組。還有一個日誌資料檔案(mydb_log)不屬於任何檔案組。

建立完成後,開啟你的資料儲存目錄,可以看到SQL Server為你建立瞭如下檔案。

關於檔案和檔案組的更多資訊,可以移步到 Microsoft Database Files and Filegroups

上面展示了在建立資料庫時候建立資料檔案和檔案組。其實也可以資料庫建立完成後,再建立資料檔案和檔案組。

建立檔案組

右鍵資料庫 -> 屬性(Properties) -> 檔案組(Filegroups)

建立資料檔案

右鍵資料庫 -> 屬性(Properties) -> 檔案組(Files)

通過T-SQL建立資料檔案和檔案組

你也可以通過T-SQL指令碼來建立資料檔案和檔案組

--建立資料庫檔案組
--alter database 資料庫名稱 add filegroup 檔案組名稱
alter database mydb add filegroup myfilegroup1
alter database mydb add filegroup myfilegroup2
alter database mydb add filegroup myfilegroup3
alter database mydb add filegroup myfilegroup4
alter database mydb add filegroup myfilegroup5

--建立資料檔案
--alter database 資料庫名稱 add file 
--(name=N'檔名稱',filename=N'檔案路徑',size=檔案初始,filegrowth=檔案自動增量)
--to filegroup 檔案組名稱
alter database mydb add file 
(name=N'myfile1',filename=N'D:\softwares\sqlserver\root\MSSQL14.MSSQLSERVER\MSSQL\DATA\myfile1.ndf',size=10Mb,filegrowth=5mb)
to filegroup myfilegroup1

alter database mydb add file 
(name=N'myfile2',filename=N'D:\softwares\sqlserver\root\MSSQL14.MSSQLSERVER\MSSQL\DATA\myfile2.ndf',size=10Mb,filegrowth=5mb)
to filegroup myfilegroup2

alter database mydb add file 
(name=N'myfile3',filename=N'D:\softwares\sqlserver\root\MSSQL14.MSSQLSERVER\MSSQL\DATA\myfile3.ndf',size=10Mb,filegrowth=5mb)
to filegroup myfilegroup3

alter database mydb add file 
(name=N'myfile4',filename=N'D:\softwares\sqlserver\root\MSSQL14.MSSQLSERVER\MSSQL\DATA\myfile4.ndf',size=10Mb,filegrowth=5mb)
to filegroup myfilegroup4

alter database mydb add file 
(name=N'myfile5',filename=N'D:\softwares\sqlserver\root\MSSQL14.MSSQLSERVER\MSSQL\DATA\myfile5.ndf',size=10Mb,filegrowth=5mb)
to filegroup myfilegroup5

3.2 建立分割槽函式

上面已經建立了檔案組,接下來就是建立分割槽函式,分割槽函式是資料庫中的一個獨立物件,它將表的行對映到一組分割槽,所以分割槽函式解決的是HOW的問題,即表如何分割槽的問題。

create partition function 分割槽函式名(<分割槽列型別>) as range [left/right] 
for values (每個分割槽的邊界值,....) 

上面的left代表左邊界,right代表右邊界。當 資料庫引擎 按升序從左到右排序時,邊界值是屬於左側還是右側(預設為左側)。換句話說,就是一個為小於等於,另一個為小於。

create partition function myPartitionFun(datetime) as range right 
for values ('2010-01-01 00:00:00','2017-01-01 00:00:00','2019-01-01 00:00:00','2020-01-01 00:00:00')

筆者資料庫中所有資料的時間範圍是2002到2020年。上面的時間間隔是呈現錐形分佈的,之所以這麼設計,這是因為訂單表對當年發生的訂單的操作是最頻繁的,其次是1年前的訂單(頻繁),再就是2-3年的訂單(操作比較頻繁),再則就是3-10年的訂單(操作偶爾發生),最後是10年前的訂單(幾乎不再操作訂單),因此這個時間間隔是越來越大的。當然如果有對所有資料都有較為頻繁的操作,可以分18個區(2002年到2020年),給每年都分割槽。

3.3 建立分割槽方案

分割槽方案定義了一個特定的分割槽函式將使用的物理儲存結構(就是檔案組),或者說是分割槽方案將分割槽函式生成的分割槽對映到我們定義的一組檔案組。因此建立一個分割槽方案,需要分割槽函式和檔案組名稱。

create partition scheme <分割槽方案名稱> as partition <分割槽函式名稱> [all]to (檔案組名稱,....) 

我們已經知道分割槽函式解決的就是HOW的問題, 而這個分割槽方案就是WHERE的問題,它把分割槽函式生成的分割槽對映對映到指定的一組檔案組中。

create partition scheme myPartitionSchema as partition myPartitionFun to (myfilegroup1,myfilegroup2,myfilegroup3,myfilegroup4,myfilegroup5);

分割槽函式生成的分割槽數不能大於分割槽方案中指定的檔案組數量。如果生成的分割槽數小於檔案組的數量,那麼多出的檔案組,會被標記為下次使用的檔案組。myPartitionFun指定了4臨界值因此會生成5個分割槽,myPartitionSchema恰好指定了5個檔案組一一對應5個分割槽。

建立好分割槽方案後,可以在資料庫 -> 儲存(Storage) -> 分割槽方案(Partition Schemas)中檢視

3.4 建立分割槽表

我們已經建立了分割槽方案了,接下來就是把分割槽方案應用到資料表上,這就是建立分割槽表

create table <表名> (
  <列定義>
)on<分割槽方案名>(分割槽列名)

例如:

create table MyOrder 
(
id
bigint not null identity(1,1), order_num nvarchar(32) not null, order_status int not null, createtime datetime not null, updatetime datetime not null, order_desc nvarchar(500) null
) on myPartitionSchema(id);

上面是建立了一個新表,並且指定了分割槽方案。由於在2.準備測試資料中已經建立了資料表,因此這裡我們再不需要新建表,只需要將原來的錶轉化為分割槽表就可以了。

將普通錶轉化為分割槽表

分割槽表需要按照某一個欄位把資料通過分割槽方案分到不同的檔案中,而這個作為分割槽條件的欄位必需要有聚集索引才可以。之前建立的表order_detail的聚集索引是在create_time上的,這裡我們並不需要任何的修改。值得一提的是,分割槽方案實際上是和聚集索引關聯的,而且如果你想要建立一個帶分割槽方案的聚集索引(也就是給表分割槽),那麼只有先刪除之前的聚集索引,然後再建立一個帶分割槽的聚集索引。這種帶分割槽的聚集索引,也叫做分割槽索引

--刪除以前的聚集索引
DROP INDEX [create_time_clustered_index] ON [dbo].[order_detail] WITH ( ONLINE = OFF )

GO

--建立分割槽索引
CREATE CLUSTERED INDEX [create_time_clustered_index] ON [dbo].[order_detail] 
(
    create_time
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [myPartitionSchema]([create_time])

也可以通過嚮導把普通錶轉化為分割槽表

右鍵聚集索引 -> 屬性(Properties) -> 儲存(Storage)

嚮導建立分割槽索引的過程也是顯示吧舊聚集索引刪除,然後再建立一個新的聚集索引並且指定分割槽方案。

3.5 使用分割槽嚮導建立分割槽表

上面介紹的幾乎都是通過程式碼實現的,Microsoft SQL Server Management Studio 提供了更方便的圖形化方式。

右鍵要分割槽的表 -> 儲存(Storage)-> 建立分割槽(Create Partition)-> 下一步

選擇要分割槽的列

選擇或建立分割槽函式

選擇或建立分割槽方案

指定臨界值,以及左邊界或右邊界。

3.6 秀一秀肌肉

到目前為止,分割槽表已經建立完成了,接下來就是秀一秀它的效能了。我準備了一張表order_detail_non_partition,資料和索引都和order_detail表一樣,只是order_detail_non_partition只是沒有分割槽。

--分割槽索引
select * from order_detail where create_time >= '2012-05-01 00:00:00' and create_time < '2019-06-01 00:00:00';
--無分割槽,聚集索引
select * from order_detail_non_partition where create_time >= '2012-05-01 00:00:00' and create_time < '2019-06-01 00:00:00';

筆者的打開了SQL SERVER Management Studio中的實時資料查詢功能。

上面查詢了2010年7月份到2020年9月份的所有資料,雖然查詢所花費的時間都差不多,但是分割槽表在效能方面還是明顯優於非分割槽表(I/O消耗,CPU消耗, 子樹的大小,運算子的開銷....)。

4. 關於表分割槽的常用管理

4.1 拆分分割槽

在分割槽函式中新增一個邊界值。

--分割槽拆分
alter partition function myPartitionFun()
split range(N'2005-01-01T00:00:00.000')

如果分割槽函式已經關聯了分割槽方案,那麼分割槽數 不能大於分割槽方案中的檔案組數。如果你的分割槽數和分割槽方案中的檔案組數不符合要求,你可以先擴充套件分割槽方案中的檔案組數,再擴充套件分割槽函式的臨界值。

擴充套件分割槽方案中的檔案組數

--新建立一個數據檔案
alter database mydb add filegroup myfilegroup6
GO
--新建一個檔案組
alter database mydb add file 
(name=N'myfile6',filename=N'D:\softwares\sqlserver\root\MSSQL14.MSSQLSERVER\MSSQL\DATA\myfile6.ndf',size=10Mb,filegrowth=5mb)
to filegroup myfilegroup6

GO
--新增檔案組到分割槽方案
ALTER PARTITION SCHEME myPartitionSchema  
NEXT USED myfilegroup6;
GO

4.2 合併分割槽

合併分割槽和拆分分割槽恰好相反,就是把兩個分割槽合併為一個分割槽,可以通過刪除分割槽函式中的臨界值來完成。

--合併分割槽
alter partition function myPartitionFun()
merge range(N'2005-01-01T00:00:00.000')

4.3 檢視指定資料所在的分割槽

當進行了表分割槽後,資料就會分散儲存到不同的分割槽中。可以通過如下的命令,來查詢資料到底存到那個分割槽中的:

--查詢分割槽依據列為2020-08-28 14:34:02.890的資料在哪個分割槽上
select $partition.myPartitionFun(N'2020-08-28 14:34:02.890')  --返回值是5,表示此值存在第5個分割槽 

也可查詢所有非空分割槽中存在的資料行數

--檢視分割槽表中,每個非空分割槽存在的行數
select $partition.myPartitionFun(create_time) as partitionNum,count(*) as recordCount
from order_detail
group by  $partition.myPartitionFun(create_time)

或是查詢某個分割槽中的所有資料

---檢視指定分割槽中的資料記錄
select * from order_detail where $partition.myPartitionFun(create_time)=5