并行执行CREATE DATABASE语句会导致错误,但不会导致单独的SQL Server实例

Ran*_*lph 7 c# sql-server entity-framework

我在我的应用程序中使用最新版本的Entity Framework(但我不认为EF是这里的问题,只是陈述我们正在使用的ORM)并拥有这种多租户架构.我正在做一些用C#构建的压力测试,其中它创建了X个并行运行的任务来做一些事情.在整个过程开始的某个时刻,它将为每个任务(在这种情况下为每个租户)创建一个新数据库,然后继续处理大部分操作.但是在某些任务上,它会在我尝试创建新数据库的代码的确切部分抛出2个SQL异常.

例外#1:

无法获得数据库'model'的独占锁定.稍后重试该操作.CREATE DATABASE失败.无法创建列出的某些文件名.检查相关错误.

例外#2:

超时已过期.操作完成之前经过的超时时间或服务器没有响应.

它是这两个中的任何一个并抛出我的代码的同一行(当EF创建数据库时).显然在SQL Server中,在创建数据库时,它一次只执行一次并锁定"模型"数据库(请参阅此处),因此某些正在等待的任务会引发超时或锁定"模型"错误.

这些测试是在我们的开发SQL Server 2014实例(12.0.4213)上完成的,如果我执行100个并行任务,那么某些任务就会抛出错误,有时甚至会执行近一半的任务.

但是这是所有这些中最令人不安的部分,当我在我的PC上本地安装的其他SQL服务器实例(12.0.2000)上测试时,没有这样的错误抛出并完全完成我执行的所有任务(甚至1000个任务)在平行下!).

到目前为止我尝试过的解决方案但没有奏效:

  • 将EF中的Object上下文的超时更改为无限
  • 尝试在连接字符串上添加更长或无限的超时
  • 尝试在EF上添加重试策略并使其更长并且更频繁地运行
  • 目前,尝试安装具有类似环境的虚拟机到我们的Dev服务器(使用Windows Server 2014 R2)并测试特定版本的SQL Server,以尝试查看版本是否与它有任何关系(是的,我是那个绝望:))

无论如何,是一个简单的C#控制台应用程序,您可以下载并尝试复制该问题.这个测试应用程序将执行您输入的N个任务,并简单地创建一个数据库并在之后进行清理.

DWr*_*ght 3

2 观察结果:

  1. 由于根本问题与并发性以及对“资源”的访问有关,该“资源”在关键点只允许单个访问器,但不允许并发访问器,因此在执行时可能会在两台不同的计算机上获得不同的结果也就不足为奇了负载下的高并发场景。此外,可能还涉及 SQL Server 引擎差异。所有这些都是尝试找出和调试并发问题的课程的标准,特别是涉及具有自己非常强大的并发概念的引擎。

  2. 当事情从经验上看不起作用时,为什么不通过设计来更清晰地处理问题来改变方法,而不是试图让某些事情发挥作用或充分解释情况,从而违背情况的本质?

    • 一种选择:通过某种并发同步机制来调节访问,承认 SQL Server 需要在模型数据库上拥有独占锁的现实——这听起来System.Threading.Monitor很适合这里发生的情况,并且它允许您控制那里发生的情况是一个超时,超时由您选择。这将有助于防止 SQL Server 端可能发生的那种锁定类型场景,这将是当前“超时”症状的解释(尽管压力负载可能是唯一的解释)。

    • 另一种选择:看看是否可以以根本不需要同步的方式进行设计。达到永远不会请求同时创建多个数据库的程度。某种类型的创建请求队列(并且该队列保证仅由一个线程提供服务),请求任务对创建结果执行异步/等待模式。

不管怎样,在压​​力测试下,你都会遇到这样的情况:压力测试会减慢速度,超强负载会导致故障。关键问题是:

  • 您的设计能否处理可能最坏情况负载的几倍并仍然显示出可接受的性能?
  • 如果确实发生故障,您对故障的响应是否以您设计的方式“控制”。