The*_*war 2 sql-server dbcc transactional-replication
我们在 SQL Server 2008 数据库上启用了一种方式的事务复制。由于一些问题,我们不得不运行DBCC checkDB
,将数据库带入单用户模式。当我们完成DBCC
活动并再次启用复制时,复制将不起作用。我们必须再次初始化快照才能使复制工作。
你能帮我理解为什么复制在这种情况下会中断吗?我理解DBCC
删除表中的一些行和索引,但根据我的理解,当我们启用复制时,这些更改应该传播给订阅者而不会出现任何问题。
当我与我的高级团队成员核对时,我被告知这是由于 LSN 不匹配,但我无法从逻辑上理解。
任何帮助深表感谢。
缺少 3 条重要的信息来准确指出您的特定场景中出了什么问题:
CHECKDB
。如果您在复制环境中遇到数据库损坏并且您不知道如何准确解释修复结果或者如果您的出版物不是很大,我的建议是始终重新初始化(如果可能)或删除/重新创建出版物订阅.
原因是 由 执行的大多数修复操作DBCC CHECKDB
不会复制到订阅者。(请参阅稍后的演示)如果您有非常大的发布并且您确切地知道如何读取DBCC CHECKDB
输出,您可能能够手动修复表或行上的内容等级。
发布数据库损坏后复制失败的主要原因
事务日志损坏
元数据损坏
用户数据损坏
一般设置
我正在使用以下脚本在同一台服务器上设置发布者、分发者、订阅者,创建源和目标数据库以及具有 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)
Run Code Online (Sandbox Code Playgroud)
设置脚本:
--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
Run Code Online (Sandbox Code Playgroud)
运行脚本后,启动快照代理,你应该完成。
事务日志损坏
当您遇到事务日志损坏并DBCC CHECKDB (RepSource,REPAIR_ALLOW_DATA_LOSS)
在紧急模式下使用重建日志文件时,您将看到发布和订阅都消失了。你必须重新创建这些。
确认您的数据库现在已损坏:
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.
Run Code Online (Sandbox Code Playgroud)
要解决此问题(无需备份/恢复),您必须执行紧急修复:
ALTER DATABASE [RepSource] SET EMERGENCY
ALTER DATABASE [RepSource] SET SINGLE_USER
DBCC CHECKDB (RepSource,repair_allow_data_loss)
ALTER DATABASE [RepSource] SET MULTI_USER
Run Code Online (Sandbox Code Playgroud)
输出:文件激活失败。物理文件名“e:\SQLServer\Log\RepSource_log.ldf”可能不正确。无法重建日志,因为在数据库关闭时有打开的事务/用户,数据库没有发生检查点,或者数据库是只读的。如果由于硬件或环境故障手动删除或丢失事务日志文件,则可能会发生此错误。警告:数据库“RepSource”的日志已重建。事务一致性已丢失。RESTORE 链被破坏,服务器不再拥有先前日志文件的上下文,因此您需要知道它们是什么。您应该运行 DBCC CHECKDB 来验证物理一致性。数据库已置于 dbo-only 模式。当您准备好使数据库可供使用时,
现在,如果您查看输出,您会明白为什么事务复制不再起作用:
警告:数据库“RepSource”的日志已重建。事务一致性已丢失。RESTORE 链被破坏,服务器不再拥有先前日志文件的上下文,因此您需要知道它们是什么。
事务复制基于事务日志,破坏 LSN 链会破坏复制。
如果您查看您的出版物和订阅,您会发现它们都消失了。您必须从头开始重新创建它们。
注意:删除日志文件是相当大的一步。我没有任何工具只能破坏事务日志文件中的单个日志记录。我不确定在这种情况下是否也会删除发布和订阅。
元数据损坏
配置复制时,会创建多个系统表来保存复制工作所需的数据。所谓的元数据。在主数据库、MSDB、分发数据库、发布数据库和订阅数据库中创建了表。
就我个人而言,我没有这种损坏的经验,但如果您的发布数据库中的这些系统表中的任何一个被损坏,您将不得不重新设置复制。我只是奇怪地测试了它,我能够破坏 syspublications、sysarticles、sysarticleupdates 和复制继续进行。??
用户数据损坏
现在这就是它变得有趣的地方。因为在您不知情的情况下,发布者和订阅者之间可能会出现不一致。
原因是,如果您使用DBCC REPAIR_ALLOW_DATA_LOSS来“修复”某些东西,那么它在物理层面上是固定的。事务复制与读取发布数据库日志文件中的所有事务的日志读取器一起使用。如果事务被标记为复制,则在订阅者上执行的逻辑复制语句中读取和翻译它。它可以处理 INSERT、DELETE 等语句。但它不能处理修复语句,例如数据页被释放。并不是没有记录修复。这是!只是日志阅读器不知道如何处理它们。
下面是一个例子:
让我们找出哪些页面用于我们使用前面的脚本在 RepSource 数据库中创建的 ReUserTable:
--create user data corruption of usertable
use [RepSource]
Go
--find the data pages of RepUserTable
DBCC IND ('RepSource','RepUserTable',1)
Run Code Online (Sandbox Code Playgroud)
输出:
我们将以第 296 页为例。这是一个数据页(Pagetype=1)。让我们看看这个页面上有哪 2 条记录。(每条记录为 4015 字节,因此每页 2 条记录。)
DBCC TRACEON(3604)
DBCC PAGE('RepSource',1,296,3)
Run Code Online (Sandbox Code Playgroud)
输出
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
Run Code Online (Sandbox Code Playgroud)
所以在这个页面上,我们有 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.
Run Code Online (Sandbox Code Playgroud)
由于这是聚集索引的损坏,我只能用 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).
Run Code Online (Sandbox Code Playgroud)
让我们这样做:
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".
Run Code Online (Sandbox Code Playgroud)
现在是重要信息: 修复:页面 (1:296) 已从对象 ID 389576426 中释放
修复的工作原理是在一组新页面上重建聚集索引,它只是跳过损坏的页面。它修复了分配页和链接页中的所有引用。重建后,就好像该页面从未存在过一样。(这已全部记录。)修复无法在行级别进行,因为我损坏了标题,而 DBCC CHECKDB 根本不知道页面上有什么。
结果: 对象“RepUserTable”在 4999 页中有 9998 行。
1 页消失,2 条记录消失。logreader 代理不知道该页面上有多少行。所以它不知道如何将其复制到订阅者。
如果您现在重新启动日志代理。你会看到复制不会失败.. 但是:
在源上,我们没有记录 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
Run Code Online (Sandbox Code Playgroud)
如果您现在在源数据库上重新插入记录 3 和 4,当这些行被复制到订阅者时,您将收到 PK 违规错误。那行不通。我假设这就是您所说的“复制不起作用”的意思
但更重要的是。如果你不这样做,你就不会注意到任何事情。但是你会有更多关于订阅者的记录。