玩转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 

玩转SQL Server复制回路の变更数据类型、未分区表转为分区表的更多相关文章

  1. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  2. SQL Server 复制系列(文章索引)

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 前言(Introduction) 复制逻辑结构图(Construction) 系列文章索引(Catalog) 总结&am ...

  3. SQL Server复制入门(一)----复制简介【转】

    SQL Server复制入门(一)----复制简介 简介 SQL Server中的复制(Replication)是SQL Server高可用性的核心功能之一,在我看来,复制指的并不仅仅是一项技术,而是 ...

  4. Dynamics AX 2012 性能优化之 SQL Server 复制

    Dynamics AX 2012 性能优化之 SQL Server 复制 分析数据滞后 在博文 Dynamics AX 2012 在BI分析中建立数据仓库的必要性 里,Reinhard 阐述了在 AX ...

  5. 【查阅】教你使用SQL SERVER复制

    关键词:复制,复制总结,复制汇总,复制查阅 1.概念与搭建 Step1:SQL SERVER复制介绍 Step2:SQL Server 复制事务发布 Step3:SQL Server 通过备份文件初始 ...

  6. SQL Server 无法连接到服务器。SQL Server 复制需要有实际的服务器名称才能连接到服务器。请指定实际的服务器名称。

    异常处理汇总-数据库系列  http://www.cnblogs.com/dunitian/p/4522990.html SQL性能优化汇总篇:http://www.cnblogs.com/dunit ...

  7. SQL Server复制出错文章集锦

    SQL Server复制出错文章集锦 为了方便大家对数据库复制过程中出错的时候更好地解决问题 本人收集了SQL Server相关复制出错解决的文章   The process could not ex ...

  8. SQL Server 复制订阅

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/高性能解决方案/高可用 概述 配置复制就没有数据库镜像和AlwaysOn的要求那么高,只需要两台服务器能通过TCP进行通讯即可,两 ...

  9. SQL Server 复制:事务发布

    一.背景 在复制的运用场景中,事务发布是使用最为广泛的,我遇到这样一个场景:在Task数据库中有Basic与Group两个表,需要提供这两个表的部分字段给其它程序读取放入缓存,程序需要比较及时的获取到 ...

随机推荐

  1. locate 最快的查找文件的命令 NB

    我见过最NB的查找文件最快的命令 [root@NB data]# locate teamviewer. /data/Software/teamviewer.i686.rpm /home/ok/.loc ...

  2. Linux系统目录结构以及简单说明

    Linux系统目录结构以及简单说明 linux目录图: / root --- 启动Linux时使用的一些核心文件.如操作系统内核.引导程序Grub等. home --- 存储普通用户的个人文件 ftp ...

  3. JQuery的无缝滚动

    图片无缝向左滚动的代码如下:   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "h ...

  4. [转]在Eclipse中使用JUnit4进行单元测试(初级篇)

    首先,我们来一个傻瓜式速成教程,不要问为什么,Follow Me,先来体验一下单元测试的快感! 首先新建一个项目叫JUnit_Test,我们编写一个Calculator类,这是一个能够简单实现加减乘除 ...

  5. 在公司里面,如何让笔记本连上wifi?

    1.复制谷歌浏览器图标的快捷方式,重命名为chrome Android,鼠标右键设置该快捷方式的属性,在目标处,加上 C:\Users\admin\AppData\Local\Google\Chrom ...

  6. GruntJS常用前端流程自动化配置-【持续优化】

    前言 近期用Grunt进行前端开发经常要用到的一些Grunt插件,用起来非常顺手. 一.package.json文件配置如下: 包括coffeescript编译为js,css加CSS3适配前缀,css ...

  7. MacOS下Express安装过程中遇到的问题

    问题描述: 使用nmp install express -g命令全局安装express后,在终端使用express -V命令可以获取到express的版本号,但在引用express的项目运行时,会报缺 ...

  8. linux菜鸟日记(5)

    iptables详细语法及配置: SNAT:源地址转换DNAT:目标地址转换PNAT:端口地址转换 ----------------------------------iptables规则链 路由以后 ...

  9. 总结js的一些复制方法

    1.复制对象: var item1={XXX}; var item2=$.extend(true,{},item1);//深度克隆对象(jQuery方法). lodash也有相关方法:https:// ...

  10. 网页制作中在头部固定悬浮table表头(thead)的方法

    这两天接了一个需求,页面是这样的 然后需求是页面中的这个表格当页面向上滚动,且表格的表头到达窗口上方时,表头悬浮在页面的上方,表格正常滚动,这样表格内的数据可以随时看到表头内容. 一开始我认为这是极简 ...