【问题标题】:Sql Server Transient Exception NumbersSql Server 瞬态异常号
【发布时间】:2016-02-25 14:58:17
【问题描述】:

我想为我的数据库调用编写一些包装代码(使用 C# 和用于访问数据库的 Microsoft 技术),自动重试“瞬态”异常。暂时的,我的意思是很有可能最终会解决的事情(与永远不会起作用的逻辑错误相反)。我能想到的例子包括:

  • 死锁
  • 连接超时
  • 命令超时

我曾计划使用 SqlException 的错误号来发现这些。比如:

List<RunStoredProcedureResultType> resultSet = null;
int limit = 3;
for (int i = 0; i < limit; ++i)
{
    bool isLast = i == limit - 1;
    try
    {
        using (var db = /* ... */)
        {
            resultSet = db.RunStoredProcedure(param1, param2).ToList();
        }
        //if it gets here it was successful
        break;
    }
    catch (SqlException ex)
    {
        if (isLast)
        {
            //3 transient errors in a row. So just kill it
            throw;
        }
        switch (ex.Number)
        {
            case 1205: //deadlock
            case -2:   //timeout (command timeout?)
            case 11:   //timeout (connection timeout?)
                // do nothing - continue the loop
                break;
            default:
                //a non-transient error. Just throw the exception on
                throw;
        }
    }
    Thread.Sleep(TimeSpan.FromSeconds(1)); //some kind of delay - might not use Sleep
}
return resultSet;

(请原谅我的任何错误 - 我刚刚写了这个。我也意识到我可以很好地总结它......)

所以关键问题是:我应该将哪些数字视为“瞬态”(我意识到我认为瞬态的数字可能与其他人认为的瞬态不同)。我在这里找到了一个不错的列表:

https://msdn.microsoft.com/en-us/library/cc645603.aspx

但它的海量和注释非常有用。 有没有其他人建立了一个他们用于类似事情的列表?

更新

最后,我们选择了“错误列表”——如果错误是已知“非暂时性错误”列表中的一个——通常是程序员错误。我已经包含了我们用作答案的数字列表。

【问题讨论】:

  • 我们做了类似的事情。称之为“可恢复的异常”。包括连接错误、超时和死锁。但是:当您重复三次调用时,死锁可能会持续存在 - 考虑添加可变延迟或其他死锁解决方法。当您立即重试两次时,由于过载而导致的连接超时也可能会变得更糟。
  • 哦,是的,我计划延迟。谢谢@dlatikay - 将更新上述内容
  • 您好,您正在寻求建议,不幸的是,这不是一个可解决的问题,因为任何程序员都可能对什么对您的案例有用有不同的看法,所以按照目前的编辑方式,它是无关;问候。
  • 一个想法是按严重程度进行分类,请参见此处:technet.microsoft.com/en-us/library/aa937483(v=sql.80).aspx 但不幸的是,资源相关错误的异常类将“磁盘空间不足”和“锁定”等内容合二为一等级。也在这里确认stackoverflow.com/questions/9405648/…
  • @jclozano 不会完全同意。可恢复错误列表实际上应该是确定性的。微软只走了一半。他们将错误分类为“警告”、“用户可以纠正的错误”和“与资源相关的错误”等级别,但不是“通过重复/定时/修改调用上下文可恢复”与“永久错误”

标签: c# sql-server


【解决方案1】:

sql Azure 中有一个类[SqlDatabaseTransientErrorDetectionStrategy.cs] 用于瞬态故障处理。它涵盖了几乎所有类型的可以被视为瞬态的异常代码。它也是Retry strategy 的完整实现。

在此处添加 sn-p 以供将来参考:

/// <summary>
/// Error codes reported by the DBNETLIB module.
/// </summary>
private enum ProcessNetLibErrorCode
{
    ZeroBytes = -3,

    Timeout = -2,
    /* Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. */

    Unknown = -1,

    InsufficientMemory = 1,

    AccessDenied = 2,

    ConnectionBusy = 3,

    ConnectionBroken = 4,

    ConnectionLimit = 5,

    ServerNotFound = 6,

    NetworkNotFound = 7,

    InsufficientResources = 8,

    NetworkBusy = 9,

    NetworkAccessDenied = 10,

    GeneralError = 11,

    IncorrectMode = 12,

    NameNotFound = 13,

    InvalidConnection = 14,

    ReadWriteError = 15,

    TooManyHandles = 16,

    ServerError = 17,

    SSLError = 18,

    EncryptionError = 19,

    EncryptionNotSupported = 20
}

还有一个switch case来检查sql异常中是否返回错误号:

switch (err.Number)
{
    // SQL Error Code: 40501
    // The service is currently busy. Retry the request after 10 seconds. Code: (reason code to be decoded).
    case ThrottlingCondition.ThrottlingErrorNumber:
        // Decode the reason code from the error message to determine the grounds for throttling.
        var condition = ThrottlingCondition.FromError(err);

        // Attach the decoded values as additional attributes to the original SQL exception.
        sqlException.Data[condition.ThrottlingMode.GetType().Name] =
            condition.ThrottlingMode.ToString();
        sqlException.Data[condition.GetType().Name] = condition;

        return true;

    // SQL Error Code: 10928
    // Resource ID: %d. The %s limit for the database is %d and has been reached.
    case 10928:
    // SQL Error Code: 10929
    // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. 
    // However, the server is currently too busy to support requests greater than %d for this database.
    case 10929:
    // SQL Error Code: 10053
    // A transport-level error has occurred when receiving results from the server.
    // An established connection was aborted by the software in your host machine.
    case 10053:
    // SQL Error Code: 10054
    // A transport-level error has occurred when sending the request to the server. 
    // (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
    case 10054:
    // SQL Error Code: 10060
    // 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: TCP Provider, error: 0 - A connection attempt failed 
    // because the connected party did not properly respond after a period of time, or established connection failed 
    // because connected host has failed to respond.)"}
    case 10060:
    // SQL Error Code: 40197
    // The service has encountered an error processing your request. Please try again.
    case 40197:
    // SQL Error Code: 40540
    // The service has encountered an error processing your request. Please try again.
    case 40540:
    // SQL Error Code: 40613
    // Database XXXX on server YYYY is not currently available. Please retry the connection later. If the problem persists, contact customer 
    // support, and provide them the session tracing ID of ZZZZZ.
    case 40613:
    // SQL Error Code: 40143
    // The service has encountered an error processing your request. Please try again.
    case 40143:
    // SQL Error Code: 233
    // The client was unable to establish a connection because of an error during connection initialization process before login. 
    // Possible causes include the following: the client tried to connect to an unsupported version of SQL Server; the server was too busy 
    // to accept new connections; or there was a resource limitation (insufficient memory or maximum allowed connections) on the server. 
    // (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
    case 233:
    // SQL Error Code: 64
    // A connection was successfully established with the server, but then an error occurred during the login process. 
    // (provider: TCP Provider, error: 0 - The specified network name is no longer available.) 
    case 64:
    // DBNETLIB Error Code: 20
    // The instance of SQL Server you attempted to connect to does not support encryption.
    case (int)ProcessNetLibErrorCode.EncryptionNotSupported:
        return true;
}

查看完整的source here。

【讨论】:

  • 这很好。只是添加一个说明,这个列表仍然不完整。 EF 和其他人一样对此一无所知。他们忘记了物品。示例:快照隔离写入冲突。此外,我没有在该列表上看到死锁,这意味着该列表的价值非常可疑。
  • 谢谢@vendettamit - 看起来不错,但正如 usr 提到的,它错过了僵局的事实让我感到有点怀疑,因为这可能是我有兴趣重试的主要因素之一。我想也许这不是 EF 可以决定自动重试的东西吗?
  • @vendettamit - 您能否通过提及 usr 在他的 cmets 中缺少的错误来更新您的答案?这足以成为我认为的答案
  • 您运行@usr 在sql server 上提供的查询并获得您感兴趣的所需代码。例如您想要包含的死锁代码可以过滤为SELECT * FROM sys.messages WHERE language_id = 1033 And text LIKE '%deadlock%'
【解决方案2】:

很抱歉回答我自己的问题,但如果有人仍然感兴趣,我们刚刚开始建立自己的错误代码列表。不理想,但我们认为这种情况不应该经常发生。

我们选择了“坏列表”方法,而不是问题中暗示的“好列表”。到目前为止,我们拥有的 id 是:

PARAMETER_NOT_SUPPLIED = 201;
CANNOT_INSERT_NULL_INTO_NON_NULL = 515;
FOREGIN_KEY_VIOLATION = 547;
PRIMARY_KEY_VIOLATION = 2627;
MEMORY_ALLOCATION_FAILED = 4846;
ERROR_CONVERTING_NUMERIC_TO_DECIMAL = 8114; 
TOO_MANY_ARGUMENTS = 8144;
ARGUMENT_IS_NOT_A_PARAMETER = 8145;
ARGS_SUPPLIED_FOR_PROCEDURE_WITHOUT_PARAMETERS = 8146;
STRING_OR_BINARY_TRUNCATED = 8152;
INVALID_POINTER = 10006;
WRONG_NUMBER_OF_PARAMETERS = 18751;

我们注意到的另一件事是,如果连接池超时,您不会收到 SqlException,而是会收到 InvalidOperationException 报告“超时已过期”。遗憾的是它不是 SqlException,但值得捕获。

我会尽量保持最新,添加任何内容。

【讨论】:

    【解决方案3】:

    没有可重试代码的规范列表。其他团队之前也遇到过这个问题。 EF 团队制定了重试策略。您可能想要突袭他们的代码。但这份清单并不完整。我在 GitHub 上看到了 EF 提交,他们修改了列表。

    我也有这个问题。我添加了一些从SELECT * FROM sys.messages WHERE language_id = 1033 AND text LIKE '%...%' 挖出的明显错误代码。然后,我在应用遇到它们时添加了代码。

    您还需要针对超时和网络错误重试特殊错误号。服务器无法生成该号码,因为连接已断开。我认为这个数字是 -2,但你需要确定。

    SQL Server 定义的错误级别对此没有用处(而且大多数情况下)。

    【讨论】:

      【解决方案4】:

      我们正在使用 vendettamit 的列表,并在我们偶然发现另一个我们确定为暂时性的错误代码时继续扩展它。重要的是要注意瞬态的定义是“值得重试,也许下次会起作用”,而不是 SQL Server 问题。到目前为止,我们已经添加了以下代码:

      • 53:建立与服务器的连接时出错。
      • 109:管道已结束(仅当您通过命名管道连接到本地 SQL Server 时才需要)
      • 11004:无法连接(通常表示客户端网络尚未准备好)
      • 17142:服务器已暂停(便于在 SQL 服务器“消失”一会儿时测试行为)

      当您知道您的连接字符串很好(即因为您最近设法建立了连接)时,您可以添加以下代码。同样,您应该不使用这些来重试建立初始连接,如果您的连接字符串中有错误的参数,这些也会弹出。

      • 11001:找不到主机(可能会出现在网络更改上)
      • 1326: 错误的用户名或密码(我们已经看到在虚拟机上拉虚拟以太网电缆时弹出此问题)
      • 258:未找到服务器(无 TCP 应答)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-24
        • 1970-01-01
        • 1970-01-01
        • 2017-04-29
        • 1970-01-01
        • 2018-03-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多