玩轉SQL Server復制回路の變更數據類型、未分區表轉為分區表
復制的應用:
初級應用:讀寫分離、數據庫備份
高級應用:搬遷大型數據庫(跨機房)、變更數據類型、未分區表轉為分區表
京東的復制專家 菠蘿 曾經寫過文章、在數據庫大會上也做過演講,但是我相信真正按照菠蘿兄的文章自己去做一次實驗的人應該不多
京東的復制專家 菠蘿 的文章地址:Replication的犄角旮旯(一)--變更訂閱端表名的應用場景
為什麼要玩轉復制,大家想象一下:變更數據類型、未分區表轉為分區表 這些業務場景經常都會發生,特別在數據量特別大的公司
變更數據類型:沒有其他特別好的辦法,數據量大,鎖表時間會比較長
未分區表轉為分區表:有時候一張表的數據量已經很多了,比如體積已經達到100G,那么這時候需要做表分區,方法是重建聚集索引或者導數據
上面的方法不多不少都有一些缺陷,對于數據量特別大的情況下,如果超出業務的預期停機時間……菊花殘,滿地傷,被領導認為辦事不力
常見場景:
1、變更其中的自增列主鍵,int-》bigint ,將表改為表分區
2、100G+的大表
3、單次最長停機時間:為1小時
復制回路,一次搞定
下面介紹一下,如何在一個實例下,通過三個數據庫,建立一個復制回路,完成上面的需求
實驗環境:一臺電腦,一個SQL Server實例,SQL Server2012, Windows7
結構圖
從上圖可以看出,由于都是在同一個實例,同一臺機器下,所以機器磁盤需要有足夠的磁盤空間!!
因為[testloopbackA]庫有一個[testAltertype]表100G,復制到[testloopbackB]庫[testAltertype]表100G
復制到[testloopbackC]庫[testAltertype]表100G,最后復制回去[testloopbackA]庫[testAltertype]表100G
加上生成的快照文件,當然快照文件可能會壓縮,但是一定要保證有足夠的磁盤空間
下面是具體演示
1、建庫腳本

USE [master] GO /****** Object: Database [testloopbackA] Script Date: 2015/6/3 8:21:01 ******/ CREATE DATABASE [testloopbackA] CONTAINMENT = NONE ON PRIMARY ( NAME = N'testloopbackA', FILENAME = N'D:\DataBase\testloopbackA.mdf' , SIZE = 30720KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ), FILEGROUP [FG_testChangepartition_Id_01] ( NAME = N'FG_testChangepartition_Id_01_data', FILENAME = N'D:\DataBase\testloopbackA\FG_testChangepartition_Id_01_data.ndf' , SIZE = 24576KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1048576KB ), FILEGROUP [FG_testChangepartition_Id_02] ( NAME = N'FG_testChangepartition_Id_02_data', FILENAME = N'D:\DataBase\testloopbackA\FG_testChangepartition_Id_02_data.ndf' , SIZE = 24576KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1048576KB ) LOG ON ( NAME = N'testloopbackA_log', FILENAME = N'D:\DataBase\testloopbackA_log.ldf' , SIZE = 2432KB , MAXSIZE = 2048GB , FILEGROWTH = 10%) GO USE [master] GO /****** Object: Database [testloopbackB] Script Date: 2015/6/3 8:22:11 ******/ CREATE DATABASE [testloopbackB] CONTAINMENT = NONE ON PRIMARY ( NAME = N'testloopbackB', FILENAME = N'D:\DataBase\testloopbackB.mdf' , SIZE = 30720KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ), FILEGROUP [FG_testChangepartition_Id_01] ( NAME = N'FG_testChangepartition_Id_01_data', FILENAME = N'D:\DataBase\testloopbackB\FG_testChangepartition_Id_01_data.ndf' , SIZE = 24576KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1048576KB ), FILEGROUP [FG_testChangepartition_Id_02] ( NAME = N'FG_testChangepartition_Id_02_data', FILENAME = N'D:\DataBase\testloopbackB\FG_testChangepartition_Id_02_data.ndf' , SIZE = 24576KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1048576KB ) LOG ON ( NAME = N'testloopbackB_log', FILENAME = N'D:\DataBase\testloopbackB_log.ldf' , SIZE = 2432KB , MAXSIZE = 2048GB , FILEGROWTH = 10%) GO USE [master] GO /****** Object: Database [testloopbackC] Script Date: 2015/6/3 8:22:14 ******/ CREATE DATABASE [testloopbackC] CONTAINMENT = NONE ON PRIMARY ( NAME = N'testloopbackC', FILENAME = N'D:\DataBase\testloopbackC.mdf' , SIZE = 30720KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ), FILEGROUP [FG_testChangepartition_Id_01] ( NAME = N'FG_testChangepartition_Id_01_data', FILENAME = N'D:\DataBase\testloopbackC\FG_testChangepartition_Id_01_data.ndf' , SIZE = 24576KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1048576KB ), FILEGROUP [FG_testChangepartition_Id_02] ( NAME = N'FG_testChangepartition_Id_02_data', FILENAME = N'D:\DataBase\testloopbackC\FG_testChangepartition_Id_02_data.ndf' , SIZE = 24576KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1048576KB ) LOG ON ( NAME = N'testloopbackC_log', FILENAME = N'D:\DataBase\testloopbackC_log.ldf' , SIZE = 2432KB , MAXSIZE = 2048GB , FILEGROWTH = 10%) GO
下面分區方案和分區函數都在三個庫上執行
--1.創建分區函數 CREATE PARTITION FUNCTION Fun_testChangepartition_Id(INT) AS RANGE LEFT FOR VALUES(2) --2.創建分區方案 CREATE PARTITION SCHEME [Sch_testChangepartition_Id] AS PARTITION [Fun_testChangepartition_Id] TO([FG_testChangepartition_Id_01],[FG_testChangepartition_Id_02])
建表腳本

USE [testloopbackA] GO --更改數據類型 CREATE TABLE [testAltertype](id INT IDENTITY(1,1) PRIMARY KEY,name NVARCHAR(100)) GO --變分區表 CREATE TABLE [testChangepartition](id INT IDENTITY(1,1) PRIMARY KEY,name NVARCHAR(100)) GO --插入測試數據 INSERT INTO [dbo].[testAltertype] ( [name] ) VALUES ( N'nihao' -- name - nvarchar(100) ) INSERT INTO [dbo].[testChangepartition] ( [name] ) VALUES ( N'nihao' -- name - nvarchar(100) ) SELECT * FROM [testAltertype] SELECT * FROM [testChangepartition]
2、在[testloopbackB]庫先建好2個表
USE [testloopbackB] GO --更改數據類型 CREATE TABLE testAltertype_new(id BIGINT IDENTITY(1,1) PRIMARY KEY,name NVARCHAR(100)) GO --變分區表 CREATE TABLE testChangepartition_new(id INT IDENTITY(1,1) PRIMARY KEY,name NVARCHAR(100)) ON [Sch_testChangepartition_Id](id) GO
3、創建[testloopbackA]庫到[testloopbackB]庫的發布,這一步很關鍵,因為在發布的時候需要修改項目屬性,在發布屬性里,還需要選擇快照為字符類型
testChangepartition_new表
testAltertype_new表
[testloopbackA]庫到[testloopbackB]庫的復制
4、建立[pub_testloopbackAtotestloopbackB]發布的訂閱
5、在[testloopbackB]庫里, 將[testAltertype_new]表和[testChangepartition_new]表里的id列里的不用于復制設置為"是"
[testAltertype_new]表
[testChangepartition_new]表
6、測試
在[testloopbackA]庫的[testAltertype]表和[testChangepartition]表各插入一些記錄
USE [testloopbackA] GO --插入測試數據 INSERT INTO [dbo].[testAltertype] ( [name] ) VALUES ( N'nihao2' -- name - nvarchar(100) ) INSERT INTO [dbo].[testChangepartition] ( [name] ) VALUES ( N'nihao2' -- name - nvarchar(100) ) SELECT * FROM [testAltertype] SELECT * FROM [testChangepartition]
在[testloopbackB]庫就能看到新插入的記錄
USE [testloopbackB] GO SELECT * FROM [dbo].[testAltertype_new] SELECT * FROM [dbo].[testChangepartition_new]
在[testloopbackB]庫里執行
USE [testloopbackB] GO --查看分區架構文件組分布 SELECT CONVERT(VARCHAR(MAX), ps.name) AS partition_scheme , p.partition_number , CONVERT(VARCHAR(MAX), ds2.name) AS filegroup , CONVERT(VARCHAR(MAX), 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('testChangepartition_new') AND i.index_id IN ( 0, 1 ) ORDER BY p.partition_number
數據已經入到相應分區
7、繼續將[testloopbackB]庫的[testAltertype_new]表和[testChangepartition_new]表復制到[testloopbackC]
這一步需要注意:[testAltertype_new]表不需要再跟[testloopbackA]庫到[testloopbackB]庫的復制那樣設置項目屬性->XX_new,只需要保持默認就行了
[testChangepartition_new]表跟剛才一樣,需要設置項目屬性->XX_new
先在[testloopbackC]庫建好 [testChangepartition_new]表,[testAltertype_new]表不需要預先建立
USE [testloopbackC] GO --變分區表 CREATE TABLE testChangepartition_new(id INT IDENTITY(1,1) PRIMARY KEY,name NVARCHAR(100)) ON [Sch_testChangepartition_Id](id) GO
8、建[testloopbackB]庫到[testloopbackC]庫的發布
8、建立訂閱[testloopbackC]庫
啟動快照初始化
然后需要對[testChangepartition_new]表設置不用于復制 為“是”
[testAltertype_new]表不需要設置
9、測試
[testloopbackA]庫插入的記錄,[testloopbackC]庫馬上能看到

USE [testloopbackC] go --查看分區架構文件組分布 SELECT CONVERT(VARCHAR(MAX), ps.name) AS partition_scheme , p.partition_number , CONVERT(VARCHAR(MAX), ds2.name) AS filegroup , CONVERT(VARCHAR(MAX), 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('testChangepartition_new') AND i.index_id IN ( 0, 1 ) ORDER BY p.partition_number --分區區間 --SELECT * FROM sys.partition_range_values
數據入到相應分區
10、跟第7步一樣,但是這一次是[testloopbackC]庫到[testloopbackA]庫
先在[testloopbackA]庫建好 [testChangepartition_new]表,[testAltertype_new]表不需要預先建立
USE [testloopbackA] GO --變分區表 CREATE TABLE testChangepartition_new(id INT IDENTITY(1,1) PRIMARY KEY,name NVARCHAR(100)) ON [Sch_testChangepartition_Id](id) GO
11、建[testloopbackC]庫到[testloopbackA]庫的發布
12、建立訂閱[testloopbackA]庫
啟動快照初始化
然后需要對[testChangepartition_new]表設置不用于復制 為“是”
[testAltertype_new]表不需要設置
13、測試
[testloopbackA]庫插入的記錄,[testloopbackA]庫馬上能看到
USE [testloopbackA] GO SELECT * FROM [dbo].[testAltertype_new] SELECT * FROM [dbo].[testChangepartition_new]

USE [testloopbackA] go --查看分區架構文件組分布 SELECT CONVERT(VARCHAR(MAX), ps.name) AS partition_scheme , p.partition_number , CONVERT(VARCHAR(MAX), ds2.name) AS filegroup , CONVERT(VARCHAR(MAX), 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('testChangepartition_new') AND i.index_id IN ( 0, 1 ) ORDER BY p.partition_number --分區區間 --SELECT * FROM sys.partition_range_values
數據進入到相應分區
接下來就是找個適當的時間,比如凌晨, 停寫, 拆復制, 改表名 打完 收工!!
總結
在搭建復制回路的過程當中,本人發現加字段是不行的,比如testloopbackA庫testAddcolumn_new表有四個字段
然后預先在testloopbackB庫建立testAddcolumn_new表,并增加一個字段,在快照初始化的時候報錯
錯誤消息: 進程無法向表“"dbo"."testAddcolumn_new"”進行大容量復制。 (源: MSSQL_REPL,錯誤號: MSSQL_REPL20037) 獲取幫助: http://help/MSSQL_REPL20037 已達到文件末尾,缺少結束符或字段數據不完整 若要獲取詳細說明初始化訂閱表時所遇到的錯誤的錯誤文件,請執行在下面顯示的 bcp 命令。有關該 bcp 實用工具及其支持的選項的詳細信息,請參閱 BOL。 (源: MSSQLServer,錯誤號: 20253) 獲取幫助: http://help/20253 bcp "testloopbackB"."dbo"."testAddcolumn_new" in "E:\DataBase\ReplData\unc\NAME-PC_TESTLOOPBACKA_PUB_TESTLOOPBACKATOTES1cf75016\20150604115556\testAddcolumn_2.bcp" -e "errorfile" -t"\n<x$3>\n" -r"\n<,@g>\n" -m10000 -SNAME-PC -T -w (源: MSSQLServer,錯誤號: 20253) 獲取幫助: http://help/20253
方法一
在這種情況下,我們可以采取跟MySQL主從一樣的策略,建立好發布訂閱之后,在訂閱上添加字段,這時候訂閱表會應用發布端發過來的DML和加字段DDL
但是,基本上影響不是很大,當添加字段DDL執行完畢之后,就可以切表了
方法二
可以考慮在停機維護窗口添加或者考慮升級到SQL Server2012 ,SQL Server2012 對加字段已經作了一些修改
對阻塞減少到最低
相關文章:Sql Server 2012新特性 Online添加非空欄位.
如有不對的地方,歡迎大家拍磚o(∩_∩)o
文章列表