詳解SQL Server 2008復制分區清理數據
51CTO數據庫頻道向您推薦《SQL Server 2008深度應用》和《SQL Server入門到精通》兩個專題讓您更深刻的了解本文。
場景:
某種特定業務下,我們的部分業務數據可能只會保留比較短的時間,用來做臨時處理。因為考慮高可用的特性,可能會利用 SQL Server的復制組件復制這種數據到另外的 類似前端,查詢中心等數據庫服務器,創建一個冗余副本。復制組件標記事務日志,追蹤所有的Update,Insert,Delete操作。可是如果不定期清理這種表,那么在一個快速增長的環境下,表變的臃腫不堪,不僅僅浪費磁盤空間,而且給性能帶來了負面影響。
如:
A臨時數據中心 ---同步-->B 查詢/其他業務中心,副本
保證A不可用的時候,B的業務不依賴于A服務器,利用A同步過來的數據B自己直接處理。
常見的解決方案:
一、很簡單的,我們可以想到,建立一個清理Job ,根據日期標識,確定刪除的范圍不出一點錯誤,比如我每天的晚上12:00以后清理昨天的數據,那么可能會構建這樣的查詢(朋友們,也許會說使用TRUNCATE ,準時的12:00來TRUNCATE掉,不過很不幸的是,SQL Server 復制是利用日志標記的方式來同步數據,而TRUNCATE語句不會被日志標記,所以TRUNCATE語句在復制表中是被禁用的)
- DELETE FROM dbo.t WHERE complete_time >= '2010-05-04'
- AND complete_time < '2010-05-05'
來刪除5月4號的所有數據,可能上億也可能更多。
優化方案:因為考慮到存在帶量的更新 刪除 和插入操作,所以首先講數據庫設置成SIMPLE恢復模式,以最小化日志方式。
由于DELETE單個語句是一個事務性的語句,要么全部成功,要么全部失敗。那么可想如果刪除的是億級別的數據,那么日志增長,IO負荷非常的大。
所以 可能優化DELETE 操作:
- WHILE 1=1
- BEGIN
- DELETE TOP(5000) FROM dbo.t
- WHERE complete_time >= '2010-05-04' AND complete_time < '2010-05-05'
- IF @@ROWCOUNT<5000
- BREAK;
- END
將刪除操作,縮短成每5000行一批的數據來處理。
缺點:因為存在復制,那么很顯然,浪費了極多的帶寬,特別在Internet這種環境下,利用VPN的連接,尤其浪費。VPN帶寬十分的渺小。再者,在進行億級的DELETE,時間耗費也是非常恐怖的,而且DELETE過程中,由于存在Range-X 范圍排他鎖,許多開發人員寫的SQL Query沒加上WITH(NOLOCK)或者需要事務處理的查詢語句,都將被阻塞。
二、刪除復制,TRUNCATE表。我們可以直接刪除復制的發布和訂閱,然后講兩邊的表都直接TRUNCATE掉,這種刪除方式非常的高效,只不過,要確保其他新增的數據不被誤刪,也存在一定的風險,構思如下:
A數據庫 每天需要清理 前天數據,B服務器通過訂閱PUSH獲取同步數據。
因此Job 的建立有點復雜,尤其 當12點突然宕機的時候,數據沒有及時清理,或者12:00執行TRUNCATE的時候,下一天的00:00:01秒的數據被插入了,等等數據完整性問題,讓我采用這個方案回避了。
缺點:估計這個Job不怎么好寫,首先是自動刪除復制,重新創建復制,創建訂閱,雖然利用Generate Script可以到處腳本,但是整個解決方案過于復雜了。
新方案:進入主題了,在SQL Server 中分區表有一個非常 實用的語句ALTER TABLE …SWITCH,這個DDL可以快速的講同文件組的表的某個分區迅速的轉移到另外的表。(很顯然,應該是利用數據的位置偏移量的指針的轉移到新表的方法來實現的),這種方案轉移數據非常迅速,因為不存在大量的IO操作,所以一般都是秒殺級別任意大數據量(如果當前表的索引等比較多,維護操作還是需要點時間的)。但是SQL Server 2005 是不支持在復制中使用SWITCH語句的(這點很郁悶,命名都可以跟蹤DDL,為什么這個不能傳送到訂閱服務器,只要確認發布和訂閱同時存在目標表和相同的分區方案不就好了,微軟2005考慮不周啊)。
但是前不久我在考慮我們每天億級別的數據增長,清理數據方案變的十分迫切的時候,我發現SQL Server 2008支持這種操作。下面就開始整個Solution吧:
開始方案前,大家其實根據自己的業務來建立分區方案。我采用的是疊代追加 的方式來擴展以及清理分區的。
當然我也看過類似 使用根據某列鍵列 建立計算列并且講計算列PERSISTED 之后分區的。(可以緊靠業務,自己處理,我在這里展示一種,根據ID自增方案分區擴張的方案)。
腳本:
- /*
- Title:Rapid Delete In The Replication
- Author:浪客
- Environment:Windows Server 2008 Enterprise + SQL Server 2008 Enterprise
- Description:請在非生產環境下測試
- */
- USE [master]
- GO
--我們創建包含PRIMARY分區在內一共3分區的數據庫
- CREATE DATABASE [db_partition_test]
- ON PRIMARY
- (
- NAME = N'db_partition_test',
- FILENAME = N'C:\db_partition_test.mdf'
- ),
- FILEGROUP [FG1]
- (
- NAME = N'db_partition_test_fg1_1',
- FILENAME = N'C:\db_partition_test_fg1_1.ndf'
- ),
- FILEGROUP [FG2]
- (
- NAME = N'db_partition_test_fg2_1',
- FILENAME = N'C:\db_partition_test_fg2_1.ndf'
- )
- LOG ON
- (
- NAME = N'db_partition_test_log',
- FILENAME = N'C:\db_partition_test_log.ldf'
- )
- GO
- USE [db_partition_test]
- GO
--創建分區函數,分區范圍為 id<=100 | 100<id<=200 | id>200
- CREATE PARTITION FUNCTION [id_range_pf_1](int) AS RANGE LEFT FOR VALUES (100, 200)
- GO
--創建分區方案 ,分區一到PRIMARY,分區二到FG1,分區三到FG2
- CREATE PARTITION SCHEME [id_range_ps_1] AS PARTITION [id_range_pf_1] TO ([PRIMARY],FG1,FG2);
- GO
--創建分區表
- CREATE TABLE dbo.lovesql
- (
- ID INT IDENTITY(1,1) NOT FOR REPLICATION NOT NULL PRIMARY KEY,
- Col Uniqueidentifier DEFAULT(NEWID())
- ) ON id_range_ps_1(ID)
--插入測試數據 300條
- INSERT INTO dbo.lovesql DEFAULT VALUES
- GO 300
--查看表分區以后的分區行數
- SELECT $PARTITION.id_range_pf_1(ID) [PartitionNum],COUNT(1) [PartitionRowCount]
- FROM dbo.lovesql
- GROUP BY $PARTITION.id_range_pf_1(ID)
- ORDER BY [PartitionNum]
-輸入結果,每個分區100條數據,分區正確!
--PartitionNum PartitionRowCount
-------------- -----------------
--1 100
--2 100
--3 100
--現在開始建立復制,首先建立目標數據repl_db_partition_test
- USE [master];
- GO
- CREATE DATABASE [repl_db_partition_test]
- ON PRIMARY
- (
- NAME = N'repl_db_partition_test',
- FILENAME = N'C:\repl_db_partition_test.mdf'
- ),
- FILEGROUP [FG1]
- (
- NAME = N'repl_db_partition_test_fg1_1',
- FILENAME = N'C:\repl_db_partition_test_fg1_1.ndf'
- ),
- FILEGROUP [FG2]
- (
- NAME = N'repl_db_partition_test_fg2_1',
- FILENAME = N'C:\repl_db_partition_test_fg2_1.ndf'
- )
- LOG ON
- (
- NAME = N'repl_db_partition_test_log',
- FILENAME = N'C:\repl_db_partition_test_log.ldf'
- )
- GO
上面已經創建好了,基本的測試環境,那么開始建立復制。
在Object Explorer打開連接的實例中的Replication(復制)文件夾à右鍵新建發布à在向導中選擇Publication Database為db_encryption_testà選擇事務復制à在Article中選擇lovesql表->在右手邊的Article Properties設置表的發布屬性à配置如下:
下一步跳過Filter Table Rowsà在Snapshot Agent中,選中第一個復選框à在Agent Security中選擇自己設置的帳號吧,我這里選擇了Run under the SQL Server Agent services count(記得,如果你的Sql Agent對應的Windows 帳號沒有源表以及distribution表的db_owner權限,以及沒有快照文件夾的權限,會出錯的,具體的可以自己參看MSDN如何Security Settingà給Publications取一個名字,就叫lovesql吧。
接下來建立db_encryption_test 到 repl_db_encryption_test的訂閱:
在Object Explore中找到Replication/Local Publications文件夾下,找到你的發布,然后右鍵新建訂閱à一直下一步知道出現Subscribers,然后新添加一個訂閱 指向本地的repl_db_encryption_testà在安全中選擇Run under the sql server agent service count,然后一直下一步,直到成功。
如果3個代理都成功了(Snapshot代理,Log Read代理,Distribute代理),那么看看repl_db_encryption_test下的表lovesql是否存在,右鍵屬性查看 他的Storage 里面是否已經分區了
腳本執行:
- /*
- Title:Rapid Delete In The Replication
- Author:浪客
- Environment:Windows Server 2008 Enterprise + SQL Server 2008 Enterprise
- Description:請在非生產環境下測試
- */
- USE db_partition_test;
- GO
--默認發布是不會啟用 “分區切換”以及分區DDL的。所以修改發布的屬性
- EXEC sp_changepublication @publication=N'lovesql',@property=N'allow_partition_switch',@value=N'true';
--啟用SWITCH DDL 復制
- EXEC sp_changepublication @publication=N'lovesql',@property=N'replicate_partition_switch',@value=N'true';
--確保發布和訂閱兩邊的數據庫都存在一個 臨時表,而且必須是空數據的臨時表 用來快速切換分區使用
--確保分區的表,和切換分區的表使用的是同一個FILEGROUP
--
- USE db_partition_test;
- GO
- CREATE TABLE dbo.temp_lovesql_primary
- (
- ID INT PRIMARY KEY,
- Col Uniqueidentifier
- ) ON [PRIMARY]
- USE repl_db_partition_test;
- GO
--同樣的在訂閱庫創建臨時表
- CREATE TABLE dbo.temp_lovesql_primary
- (
- ID INT PRIMARY KEY,
- Col Uniqueidentifier
- ) ON [PRIMARY]
-切換分區1到臨時比哦啊
- USE db_partition_test;
- GO
- ALTER TABLE dbo.lovesql SWITCH PARTITION 1 TO dbo.temp_lovesql_primary
--查看表分區以后的分區行數
- SELECT $PARTITION.id_range_pf_1(ID) [PartitionNum],COUNT(1) [PartitionRowCount]
- FROM dbo.lovesql
- GROUP BY $PARTITION.id_range_pf_1(ID)
- ORDER BY [PartitionNum]
- --PartitionNum PartitionRowCount
- -------------- -----------------
- --2 100
- --3 100
- USE repl_db_partition_test;
- GO
--查看repl_db_partition_test 中 表分區以后的分區行數
- SELECT $PARTITION.id_range_pf_1(ID) [PartitionNum],COUNT(1) [PartitionRowCount]
- FROM dbo.lovesql
- GROUP BY $PARTITION.id_range_pf_1(ID)
- ORDER BY [PartitionNum]
--PartitionNum PartitionRowCount
-------------- -----------------
--2 100
--3 100
--恭喜,測試成功,接下來,對兩邊同時TRUNCATE TABLE 就好了
結束語:
希望大家能夠一次性測試通過。GL,GG。這里提供了一種分區的方案來刪除數據,其實非PRIMARY的文件組,也是使用的,只要在建立SCHEME的時候ALL TO PRIMARY就成了。
腳本一:/Files/bhtfg538/MSSQL/Replication/1.txt
腳本二: /Files/bhtfg538/MSSQL/Replication/2.txt
原文標題:SQL Server 2008 復制 分區SWITCH清理數據 Solution
鏈接:http://www.cnblogs.com/bhtfg538/
【編輯推薦】
- SQL Server使用索引實現數據訪問優化
- SQL Server數據庫優化經驗總結
- 如何使用SQLServer數據庫查詢累計值
- 淺析Oracle和SqlServer存儲過程的調試、出錯處理
- 幾段SQLServer語句和存儲過程
- 50種方法優化SQL Server數據庫查詢