AlwaysOn 可用性组中的数据库恢复为普通数据库

Posted

技术标签:

【中文标题】AlwaysOn 可用性组中的数据库恢复为普通数据库【英文标题】:Database from AlwaysOn availability group restored as normal database 【发布时间】:2018-06-10 21:33:45 【问题描述】:

我在从 SQL Server 2017 中 AlwaysOn 高可用性组中的数据库集还原的普通 SQL 数据库时遇到问题。

我将生产数据库的副本恢复到另一台服务器,用作 QA 测试数据库,但名称也不同 - MyDB_demo

问题是,QA 应用程序副本(与具有新开发增强功能的生产代码相同)有时会出错。

即使我的 conn str 指向 MyDB_demo,我也会收到以下错误

[SqlException (0x80131904):目标数据库 ('MyDB') 位于可用性组中,当应用程序意图设置为只读时,当前可访问连接。有关应用程序意图的详细信息,请参阅 SQL Server 联机丛书。]

System.Data.SqlClient.SqlConnection.OnError(SqlException 异常,布尔型 breakConnection,Action 1 wrapCloseInAction)+2444190 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException 异常,布尔型 breakConnection,Action 1 wrapCloseInAction)+5775712 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) +285 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) +4169 System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() +58 System.Data.SqlClient.SqlDataReader.get_MetaData() +89 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption) +409 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,Boolean async,Int32 超时,Task& 任务,Boolean asyncWrite,Boolean inRetry,SqlDataReader ds,Boolean describeParameterEncryptionRequest)+2127 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String 方法, TaskCompletionSource`1 完成, Int32 超时, Task& 任务, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry) +911 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String 方法) +64 System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior行为,字符串方法)+240 System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior 行为)+41 System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior 行为)+12 System.Data.Common.DbDataAdapter.FillInternal(DataSet 数据集,DataTable[] 数据表,Int32 startRecord,Int32 maxRecords,String srcTable,IDbCommand 命令,CommandBehavior 行为)+139 System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior 行为) +136 System.Data.Common.DbDataAdapter.Fill(DataSet 数据集) +88 MyApp.SqlHelper.ExecuteDataset(SqlConnection connection, CommandType commandType, String commandText, SqlParameter[] commandParameters) +163 MyApp.PermitFunctions.GetSystemMessages(String sp, Int32 iPermitID, Int32 iAppID, SqlConnection cn) +219 MyApp.Municipality.LoadSystemMessage() +3869 MyApp.Municipality.Page_Load(Object sender, EventArgs e) +101 System.Web.UI.Control.OnLoad(EventArgs e) +95 System.Web.UI.Control.LoadRecursive() +59 System.Web.UI.Control.LoadRecursive() +131 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +678

在新恢复的数据库(现在命名为MyDB_demo)中是否有任何引用存储了生产数据库的原始名称,为什么它试图访问它?

欢迎提出任何建议。

编辑

实际上,用于恢复MyDB_demo的服务器是AlwasyOn可用性组的辅助节点之一;它还包含生产数据库的 RO 副本,MyDB

所以服务器有:

生产数据库的 RO 副本 (MyDB) 为 QA 恢复了正常的独立数据库 - MyDB_demo

因此,我理解错误消息 - 如果我尝试从连接字符串直接访问生产数据库的辅助 RO 副本,这将是有意义的。

但我没有:连接字符串(我仔细检查过)正在尝试连接到 QA 数据库 MyDB_demo。

这里有一些附加信息:

SQLHelper 类中引发错误,该类是MS 的帮助类,用于在ExecuteDataset 函数中使用SQL Server 该错误仅在一个存储过程中引发 - 许多其他存储过程以及直接 SQL 语句运行良好 我检查了存储过程,认为它可能意外包含对 DB 名称的硬编码引用 - 它没有 还有奇怪的部分 - 我使用与在 SSMS 中的应用程序调用相同的参数运行存储过程 - 它运行得很好 - 没有错误

所以看起来连接字符串可能会以某种方式被 NET 应用程序本身更改(!!!),并且仅适用于这个存储过程?

有人遇到过这样的事情吗?

谢谢

【问题讨论】:

【参考方案1】:

由于奇怪的(阅读:愚蠢)情况,罪魁祸首 SP 仅在从应用程序内调用时失败,但在 SSMS 中尝试时运行正常,我尝试了一种“愚蠢”的方法:我检查了它的代码,注释掉了两个字段设置了像select top 1 from ..... where.... 这样的子选择(实际上我用虚拟值替换了它的值),我更改了一个最初指定为"InspectionType" DescOrder By 字段,我从中删除了引号.

执行此操作后,即使从应用程序调用,SP 也会突然开始正常工作。 然后我将所有更改恢复为原始(添加引用并放回子选择)并且 SP 继续正常工作。

所以...问题解决了。 愚蠢问题的愚蠢方法(!?!?!)

无论如何,如果有人对可能发生的事情有更好的想法或解释,我很高兴听到它


编辑

我想我理解修复。 通过编辑和保存存储过程,它的查询计划被重新编译。 所以最初的错误可能是由旧的查询计划引起的。 但是为什么它引用了数据库名称呢?查询计划中是否引用了实际的数据库名称?这对我来说有点奇怪。

还有一个问题(未决):

SQL Server 优化器是否检测数据库是否在高可用性模式下运行,并且在优化查询时,它是否决定查询是否为只读模式并自动将其重定向到只读节点?即使连接字符串中不存在 ApplicationIntent 只读参数? 因为它不是在这种情况下,即使在生产中也是如此 - 我们只是实现了 AlwaysOn 功能,并且正在更新应用程序以利用 R/O 节点。

感谢任何cmets

【讨论】:

以上是关于AlwaysOn 可用性组中的数据库恢复为普通数据库的主要内容,如果未能解决你的问题,请参考以下文章

将数据库文件 (mdf) 添加到 AlwaysOn 可用性组中的数据库

SQL Server 2017 AlwaysOn AG 自动初始化

SQL Server AlwaysOn架构及原理

Always On 可用性组中的 MSDTC

SQL Server AlwaysOn部署

SQL 2014 AlwaysOn 搭建