1. 程式人生 > >SQL Server Replication

SQL Server Replication

sql server replication 為實現資料庫讀寫分離,高可用等都提供了不錯的解決方案。實現 replication 總共分為三種方法:

  1. 是 SSMS 來搭建,這個方法比較簡單,只要你在腦袋裡有了成型的架構思維和拓撲結構,就能很容易實現;
  2. 是依賴 T-sql script 的一套儲存過程,以 sp_create*, sp_drop* 等儲存過程, 配合 distribution 資料庫的動態管理檢視 (DMV) 來實現搭建,維護和監控;
  3. 可以使用 .NET Client 程式設計實現,這套方法比較靈活,而且可以系統化的管理多個 replciation 應用,比較適合大規模的 replication 搭建與管理。當然,既然有 .NET Client 方法也會有 Native Code 的方法,這個應用就有點廣了,我也不至於用 c 從頭來玩一遍分散式。

網路上針對第一種方法有了很多的記載和實驗,動手一搜可以搜到很多的案例,自己看著搭建也可以很快走完整套流程,中間可能會有一些小細節或者小問題需要注意。但是涉及到第二種和第三種方法,就很少了,我在 MaryKay 的時候,老外針對這兩種情況,也是選擇放棄,甚至第一種就不考慮使用,大概就是因為不好用的原因。所以今天我就要以第一種方法切入,然後談一下後面兩種方法。

先從架構拓撲談起,再細化到元件(Agents) 以及實現方法,最後講一講管理。從概念講起,先在頭腦裡搭建這個 replication 的框架,每一個元件的配置及使用,然後靈活配置各個元件的應用,這一步一個腳印的往前進,循序漸進,才能走的更遠。這個過程會花費很長的時間,所以耐心,是我完成搭建 replication 的一個必要條件。

這裡寫圖片描述
上面這張圖,簡明扼要的闡述了 replication 的架構與元件,綠線表示讀,紅線表示寫。看圖說話,我們一一將裡面的關鍵點說明白:

  1. Publisher, Distributor, Subscriber, 這三者可以分佈在不同的 database instance 上面,也可以都在同一個 Instance 上面。 實踐中,我們一般都會將三種角色,分佈在三個不同的伺服器的 database instance 上面,保證了應用的可用性,不至於一臺伺服器宕機,其他服務都不可用的狀況。
  2. Replication 是一種資料複製服務,在 sql server 中的實現,是脫離了 database engine 概念的,像 storage engine, query engine 等。 Replication (資料讀)的實現,其實是一組 windows executables 的應用,學名上是 Agents,這些 Agents 普遍來說,都是建立在 Distributor 這個角色所在的 database instance 裡面,具體就是配置在 Distribution 這個資料庫裡面,當然這個資料庫名字可以更改,我們需要知道的是,這個資料庫就像是一組元資料,儲存了我們定義好的一系列 replication 元件,包括像 publication, subscription 等等。 這裡唯一要注意的,就是 pull subscription 與 push subscription 的 Agents 存放地點,pull subscription 是將 Agents 存放到了 subscriber 上面而 push subscription 的 Agents 都存放到了 Distributor 上面。
  3. 既然談到了 distributor, 我們就要提到,整個 replication 的建立的時候,第一個要建立的要素就是 Distributor: 選一臺伺服器,setup 一個 database instance, 讓它作為 Distributor 角色,建立存放元資料的 distribution 資料庫。為了易於管理,可以建立多個 Distribution 資料庫,讓每一個 distribute 資料庫存放一組 publisher 的publication. 接下來就是建立 Publication 與 Subscription. 次序是先 publication 再 subscription. 這裡有兩個注意點: 一是 distributor 所在的 instance, 想要 publish 一些 publication的話,只能以自己的 distributor 作為分佈者;二是任何想要用到這個 distributor 的 publisher 都必須在這個 distributor 上面先註冊。理論上就是,我們不能隨便分發你的出版刊物,但是你們出版社,也不能隨便使用我們的分銷渠道為你們服務,都必須先訂了合同。
  4. Replication 可以有三種表現形式: Snapshot Replication, Transactional Replication 以及 Merge Replication. transactional replication: 可以用來整合資料倉庫,提供高可用與高併發。 Merge Replication, 最常見的應用場景是移動應用,也可以做資料整合;Snapshot replication 則是用來初始化資料,在應用 transactional , merge replication 的時候,做一次全量抽取。可見, Snapshot Replicatoin 提供的是一個基礎功能。
  5. 關於 Snapshot Replication, 有趣的是它的實現機制: 首先會在 publication 資料庫記上倆比 marker, 第一筆是記錄 snapshot 開始的時間,第二筆是記錄 snapshot 結束的時間。當把 snapshot 生成 bcp file 之後, log reader agent 會把倆比 marker 中發生的 command 也生成日誌,再應用到已經生成的 bcp 檔案中。 這樣的作用就是保證一份完整的 snapshot。之後的 transaction replication 就只要生成最後一個 marker 之後的操作就行。從而避免了重複生成 snapshot 生成時間之間的發生在 publication database 上的操作。這樣的 snapshot replication 就可以接受最後一個 log marker 之後的日誌重做了。
    這裡先埋下個小問題:如果沒有日誌的操作,該怎麼同步呢?

把圖裡的內容都講了一遍了,就要問自己幾個問題了:

  1. Publication 的內容都分發出去了,那麼怎麼保證 Publication 的內容與各個 Subscription 之間的內容保持一致性了呢? 檢驗機制有哪些? 一般我們想到的是做 Audit, 比如 checksum, except 等等。 但是實現起來比較複雜,sql server replication有哪些好用的特效能幫助我們快速的達到驗證資料一致性的目的呢?
  2. 既然是 replication, 資料複製嘛,那麼什麼時候會同步一次,多久呢? 我們是不是可控這些同步資料的時間間隔呢?怎麼去同步,手工,還是定義一些同步指令碼?
  3. 錯誤控制?如果同步不成功,會有哪些警告,我們改如何處理這些警告?比如一臺 remote replication 伺服器宕機,那麼這臺伺服器就不能接受 publication 了,那這一部分沒有同步的資料,是不是會保留?什麼時候會繼續同步這部分資料?
  4. Snapshot 在建立的時候,會給資料庫帶來多少影響, 鎖在這裡面是如何實現的 ? Transactional , Merge Replication 又是如何使用鎖的?
  5. replication 與 Log shipping , database mirroring 的區別: 前者可以釋出整個資料庫也可以僅僅是一個庫中的部分物件,而後倆者則釋出的是整個資料庫。那麼這部分物件同步到已存在的資料庫時,是不是會重寫當前資料庫的其他物件?
  6. 上面留下的小問題: 如果沒有日誌的操作,該怎麼同步呢? 比如我是用 bcp 插入的 publication 中的表,這部分資料都是沒有日誌的,那麼怎麼被 log reader agent 讀取到,並同步到其他 replica 裡面去?

人生嘛,就是解決一個一個的問題,要回答上面這些問題,還是靠一步一步做實驗來弄清楚,先從 SSMS 搭建一個 replication 開始:

  1. 新建 3 臺虛擬機器,裝上 sql server 2008R2 , 分別將 instance 命名為 Distributor, Publisher, Subscriber. 在 Publisher 中新建一個數據庫, 名叫 stock, 我們假定這是一個用來存放股票相關的資料庫。 新建一張表叫做 region. 我們的目標是把這張表同步到 Subscriber 中的 stock 資料庫中去。這個 Subscriber 中的 stock 是必須先建立好的。
  2. 先建立一個 Distributor 角色,將它安裝在 Distributor 這個 instance 上面,建立完之後,會有一個 distribution 的資料庫,我們這裡要額外注意下這個資料庫裡面的物件,資料庫表,stored procedure 等。這些資料庫物件在我們手工用 .net client 或者 T-sql 儲存過程搭建 replication 的時候,會經常用到。
  3. 接著在 Publisher 這個 instance 下面,新建一個 publication. 這個 Publication 包含了一張表, region. 沒錯,這份 publication 是定義在 publisher 上面,所以不能在 distributor instance 上面找到。但是在 Publisher 上面指定 Distributor 的時候,會選擇某一個 instance 來當 distributor ,我們這裡正好是這個裝有 distributor instance 的機器。所以在 distribution 資料庫中,會有相關記錄,表示已經有 publisher 在使用我們的 distributor 功能了。這裡不能搞混的是 publication 與 Distributor Agent 的概念。Distributor 的 snapshot Agent 以及 Log Reader Agent 是在 Distributor 這個伺服器上的,這兩個 Agent 是去讀 publication 的。所以 Publication 一定是在 publisher 伺服器上建立的.
  4. 同3 所說, Subscription 也是配置在 Subscriber 這個 instance 上面的。 至於 Distributor Agent 是建立在 Distributor 還是 Subscriber 上面, 就看是哪種型別的 Subscription 了。前文有講這裡不再累述。配置完,記得要看下 distribute 資料庫。這裡發生的變化都在這個庫裡能體現出來。

這裡有張圖,可以很好的看到,publication, distributor agents, subscription 的所屬問題,注意黑圓點的標註:

這裡寫圖片描述

好了,上面寫的是思路,具體的實現,我們一步一步來,在這個實現的步驟裡面,肯定會遇到很多不可思議的事情,一件一件記錄下來,肯定能獲得不少 serendipity (意外的收穫):

  1. Distributor 配置好之後,我們要為 publisher配置 distributor 了,這個時候,奇怪的問題來了,用密碼登陸 distributor 的時候,始終登陸不了,總是出現這個錯誤 “ sql server could not cononect to the distributor with the specified password”。參考了網路上的一篇帖子,有這麼個用法:
    首先,先檢查下 remote access 在資料庫裡面是不是開啟,而在可以檢查這個選項的時候,還需要將 show advanced options 這個開關給開起來,看看是怎麼開的 :
    sp_configure ‘show advanced options’, 1
    reconfigure
    這還只是第一步,先將 show advanced options 啟動,reconfigure 就是讓這個選項獲得新值之後,重新寫到資料庫配置文裡面。接下來就是配置 remote access, 讓其 Run Value 等於 1:
    sp_configure ‘remote access’, 1
    reconfigure
    到這一步就可以了,重啟 sql server instance, 再 sp_configure 檢視下 remote access 的值, 確認 Run_Value 是1 了。這一步在 publisher, distributor 上面都要執行好。

  2. 別以為 sql server 預設會幫你把一切都配置好,尤其是要用到 replication,failover cluster 等這類特性的時候,我們需要檢查一些常規的配置,比如剛才 1 中提到的 remote access, 接下來 remote access 是允許了,但是 remote access 用到的相關渠道(協議)是不是也已經開啟了呢? 這裡講的就是 sql server configuration manager 裡面會用到的 TCP/IP, Named pipes 遠端協議是不是被啟動了? 沒啟動,這裡要啟動起來。否則,經典的連線錯誤就會找上來:
    A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 28 - Server doesn’t support requested protocol)

  3. Distributor 的功能包括了,從 publisher 拉回 snapshot和相關 transaction,並推給 subscriber 或者等著 subscriber 來取 snapshot 或者 transaction。 這裡就會有兩個 sql agent job(根據我們的 replication type , job 的數量會有不同), 名字很好認,一眼就能看出來,那麼修改這兩個 job 的時間排程就能修改我們需要的 replication 更新頻率了。
  4. 前面有個問題,publication 的這一部分資料,會不會沖掉原來的表的資料和物件? 同一個表的資料會被沖掉,但是已經存在的物件是衝不掉的,比如有一張表是在訂閱之前就存在在 subscriber 資料庫中的,那麼只要 publication 裡面沒有同名的表,這張表還是會被留下來的,不受影響。
  5. 繼續回答前面的問題,我們知道 Distributor 的 log Reader Agent 是去讀取 publisher 的日誌,從而生成一份日誌重做的行為資料,存在 distribution 資料庫裡,等待下次同步的時候,在 subscriber 的 subscription 資料庫裡重新跑下這份日誌重做的行為資料,完成一次 Transactional Replication. 但是如果我們在 publisher 的 publication 上面(這份 publication 就包含一份 article, 就是一張表 region, 包括了兩個欄位 regionId, regionName), 用了一次 bcp 匯入了 1000 條資料,都知道 bcp 是不寫日誌的,那麼 Transaction Replication 做增量更新的時候,會不會同步這 1000 條資料到 subscription 的資料庫裡面呢?
    首先,我們要搭建一個 Transactional Replication 的環境。注意要給需要做 publication 的錶帶上主鍵,否則會有這麼個錯誤:
    the database contains no objects that can be replicated with the selected publication type
    接著用 bcp 匯入 1000 條資料,過不到 1 分鐘,去看下 subscription 裡的同步表 stock, 發現數據居然同步過來了。這裡面其實我犯了個錯誤,概念性的錯誤,bcp 其實是會寫日誌的,不寫日誌的情況,僅僅是在bulk-logged 和 simple recovery model 下面,不過這樣是肯定會丟資料的,事實證明,沒有日誌的表,是不允許 replicate 的。碰到這種情況, snapshot replication 是個好選擇。
  6. Snapshot Replication 在 init snapshot 的時候,會對錶進行一個 S 鎖,對 page, rid 分別進行 IX, X 鎖。影響還是蠻大的。Transaction Replication 增量讀的是日誌,由於有 primary key 的存在,所以對 key 也進行了鎖,而且是 X 鎖。
  7. 在配置 publisher publication的時候,會有對 retention 做限制。比如,(即使在 publication全部同步完之後)增量抽取的日誌保留多長時間,在每一份 subscription 沒有同步之前,會保留多長時間等。
  8. 在每一份 publication 的右鍵選單裡,包含了一個 validate subscription 的選項,好東西,用來驗證資料一致性。比起我們手寫 checksum 或者 except 來驗證,是不是方便了很多呢?這個 validation 的結果,SSMS 是不會馬上給你看到的,我們要開啟 Launch Replication Monitor 來看, 找到 Distributor to Subscription history , 檢視 action message, 就可以看到 validation 到底有沒有 pass 了。
  9. 不過細想,一個 publisher 只能訂一個 distributor ,好像不合理? 為什麼不能簽訂多個 distributor ,這樣可以分散一個 distributor 的壓力,也可以實現多播的可能性。

到這裡,把前面的自個兒問自個兒的問題都回答了,那麼我們就要進入下一個話題,如何用 T-SQL 程式設計的方法或者 .NET Client 的方法來構建 replication 呢?

  1. 大概的一個拓撲, 我們將三種角色分別安裝在三臺不同的 sql server 伺服器上
    1.1 distributor
    1.2 publisher, publication, article
    1.3 subscriber , subscription
    1.4 Agents , Schedulers
  2. 簡單的一個實現, 以 snapshot replication 為例子
    2.1 distributor script :
    我們會在 distributor 角色的 sql server 伺服器上,將這臺伺服器設定為 distributor ,建立 distribution 資料庫, 並配置一個允許使用它作為 distributor 的 publisher。
    2.2.1 sp_adddistributor 第一次執行的時候,必須指定password, 這個 password 是 distributor_admin 密碼。 在 publisher 連線 distributor 的時候,也必須指定這個 distributor_admin 的密碼,用來通訊。下面這個例子其實還需要為 sp_adddistributor 引數 @password 賦值.
    2.2.2 三大要素: distributor 所用到的 Instance , 以 serverName\instanceName 命名; distributor 用到的資料庫 distribution (名字可以更改); Snapshot 用到的儲存路徑.
DECLARE @distributor AS sysname;
DECLARE @distributionDB AS sysname;
DECLARE @publisher AS sysname;
DECLARE @directory AS nvarchar(500);
DECLARE @publicationDB AS sysname;
-- Specify the Distributor name.
SET @distributor = 'storage\distributor';
-- Specify the distribution database.
SET @distributionDB = N'distributionstock';
-- Specify the Publisher name.
SET @publisher ='publisher\crm';
-- Specify the replication working directory.
SET @directory = N'C:\snapshot';
-- Specify the publication database.
SET @publicationDB = N'stocktrans';
-- Install the server Storage\Distributor as a Distributor using the defaults,
-- including autogenerating the distributor password.
USE master
EXEC sp_adddistributor @distributor = @distributor;
-- Create a new distribution database using the defaults, including
-- using Windows Authentication.
USE master
EXEC sp_adddistributiondb @database = @distributionDB,
@security_mode = 1;
GO
-- Create a Publisher and enable stocktrans for replication.
-- Add publisher\crm as a publisher with storage\distributor as a local distributor
-- and use Windows Authentication.
DECLARE @distributionDB AS sysname;
DECLARE @publisher AS sysname;
-- Specify the distribution database.
SET @distributionDB = N'distribution'stock;
-- Specify the Publisher name.
SET @publisher = 'publisher\crm'
USE [distributionstock]
EXEC sp_adddistpublisher @[email protected],
@[email protected],
@security_mode = 1;
GO
2.2 publication script , article script
    2.2.1 首先要做的兩點,就是:一啟動 publisher 的角色;二配置要使用的 distributor . 這裡使用到的儲存過程 sp_replicationdboption .
        use stocktrans
        go
        sp_adddistributor 
        @distributor = 'storage\distributor', 
        @password = 'XXXXXXXXXXX'
        sp_replicationdboption 'stocktrans','publish',true
    2.2.2 在第一步裡指定的 replication database, 執行 sp_addpublication 來新增 publication.
-- Create a new transactional publication with the required properties.
EXEC sp_addpublication
@publication = 'stocktranspub',
@status = N'active',
@allow_push = N'true',
@allow_pull = N'true',
@independent_agent = N'true';
-- Create a new snapshot job for the publication, using a default schedule.
EXEC sp_addpublication_snapshot
@publication = 'stocktranspub',
@job_login = 'smartoffice\joe',
@job_password = 'xxxx',
-- Explicitly specify the use of Windows Integrated Authentication (default)
-- when connecting to the Publisher.
@publisher_security_mode = 1;
GO
    2.2.3 新增 article
DECLARE @publication AS sysname;
DECLARE @table AS sysname;
DECLARE @filterclause AS nvarchar(500);
DECLARE @filtername AS nvarchar(386);
DECLARE @schemaowner AS sysname;
SET @publication = N'stocktranspub';
SET @table = N'stock';
SET @schemaowner = N'dbo'';

EXEC sp_addarticle
@publication = @publication,
@article = @table,
@source_object = @table,
@source_owner = @schemaowner,
@vertical_partition = N'true',
@type = N'logbased'
2.3 subscription script 以 push subscription 為例子. 所有的操作都在publisher, publication 裡面執行。
    2.3.1 判斷 publication 是不是可以被 push 或者 pull
        Sp_helppublication
    2.3.2 新增 push subscription
        Sp_addsubscription
    2.3.3 新增 push distributor agent
        Sp_addpushsubscription_agent
    2.3.4 預設是一天執行一次 snapshot push over ,那麼怎麼去修改這個同步間隔呢?
        Sp_add_schedule
use stocktrans
go
sp_addsubscription 
  @publication = 'stocktranspub',
  @subscriber = 'sqlcluster\mysqlcluster',
  @destination_db = 'stock'

3 . 監控健康指標
3.1 Replication Monitor

4 . 移除 replication , 察看 distribution 資料庫元資料的更改

4.1 先移除 subscriber 和 subscription Sp_dropsubscription,sp_subscription_cleanup
4.2 再移除 publisher 和 publication
4.3 再移除 distributor
4.4 細節解說

為了禁止 Replication,我們必須依次執行以下步驟:
4.4.1 停掉所有的 Replication 相關的 Sql Agent Job, 關於 Replication 相關的 Sql Agent job ,命名也是有規律可以尋的,可以參考這篇文章 Replication Agent Security Model
4.4.2 在每一個 subscriper 的 subscription 資料庫上,執行 sp_removedbreplication,刪掉 replication 相關的資料庫物件。
4.4.3 在 publisher 的 publication 資料庫上,執行 sp_removedbreplication 來刪掉 replication 相關的資料庫物件。如果配置了 distributor ,那麼執行 sp_dropdistributor 來刪掉相關應用。
4.4.4 在 Distributor 的例項上,執行 sp_dropdisttpublisher. 有多少 publisher 配置了,就要執行相應的次數的命令,直到全部刪掉。之後,執行 sp_dropdistributiondb 來解除安裝相關的 distribution 資料庫。配置了多少 distribution 資料庫,針對每個庫都要做一次 sp_dropdistributiondb。最後執行 sp_dropdistributor 來刪掉這個 distributor 角色。