玩转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. linux 查看目录所在的分区

    df -h /data2 Filesystem Size Used Avail Use% Mounted on /dev/xvda1 20G .5G 12G % /

  2. `这个符号在mysql中的作用

    ` 是 MySQL 的转义符,避免和 mysql 的本身的关键字冲突,只要你不在列名.表名中使用 mysql 的保留字或中文,就不需要转义. 所有的数据库都有类似的设置,不过mysql用的是`而已.通 ...

  3. vue2.0有哪些变化

    vue2.0之后有哪些变化: 1.每个组件模板template,不再支持片段代码 之前: <template> <h3>vue-router+vue-loader</h3 ...

  4. 【maven】pom.xml报错:Cannot detect Web Project version.

    新建的maven项目 报错如下: Cannot detect Web Project version. Please specify version of Web Project through &l ...

  5. unity3d关于碰撞问题

    这个是我做忍者游戏出现的问题,做个记录也为以后有人遇到也可以借鉴.因为刚接触unity,所以对其所知甚少,说错的地方请指教. 问题:角色碰撞墙为什么会先触发碰撞地面,然后再触发碰撞墙 想要的效果:是角 ...

  6. 服务升级中的zookeeper

    服务升级中zookeeper可以管理服务中的配置以及作为leader选举以及分布式事务等, 在这次主要用于配置管理,关于配置管理主要设计如下,通过zookeeper管理配置项,通过 管理界面来管理数据 ...

  7. Hibernate注解方式配置-继承关系

    在JPA中,实体继承关系的映射策略共有三种:单表继承策略(table per class).Joined策略(table per subclass)和Table_PER_Class策略. 1.单表继承 ...

  8. 如何获取ResultSet的行数和列数

    当我们执行数据库查询返回一个ResultSet的时候,很多情况下我们需要知道这个ResultSet的大小,即它的行数和列数.我们知道它的列数可以通过resultSet.getMetaData().ge ...

  9. .NET静态变量与静态方法并发的问题

    我们知道,静态变量与静态方法都是在程序编译的时候就定义好了的,并且不会存在多个副本.所以对于静态变量来说,一旦修改了就会影响全局. 因此,静态变量是存在并发性问题的,所以当我们在操作静态变量的时候需要 ...

  10. CentOS 搭建 nginx + tomcat

    安装nginx yum install nginx 修改 nginx.conf, (/etc/nginx/nginx.conf), 网上有人做人所有配置项目的详解. #nu For more info ...