背景:
大多數項目開發中都會有幾個日志表用於記錄用戶操作或者數據變更的信息,往往這些表數據數據量比較龐大,每次對這些表數據進行操作都比較費時,這個時候就考慮用表分區對表進行切分到不同物理磁盤進行存儲,從而提高運行效率。
表分區優點:
1.性能提升:最大的好處應該是把表數據分割到不同的磁盤存儲,充分利用多cpu對數據文件同步處理帶來的數據操作效率的提升
2.數據管理:分區表進行數據備份的時候可以單獨備份需要的指定分區文件進行備份,不需要對整個表數據進行備份
3.可用性:一個分區文件遭到破壞不會影響其他文件的正常使用
實戰:
項目中有一個日志表因為每日記錄數據量太大(3個月數據2000W)需要只保留最近三個月的數據,這樣就要求每月初把3個月前的數據給刪掉,同時這個表要進行分頁查詢和數據匯總,這樣就考慮到將這張表進行分區操作,操作數據庫是SQL Server2012(只有專業版才支持分區)
第一步:創建文件組和分組文件
alter database Test add filegroup LoginLog1 alter database Test add filegroup LoginLog2 alter database Test add filegroup LoginLog3 alter database Test add filegroup LoginLog4
Test是用來測試的數據庫名稱,我們先創建4個文件組
接下來創建分組文件
alter database Test add file (Name=N'LoginLog1',filename='G:\練習\表分區測試\group\LoginLog1.ndf',size=10mb,maxsize=200Mb,filegrowth=5mb) to filegroup LoginLog1 alter database Test add file (Name=N'LoginLog2',filename='G:\練習\表分區測試\group\LoginLog2.ndf',size=10mb,maxsize=200Mb,filegrowth=5mb) to filegroup LoginLog2 alter database Test add file (Name=N'LoginLog3',filename='G:\練習\表分區測試\group\LoginLog3.ndf',size=10mb,maxsize=200Mb,filegrowth=5mb) to filegroup LoginLog3 alter database Test add file (Name=N'LoginLog4',filename='G:\練習\表分區測試\group\LoginLog4.ndf',size=10mb,maxsize=200Mb,filegrowth=5mb) to filegroup LoginLog4
第二步 創建分區函數
我們當前用時間作為分區字段,以便於日志表根據添加時間做分區
create partition function Login_Log_CreateTime (datetime) as range right for values ('2017-04-01','2017-05-01','2017-06-01')
這里我們用三個日期把整個時間軸划分為4塊:2017-04-01以前的數據、2017-04-01至2017-04-30的數據、2017-05-01至2017-05-31的數據、2017-06-01至2017-06-30的數據
注意range right的left和right的作用是決定臨界點值得歸屬,一開始我這里用的是left導致分區划分為4、5、6和6月份以后的數據,這樣導致我在下次添加新的分區的時候沒辦法添加2017-07-01的分割點,只能添加>=2017-08-01的時間點。所以這里根據情況自己定義好是Left還是right
第三步 創建分區方案
create partition scheme SchemeLogin_Log_CreateTime as partition Login_Log_CreateTime to (LoginLog1,LoginLog2,LoginLog3,LoginLog4)
這里注意如果分區函數是三個分割點,這里就要對應3+1個文件組
到這里我們的分區方案算是已經搞好了,下面就開始創建測試表並給其指定分區方案
創建測試表,指定添加時間作為分區字段
create Table Login_Log ( ID int, Name nvarchar(50), CreateTime datetime ) on SchemeLogin_Log_CreateTime(createtime)
這里有一點不太明白:如果在表中創建主鍵或唯一索引,則分區依據列必須為該列,所以我們把分區列建在createtime上面意味着建表的時候不能存在主鍵或者唯一索引。
如果我用代碼添加主鍵報錯:
alter table Login_Log add constraint PK_Login_Log primary key(ID)

但是我如果對表右擊-->設計-->設置主鍵 這樣就可以
然后我們插入測試數據
insert Login_Log (id,Name,CreateTime) values (1,'aa','2017-03-12') insert Login_Log (id,Name,CreateTime) values (2,'bb','2017-04-12') insert Login_Log (id,Name,CreateTime) values (3,'bb','2017-04-13') insert Login_Log (id,Name,CreateTime) values (4,'cc','2017-05-1') insert Login_Log (id,Name,CreateTime) values (5,'cc','2017-05-2') insert Login_Log (id,Name,CreateTime) values (6,'cc','2017-05-14') insert Login_Log (id,Name,CreateTime) values (7,'dd','2017-06-12') insert Login_Log (id,Name,CreateTime) values (8,'dd','2017-06-13') insert Login_Log (id,Name,CreateTime) values (9,'dd','2017-06-14') insert Login_Log (id,Name,CreateTime) values (10,'dd','2017-06-15')
我們現在來看下數據插入情況,下面是查詢代碼
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('Login_Log') --此處是表名 and i.index_id in (0, 1) order by p.partition_number
查詢結果如下

這樣我們可以看出正如我們分區所設的那樣在4月份以前的數據是1條,存儲在了LoginLog1的文件組中,其他數據也按CreateTime時間點存到了對應的文件組,證明我們的分區已經實現了
這里呢目前還沒有涉及到索引,后面再慢慢補充吧
到這里應該算是我們的測試已經成功了,但是我們還有一個功能點就是定時刪除三個月前的舊數據,我們先來梳理下代碼,正確的實現是要寫存儲過程然后用作業實現的,這個后面在完善
假設當前日期到了2017-6-30 23:59:59,這時候我們就要清理3月份的數據啦,並且新建一個7月份的分區點,關鍵步驟如下:
1.因為3月份數據都存儲在第一個分區里面,所以我們只需要把這個分區的數據給清理掉就行了,也不用根據時間點去表里進行過濾刪除。
我們先建立一個跟Login_Log表一樣的臨時表(同樣需要進行表分區)
create Table Login_Log_Temp ( ID int, Name nvarchar(50), CreateTime datetime ) on SchemeLogin_Log_CreateTime(createtime)
然后我們將第一分區的數據拷貝到臨時表並且刪除臨時表
--講需要刪除的分區數據插入臨時表 alter table Login_Log switch partition 1 to Login_Log_Temp PARTITION 1 --刪除臨時表 drop table Login_Log_Temp
這是我們再來查詢下數據,LoginLog1的數據已經沒有了

因為刪除了3月份的數據,這樣我們就要將分區1和分區2進行分區合並,好騰出來分組給新的分區使用
--合並分區 ALTER PARTITION FUNCTION [Login_Log_CreateTime]() MERGE RANGE ('2017-04-01')

這里可以看出分區1和分區2的數據已經合並到LoginLog1分組里面了,這樣LoginLog2分組已經空出來了
接下來建立新的分區
--修改分區方案,新的分區使用LoginLog2分組文件 ALTER PARTITION SCHEME SchemeLogin_Log_CreateTime NEXT USED [LoginLog2] --添加新的分區 ALTER PARTITION FUNCTION [Login_Log_CreateTime]() SPLIT RANGE('2017-07-01')

我們再來插入一條7月份的數據
insert Login_Log (id,Name,CreateTime) values (11,'ee','2017-07-01')

OK,我們的新分區創建完成啦
另外刪除的時候的注意點:
如果想要刪除分組呢就要先刪除分組中的數據文件,想要刪除分區函數就要先刪除分區方案,要刪除分區方案就要先刪除應用的數據表
這里列出來本次例子用到的刪除代碼
--刪除數據 drop table Login_Log_Temp drop table Login_Log drop partition scheme SchemeLogin_Log_CreateTime drop partition function Login_Log_CreateTime dbcc shrinkfile(LoginLog1,emptyfile) ALTER DATABASE test REMOVE FILE LoginLog1 ALTER DATABASE test REMOVE FILEGROUP LoginLog1 dbcc shrinkfile(LoginLog2,emptyfile) ALTER DATABASE test REMOVE FILE LoginLog2 ALTER DATABASE test REMOVE FILEGROUP LoginLog2 dbcc shrinkfile(LoginLog3,emptyfile) ALTER DATABASE test REMOVE FILE LoginLog3 ALTER DATABASE test REMOVE FILEGROUP LoginLog3 dbcc shrinkfile(LoginLog4,emptyfile) ALTER DATABASE test REMOVE FILE LoginLog4 ALTER DATABASE test REMOVE FILEGROUP LoginLog4
備注:這只是本人用來學習的一個例子,此次記錄僅作為一個參考點,有很多不完善的地方需要繼續研究
