aaaaaaaaaaaaaaaaaaaaa

一.本文所涉及的内容(Contents)

  1. 本文所涉及的内容(Contents)
  2. 背景(Contexts)
  3. 解决方案(Solution)
  4. 搭建过程(Process)
  5. 注意事项(Attention)
  6. 参考文献(References)

二.背景(Contexts)

  搭建SQL Server复制的时候,如果网络环境是局域网内,通过主机名就可以实现了,但是如果是跨网段、跨机房异地搭建复制的时候就需要注意了,因为SQL Server复制不支持通过IP连接分发服务器,那有什么办法解决跨网段、跨机房的问题呢?

三.解决方案(Solution)

  在跨网段、跨机房进行SQL Server复制的时候需要区分两种情况:一种是外网IP的1433端口对应了这台机器SQL Server的数据库端口;另外一种情况是外网IP对应SQLServer机器的端口不是1433;下面是几种解决方案:

A. 如果外网IP端口是1433,可以在Windows的host文件中指定IP地址与主机名的对应关系,主机名必须跟真实的主机名一样?

B. 因为你的外网IP端口不是1433,所以你无法在host文件中跟IP地址一起指定端口;这种情况下,如果条件允许(安全性和端口数),你可以在防火墙中开放外网IP的1433端口对应这个发布服务器的1433端口,并且限制某个IP可以访问这个端口,程序等访问发布数据库就使用另外的21433端口,保证了1433端口的安全;又可以解决端口映射问题,可以画张图解释;

(Figure1:逻辑结构图)

C. 另外一种方案是在SQL Server配置管理器里建立一个SQL Server别名,这个别名需要跟主机名一样,不需要启用SQL Server Browser服务;

四.搭建过程(Process)

(一) 环境信息

系统环境:Windows Server 2008 + SQL Server 2008

发布服务器:192.168.1.101,1924,192.168.1.101,1433,服务器名称:USER-H2B2A89PEK

分发服务器:与发布服务器同一台机器

订阅服务器:192.168.1.102,1433,服务器名称:QuZhoushiwei105

发布数据库:Task

订阅数据库:TaskSubscribe

数据库帐号:ReplicationUser/ ReplicationPassword

(二) 搭建步骤

  上面的发布服务器的外网IP开通了两个端口,一个是默认的1433,一个是1924,如果是默认的1433,可以通过host文件,而如果只有端口1924的话就只能通过SQL Server别名方式实现,这里为了做测试就一起开通了这2个端口了。

A. 下面是通过host文件的形式创建订阅的具体步骤:

1) 在发布服务器上创建发布,具体操作可以参考:SQL Server 复制事务发布,只有搭建成功之后下面的步骤才能进行;

2) 设置订阅服务器C:\Windows\System32\drivers\etc目录的host文件,添加分发服务器(我的环境是发布服务器与分发服务器是一起的,所以这里指定的是发布服务器的地址)信息:192.168.1.101 USER-H2B2A89PEK

3) 设置分发服务器C:\Windows\System32\drivers\etc目录的host文件,添加订阅服务器信息:192.168.1.102 QuZhoushiwei105

4) 在订阅服务器上创建订阅,具体步骤如下:

(Figure2:连接发布服务器)

(Figure3:成功连接发布服务器)

(Figure4:选择推送订阅)

(Figure5:选择订阅数据库)

(Figure6:推送订阅帐号密码)

(Figure7:代理计划)

(Figure8:初始化)

(Figure9:成功后的订阅信息与作业)

(Figure10:新建订阅的状态信息)

B. 下面是通过SQL Server别名的形式创建订阅的具体步骤:

1) 在发布服务器上创建发布,具体操作可以参考:SQL Server 复制事务发布,只有搭建成功之后下面的步骤才能进行;

2) 在订阅服务器上创建分发服务器(我的环境是发布服务器与分发服务器是一起的,所以这里指定的是发布服务器的地址)的别名;

(Figure11:订阅服务器上的别名)

  在分发服务器上,如果别名的设置包括:SQL Native Client 10.0 配置(32位)和SQL Native Client 10.0 配置,需要都设置订阅服务器的别名,如果没有像Figure13那样进行设置,

(Figure12:分发服务器上的别名)

(Figure13:分发服务器上的别名)

3) 接下来的步骤按照Figure2到Figure8进行就可以了,同样,最后一样可以达到Figure9、Figure10的效果;

五.注意事项(Attention)

1. 使用请求订阅,分发作业是在订阅服务器上创建的;使用推送订阅,分发作业是在分发服务器上创建;

2. 在发布服务器上创建发布的时候,如果SQL Server数据库实例名与服务器名不一致,将会出现下面的错误:

(Figure14:发布错误)

在订阅服务器上创建订阅的时候,如果SQL Server数据库实例名与服务器名不一致,将会出现下面的错误:

(Figure15:订阅错误)

3. 可以通过下面的SQL脚本修改不一致的问题,修改之后记得重启SQL Server服务才能生效;

/*
SQL Server数据库实例名与服务器名不一致的解决办法
*/
IF SERVERPROPERTY(‘SERVERNAME‘)<>@@SERVERNAME
BEGIN
    DECLARE @server SYSNAME
    SET @server=@@SERVERNAME
    EXEC sp_dropserver @server=@server
    SET @server=CAST(SERVERPROPERTY(‘SERVERNAME‘) AS SYSNAME)
    EXEC sp_addserver @server=@server,@local=‘LOCAL‘
END
SQL Server数据库实例名与服务器名不一致的解决办法
*/
IF SERVERPROPERTY(‘SERVERNAME‘)<>@@SERVERNAME
BEGIN
    DECLARE @server SYSNAME
    SET @[email protected]@SERVERNAME
    EXEC sp_dropserver @[email protected]
    SET @server=CAST(SERVERPROPERTY(‘SERVERNAME‘) AS SYSNAME)
    EXEC sp_addserver @[email protected],@local=‘LOCAL‘
END

4. 分发服务器上的快照文件会给删除?

CareySon:After the snapshot is applied at all Subscribers, replication cleanup deletes the associated .bcp file for the initial snapshots automatically.

5. 如果通过修改host文件部署发布订阅,你必须使用推送订阅模式,如果你使用请求订阅模式,因为你无法读取快照文件,将会报下面的错误:

(Figure16:请求订阅无法读取快照文件错误信息)

使用别名的形式部署发布订阅,同样会存在相同的问题,CareySon在Azure上通过开通相关权限的方式可以使用请求订阅方式,但是过程比较复杂,这里就不做讨论了;

6. 如果只在订阅服务器的host文件上指定了分发服务器(这里指定是发布服务器,因为我的发布服务器与分发服务器是一起的),没有在分发服务器的host文件上指定订阅服务器地址,将会发生下面的错误:

(Figure17:发布服务器连接不上订阅服务器)

7. 如果服务器同时存在“SQL Native Client 10.0 配置(32位)”和“SQL Native Client 10.0 配置”,测试发现“SQL Native Client 10.0 配置(32位)”设置的别名是提供给:查找SQL Server发布服务器使用的,错误信息类似Figure19所示;“SQL Native Client 10.0 配置”设置的别名是提供给:分发服务器作业推送到订阅服务器使用的,错误信息类似Figure17所示;具体原因不清楚,求科普;

(Figure18:别名)

8. 在分发服务器和订阅服务器上设置别名的时候,别名应该跟服务器的实例名要一致,不然会报下面的错误:

(Figure19:错误信息)

时间: 2024-10-14 12:45:16

aaaaaaaaaaaaaaaaaaaaa的相关文章

执行计划-数据访问方式(全表扫描与4种索引的方式)

执行计划 Oracle执行计划的相关概念: Rowid:系统给oracle数据的每行附加的一个伪列,包含数据表名称,数据库id,存储数据库id以及一个流水号等信息,rowid在行的生命周期内唯一. Recursive sql:为了执行用户语句,系统附加执行的额外操作语句,譬如对数据字典的维护等. Row source(行源):oracle执行步骤过程中,由上一个操作返回的符合条件的行的集合. Predicate(谓词):where后的限制条件. Driving table(驱动表):又称为连接的

388. Longest Absolute File Path

题目如下: Suppose we abstract our file system by a string in the following manner: The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents: dir subdir1 subdir2 file.ext The directory dir contains an empty sub-directory subdir1 and a sub-dire

九 函数

什么是函数? 为什么要用函数? 函数的分类:内置函数与自定义函数 如何自定义函数 语法 定义有参数函数,及有参函数的应用场景 定义无参数函数,及无参函数的应用场景 定义空函数,及空函数的应用场景 调用函数 如何调用函数 函数的返回值 函数参数的应用:形参和实参,位置参数,关键字参数,默认参数,*args,**kwargs 高阶函数(函数对象) 函数嵌套 作用域与名称空间 装饰器 迭代器与生成器及协程函数 三元运算,列表解析.生成器表达式 函数的递归调用 内置函数 面向过程编程与函数式编程 本节课

The internals of Python string interning

JUNE 28TH, 2014Tweet This article describes how Python string interning works in CPython 2.7.7. A few days ago, I had to explain to a colleague what the built-in function intern does. I gave him the following example: >>> s1 = 'foo!' >>>

C# 字符串详细使用

转自 http://www.cnblogs.com/candywyq/archive/2007/07/24/830021.html 1.Convert.ToInt32与Int32.Parse的恩恩怨怨 2.Split的3种用法 3.@"abc"和"abc"区别在那里 4.保留2位有效小数及四舍五入 5.url传递中文的解决方案 6.把123456789转换为12-345-6789的3种方法 7.交换两个指定位置字符的4种方法 8.“%10”的妙用 9.输出21个AA

Python string interning原理

原文链接:The internals of Python string interning 由于本人能力有限,如有翻译出错的,望指明. 这篇文章是讲Python string interning是如何工作的,代码基于CPython2.7.7这个版本. 前一段时间,我向同事解释了python的buil-in函数 intern背后到底做了什么.我给他看了下面这个例子: >>> s1 = 'foo!' >>> s2 = 'foo!' >>> s1 is s2

[LintCode] System Longest File Path

Suppose we abstract our file system by a string in the following manner: The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents: dir subdir1 subdir2 file.ext The directory dir contains an empty sub-directory subdir1 and a sub-directory 

MySQL 锁机制

Innodb的为行锁 MyISAM的为表锁 lock tables 命令是为当前线程锁定表.这里有2种类型的锁定,一种是读锁定,用命令 lock tables tablename read;另外一种是写锁定,用命令lock tables tablename write.下边分别介绍: 1. lock table 读锁定 如果一个线程获得在一个表上的read锁,那么该线程和所有其他线程只能从表中读数据,不能进行任何写操作. 下边我们测试下,测试表为user表. 不同的线程,可以通过开多个命令行My

多线程的方法和类别

获取名字 * 通过getName()方法获取线程对象的名字 new Thread("xxx") { public void run() { for(int i = 0; i < 1000; i++) { System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa"); } } }.start(); new Thread("yyy") { public void run()