繁体   English   中英

SQL Timeout在不应该的时候过期

[英]SQL Timeout Expired When It Shouldn't

我正在使用SqlConnection类并遇到命令超时到期的问题。

首先,我使用SqlCommand属性来设置命令超时,如下所示:

command.CommandTimeout = 300;

此外,我已确保将执行超时设置设置为0,以确保SQL管理方面不会有超时。

这是我的代码:

using (SqlConnection conn = new SqlConnection(connection))
            {
                conn.Open();

                SqlCommand command = conn.CreateCommand();

                var transaction = conn.BeginTransaction("CourseLookupTransaction");

                command.Connection = conn;
                command.Transaction = transaction;
                command.CommandTimeout = 300;

                try
                {
                    command.CommandText = "TRUNCATE TABLE courses";
                    command.ExecuteNonQuery();

                    List<Course> courses = CourseHelper.GetAllCourses();

                    foreach (Course course in courses)
                    {
                        CourseHelper.InsertCourseLookupRecord(course);
                    }

                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    Log.Error(string.Format("Unable to reload course lookup table: {0}", ex.Message));
                }
            }

我已经设置了日志记录并且可以在启动此函数后正好验证30秒,我在堆栈跟踪中收到以下错误消息:

Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.

为了完全公开:在上面的using语句foreach中找到InsertCourseLookupRecord() ,正在对同一个数据库中的同一个表执行另一个查询。 这是它正在执行的查询:

INSERT INTO courses(courseid, contentid, name, code, description, url, metakeywords, metadescription)
VALUES(@courseid, @contentid, @name, @code, @description, @url, @metakeywords, @metadescription)"

此表中有超过1400条记录。

我将证明任何帮助我解决这个问题的个人是最高级的伟大巫师。

我相信发生的事情是你有一个死锁情况导致你在InsertCourseLookupRecord()函数中的查询失败。 您没有将连接传递给InsertCourseLookupRecord()所以我假设您在单独的连接中运行它。 那么会发生什么:

  • 你开始了一项交易。
  • 你截断表。
  • InsertCourseLookupRecord启动另一个连接并尝试将数据插入该表,但该表已锁定,因为您的事务尚未提交。
  • 函数InsertCourseLookupRecord()中的连接超时为该连接定义的超时值30秒。

您可以更改函数以接受命令对象作为参数,并在函数内部使用它而不是创建新连接。 这将成为交易的一部分,并将全部一起提交。

要执行此操作,请将函数定义更改为:

public static int InsertCourseLookupRecord(string course, SqlCommand cmd)

从函数中取出所有连接代码,因为您将使用cmd对象。 然后,当您准备好执行查询时:

myCommand.Parameters.Clear();  //need only if you're using command parameters
cmd.CommandText = "INSERT BLAH BLAH BLAH";
cmd.ExecuteNonQuery();

它将在相同的连接和事务上下文中运行。

你在你的使用块中这样称呼它:

CourseHelper.InsertCourseLookupRecord(course, command);

您也可以只使用InsertCourseLookupRecord中的代码并将其放在for循环中,然后在using块中重用命令对象,而无需将其传递给函数。

因为您使用了两个单独的SqlConnection对象,所以由于您在外部代码中启动的SqlTransaction而导致自我死锁。 InsertCourseLookupReacord的查询以及GetAllCourses的查询被尚未提交的TRUNCATE TABLE courses调用阻止。 他们等待300秒,然后提交截断。

你有几个选择。

  1. 将SqlConnection和SqlTransaction传递给GetAllCoursesInsertCourseLookupRecord以便它们可以成为同一事务的一部分。
  2. 通过删除SqlTransaction并使用System.Transaction.TransactionScope来使用“环境事务”。 这会导致打开到服务器的所有连接都共享一个事务。 这可能会导致维护问题,具体取决于查询正在执行的操作,因为它可能需要调用可能在某些计算机上禁用的分布式事务处理协调器 (从您显示的内容看起来需要DTC,因为您有两个打开的连接同时)。

最好的选择是尝试更改代码以执行选项1,但如果您不能执行选项2。

取自文档

对上下文连接执行命令时,CommandTimeout无效(在连接字符串中使用“context connection = true”打开SqlConnection)

请检查您的连接字符串,这是我能想到的唯一可能性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM