web-dev-qa-db-ja.com

プライマリでデータ損失を許可してDBCC CheckDBを実行した後、SQL Serverレプリケーションが壊れる

SQL Server 2008データベースでは、一方向のトランザクションレプリケーションが有効になっています。いくつかの問題のため、実行する必要がありましたDBCC checkDB、データベースをシングルユーザーモードにします。 DBCCアクティビティを完了してレプリケーションを再度有効にすると、レプリケーションは機能しません。レプリケーションを機能させるには、スナップショットを再度初期化する必要がありました。

このシナリオでレプリケーションが機能しない理由を教えてください。 DBCCはテーブルの一部の行とインデックスを削除することを理解していますが、レプリケーションを有効にすると、これらの変更はサブスクライバーに問題なく伝播されるはずです。

シニアチームメンバーに確認したところ、LSNの不一致が原因だと言われましたが、論理的には理解できません。

どんな助けでも大歓迎です。

2
TheGameiswar

特定のシナリオで何が問題であったかを正確に特定するために、3つの重要な情報が欠落しています。

  1. 問題を見つけた瞬間から、レプリケーションが機能しないことを発見した瞬間まで、正確な手順はどこにありますか?
  2. 壊れたもの、修理されたもの、失われたものは何ですか?この情報はすべてCHECKDBの出力で確認できます。
  3. 「レプリケーションが機能しませんか?」エラーが発生したか、またはテーブルが同じではありませんか?データが欠落していますか?

レプリケーション環境でデータベースの破損に直面し、修復結果を正確に解釈する方法がわからない場合ORパブリケーションがそれほど大きくない場合は、常に再初期化することをお勧めします(可能な場合) )または、サブスクリプションのパブリケーションをドロップ/再作成します。

その理由は、_DBCC CHECKDB_によって実行されるほとんどの修復操作がサブスクライバーに複製されないためです(後でデモを参照してください)。非常に大きなパブリケーションがあり、_DBCC CHECKDB_出力の読み方を正確に知っている場合は、テーブルまたは行レベルで手動で修正できる。

パブリケーションデータベースの破損後にレプリケーションが失敗する主な理由

  1. トランザクションログの破損

  2. メタデータの破損

  3. ユーザーデータの破損

一般的なセットアップ

次のスクリプトを使用して、同じサーバーでパブリッシャー、ディストリビューター、サブスクライバーをセットアップし、ソースと宛先のデータベース、および10k行のテーブルと、このテーブルのパブリケーションとサブスクリプションを作成します。次のテストでは、このセットアップを使用します。

_Version:

        SELECT @@VERSION

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Microsoft SQL Server 2012 (SP1) - 11.0.3128.0 (X64) 
    Dec 28 2012 20:23:12 
    Copyright (c) Microsoft Corporation
    Enterprise Edition: Core-based Licensing (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1) (Hypervisor)
_

設定スクリプト:

_--Create a source database
CREATE DATABASE [RepSource]
GO
--Create a destination database
CREATE DATABASE [RepDest]
GO

USE [RepSource]
GO

--Create a table that we are going to publish
CREATE TABLE [dbo].[RepUserTable] 
(
    [ID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [TestString] [varchar](4000) NOT NULL DEFAULT (replicate('A',(4000))),
    CONSTRAINT [PK_RepUserTable] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO


--fill the table with 10k rows
SET NOCOUNT ON
INSERT INTO dbo.RepUserTable DEFAULT VALUES
GO 10000



--configure distribution
USE master
GO
-- declare local server as distributor
exec sp_adddistributor @distributor = @@SERVERNAME,  @heartbeat_interval= 10, @password = N'Dis@Password'
GO

--install local distribution db
exec sp_adddistributiondb   @database = N'distribution'
GO

--find the default backup directory to use as a working directory
DECLARE @path NVARCHAR(4000)
EXEC master.dbo.xp_instance_regread
            N'HKEY_LOCAL_MACHINE',
            N'Software\Microsoft\MSSQLServer\MSSQLServer',N'BackupDirectory',
            @path OUTPUT, 
            'no_output'

--Make this server a publisher
exec sp_adddistpublisher    @publisher = @@SERVERNAME,
                            @distribution_db = N'distribution', 
                            @security_mode = 1,
                            @working_directory = @path, --Make sure the sql agent account has enough permission in this dir.
                            @trusted = N'false',
                            @thirdparty_flag = 0, 
                            @publisher_type = N'MSSQLSERVER'

GO

--Make this server the subscriber as well
exec sp_addsubscriber       @subscriber = @@SERVERNAME, 
                            @type = 0,
                            @description = N'Subscriber',
                            @security_mode=1

GO


--Adding a publication
use [RepSource]
exec sp_replicationdboption @dbname = N'RepSource', @optname = N'publish', @value = N'true'
GO

exec sp_addpublication @publication = N'TestPub2', @description = N'Transactional publication of database.', @sync_method = N'concurrent', @retention = 0, @allow_Push = N'true', @allow_pull = N'true', @allow_anonymous = N'true', @enabled_for_internet = N'false', @snapshot_in_defaultfolder = N'true', @compress_snapshot = N'false', @ftp_port = 21, @allow_subscription_copy = N'false', @add_to_active_directory = N'false', @repl_freq = N'continuous', @status = N'active', @independent_agent = N'true', @immediate_sync = N'true', @allow_sync_tran = N'false', @allow_queued_tran = N'false', @allow_dts = N'false', @replicate_ddl = 1, @allow_initialize_from_backup = N'false', @enabled_for_p2p = N'false', @enabled_for_het_sub = N'false'
GO
exec sp_addpublication_snapshot @publication = N'TestPub2', @frequency_type = 1, @frequency_interval = 1, @frequency_relative_interval = 1, @frequency_recurrence_factor = 0, @frequency_subday = 8, @frequency_subday_interval = 1, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 0, @active_end_date = 0, @job_login = null, @job_password = null, @publisher_security_mode = 1
GO
exec sp_addarticle @publication = N'TestPub2', @article = N'RepUserTable', @source_owner = N'dbo', @source_object = N'RepUserTable', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'RepUserTable', @destination_owner = N'dbo', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_dboRepUserTable', @del_cmd = N'CALL sp_MSdel_dboRepUserTable', @upd_cmd = N'SCALL sp_MSupd_dboRepUserTable'
GO

--Add a subscription
use [RepSource]
exec sp_addsubscription @publication = N'TestPub2', @subscriber = @@servername, @destination_db = N'RepDest', @subscription_type = N'Push', @sync_type = N'automatic', @article = N'all', @update_mode = N'read only', @subscriber_type = 0
exec sp_addpushsubscription_agent @publication = N'TestPub2', @subscriber = @@servername, @subscriber_db = N'RepDest', @job_login = null, @job_password = null, @subscriber_security_mode = 1, @frequency_type = 64, @frequency_interval = 0, @frequency_relative_interval = 0, @frequency_recurrence_factor = 0, @frequency_subday = 0, @frequency_subday_interval = 0, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 20140107, @active_end_date = 99991231, @enabled_for_syncmgr = N'False', @dts_package_location = N'Distributor'
GO
_

スクリプトを実行した後、スナップショットエージェントを開始すれば完了です。

トランザクションログの破損

トランザクションログの破損に遭遇し、緊急モードでDBCC CHECKDB (RepSource,REPAIR_ALLOW_DATA_LOSS)を使用してログファイルを再構築すると、パブリケーションとサブスクリプションがなくなっていることがわかります。これらを再作成する必要があります。

  1. SQL Serverを停止する
  2. トランザクションログファイルの名前を変更する
  3. sQL ServerとSQLエージェントサービスを開始する

データベースが破損していることを確認します。

_SELECT state_desc 
FROM sys.databases
WHERE name='RepSource'

OUTPUT:
state_desc
------------------------------------------------------------
RECOVERY_PENDING


--try getting the db online
ALTER DATABASE [RepSource] SET ONLINE

OUTPUT:
File activation failure. The physical file name "e:\SQLServer\Log\RepSource_log.ldf" may be incorrect.
The log cannot be rebuilt because there were open transactions/users when the database was shutdown, no checkpoint occurred to the database, or the database was read-only. This error could occur if the transaction log file was manually deleted or lost due to a hardware or environment failure.
_

この問題を修正するには((バックアップ/復元なし)緊急修復を実行する必要があります:

_ALTER DATABASE [RepSource] SET EMERGENCY
ALTER DATABASE [RepSource] SET SINGLE_USER

DBCC CHECKDB (RepSource,repair_allow_data_loss)
ALTER DATABASE [RepSource] SET MULTI_USER
_

出力:ファイルのアクティブ化に失敗しました。物理ファイル名「e:\ SQLServer\Log\RepSource_log.ldf」が誤っている可能性があります。データベースのシャットダウン時に開いているトランザクション/ユーザーがあったか、データベースにチェックポイントが発生しなかったか、データベースが読み取り専用だったため、ログを再構築できません。このエラーは、ハードウェアまたは環境の障害が原因でトランザクションログファイルが手動で削除または失われた場合に発生する可能性があります。警告:データベース 'RepSource'のログが再構築されました。トランザクションの一貫性が失われました。 RESTOREチェーンが壊れており、サーバーには以前のログファイルのコンテキストがないため、それらが何であったかを知る必要があります。物理的な整合性を検証するには、DBCC CHECKDBを実行する必要があります。データベースはdbo専用モードになっています。データベースを使用できるようにする準備ができたら、データベースオプションをリセットし、余分なログファイルを削除する必要があります。

ここで、出力を見ると、トランザクションレプリケーションが機能しなくなった理由がわかります。

警告:データベース 'RepSource'のログが再構築されました。トランザクションの一貫性が失われました。 RESTOREチェーンが壊れており、サーバーには以前のログファイルのコンテキストがないため、それらが何であったかを知る必要があります。

トランザクションレプリケーションはトランザクションログに基づいており、LSNチェーンを解除するとレプリケーションが解除されます。

パブリケーションとサブスクリプションを見ると、両方ともなくなっていることがわかります。最初から作成し直す必要があります。

注:ログファイルの削除は非常に大きなステップです。トランザクションログファイルの1つのログレコードのみを破損させるツールはありません。そのシナリオでパブリケーションとサブスクリプションも削除されるかどうかはわかりません。

メタデータの破損

レプリケーションを構成すると、レプリケーションが機能するために必要なデータを保持するいくつかのシステムテーブルが作成されます。いわゆるメタデータ。マスター、MSDB、ディストリビューションデータベース、パブリッシングデータベース、サブスクライブデータベースに作成されたテーブルがあります。

個人的に私はこの種の破損の経験はありませんが、パブリッシングデータベース内のこれらのシステムテーブルのいずれかが破損した場合、おそらくレプリケーションを再セットアップする必要があります。奇妙なことにテストしたところ、syspublications、sysarticles、sysarticleupdates、および継続中のレプリケーションを破損させることができました。 ??

ユーザーデータの破損

ここが面白いところです。知らないうちにパブリッシャーとサブスクライバーの間に矛盾が生じる可能性があるからです。

その理由は、DBCC REPAIR_ALLOW_DATA_LOSSで内容を「修正」すると、物理レベルで修正されるためです。トランザクションレプリケーションは、パブリッシングデータベースのログファイル内のすべてのトランザクションを読み取るログリーダーと連動します。トランザクションにレプリケーションのマークが付けられている場合、サブクライバーで実行される論理レプリケーションステートメントでトランザクションが読み取られ、変換されます。 INSERT、DELETEなどのステートメントを処理できます。ただし、たとえばデータページが割り当て解除されている修復ステートメントは処理できません。修理が記録されないということではありません。 それは!ログリーダーがそれらの処理方法を知らないだけです。

次に例を示します。

前のスクリプトでRepSource dbに作成したReUserTableにどのページが使用されているかを調べてみましょう。

_--create user data corruption of usertable
use [RepSource]
Go

--find the data pages of RepUserTable
DBCC IND ('RepSource','RepUserTable',1)
_

出力: output of DBCC IND

この例では、296ページを使用します。これはデータページです(Pagetype = 1)。このページにある2つのレコードを見てみましょう。 (各レコードは4015バイトなので、ページあたり2レコードです。)

_DBCC TRACEON(3604)
DBCC PAGE('RepSource',1,296,3)
_

出力

_Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

ID = 3   

.......

Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4

ID = 4   
_

このページには、ID 3とID 4のレコードがあります。

ページ296が破損した後で行を選択しようとすると、チェックサムエラーが発生します。

_SELECT * FROM RepUserTable WHERE ID=3


Msg 824, Level 24, State 2, Line 2
SQL Server detected a logical consistency-based I/O error: incorrect checksum (expected: 0xb5aca98c; actual: 0xb58ea98c). It occurred during a read of page (1:296) in database ID 18 at offset 0x00000000250000 in file 'e:\SQLServer\Data\RepSource.mdf'.  Additional messages in the SQL Server error log or system event log may provide more detail. This is a severe error condition that threatens database integrity and must be corrected immediately. Complete a full database consistency check (DBCC CHECKDB). This error can be caused by many factors; for more information, see SQL Server Books Online.
_

これはクラスター化インデックスの破損であるため、_REPAIR_ALLOW_DATA_LOSS_でのみ修正できます

_--RUN DBCC CHECKDB
DBCC CHECKDB('RepSource')

OUTPUT:
Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 389576426, index ID 1, partition ID 72057594039435264, alloc unit ID 72057594043760640 (type In-row data), page (1:296). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129 and -4.
Msg 8928, Level 16, State 1, Line 1
Object ID 389576426, index ID 1, partition ID 72057594039435264, alloc unit ID 72057594043760640 (type In-row data): Page (1:296) could not be processed.  See other errors for details.
Msg 8976, Level 16, State 1, Line 1
Table error: Object ID 389576426, index ID 1, partition ID 72057594039435264, alloc unit ID 72057594043760640 (type In-row data). Page (1:296) was not seen in the scan although its parent (1:295) and previous (1:293) refer to it. Check any previous errors.
Msg 8978, Level 16, State 1, Line 1
Table error: Object ID 389576426, index ID 1, partition ID 72057594039435264, alloc unit ID 72057594043760640 (type In-row data). Page (1:297) is missing a reference from previous page (1:296). Possible chain linkage problem.

.......
repair_allow_data_loss is the minimum repair level for the errors found by DBCC CHECKDB (RepSource).
_

それをしましょう:

_    ALTER DATABASE [RepSource] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    GO
    DBCC CHECKDB (RepSource,repair_allow_data_loss)
    ALTER DATABASE [RepSource] SET MULTI_USER
OUTPUT    
Repair: The Clustered index successfully rebuilt for the object "dbo.RepUserTable" in database "RepSource".
    Repair: The page (1:296) has been deallocated from object ID 389576426, index ID 1, partition ID 72057594042056704, alloc unit ID 72057594046840832 (type In-row data).
    The error has been repaired.
    There are 9998 rows in 4999 pages for object "RepUserTable".
_

これが重要な情報です:修復:オブジェクトID 389576426からページ(1:296)の割り当てが解除されました

修復は、新しいページのセットにクラスター化インデックスを再構築することで機能し、単純に破損したページをスキップします。割り当てページとリンクされたページのすべての参照を修正します。再構築後は、ページが存在しなかったかのようになります。 (これはすべてログに記録されます。)ヘッダーを破損し、DBCC CHECKDBがページの内容を知らないため、行レベルでは修復できません。

結果:オブジェクト "RepUserTable"の4999ページに9998行あります。

1ページが消え、2レコードが消えました。ログリーダーエージェントは、そのページの行数を知る手掛かりがありません。そのため、これをサブスクライバーに複製する方法はわかりません。

ここで、ログエージェントを再起動します。レプリケーションが失敗しないことがわかります。

ソースにはレコード3と4はありませんが、サブスクライバーにはまだ存在しています。

_Select * from RepUserTable where ID IN (3,4)
/*
ID          TestString
----------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

(0 row(s) affected)
*/

--INSERT record into source table to show that replication is still working
INSERT INTO RepUserTable DEFAULT VALUES

Select MAX(ID) from RepUserTable
/*
ID
-----------
10000
*/
Select MAX(ID) as ID from RepDest.dbo.RepuserTable

/*
ID
-----------
10001
*/

Select * from RepDest.dbo.RepuserTable WHERE ID=3
/*
ID          TestString
----------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3           AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*/

--You now have Inconsistent data
_

ソースデータベースにレコード3と4を再度挿入すると、これらの行がサブスクライバーにレプリケートされるときにPK違反エラーが発生します。それはうまくいきません。そして、私はこれがあなたが「レプリケーションが機能しない」とはどういう意味かを仮定しています

しかし、もっと重要です。そうしなければ、何にも気付かないでしょう。しかし、サブスクライバーにはより多くのレコードがあります。

7
Edward Dortland

レプリケーションでは、パブリッシャーとサブスクライバーは初期化時に同期していると想定し、トランザクションログからの変更を適用してそれらを同期させます。トランザクションログをバイパスする何かを実行すると(私はDBCCがそうだと思います)、2つは同期が取れておらず、レプリケーションは幸いにも気づいていません。そのため、パブリッシャーで挿入を実行し、同じ主キーを持つデータがサブスクライバーに既に存在する状況になります。

エレンリプリーの言葉によれば、「サイト全体を軌道から核に入れます。これが確実にする唯一の方法です。」。つまり、データ損失が発生したテーブルを再初期化する必要があります。または、パブリッシャーにはないサブスクライバーに存在するデータを確認する演習を行い、そのデータをサブスクライバーから削除することもできます。以前に同様の演習を行ったことがあり、退屈な作業になる可能性があります。オプションを慎重に比較検討し、パスを選択してください。ゴッドスピード。

0
Ben Thul