有效的方式对sql server进行多线程调用?

时间:2010-12-16 19:34:05

标签: c# sql-server multithreading

我有一个sql存储过程,它将从一个像队列一样运行的表调用TOP 1000记录 - 在这个表中会有或多或少30,000-40,000条记录。对SP的调用需要大约4秒(这里有)一个xml列)所以要完成调用需要大约2分钟。 我想使用多线程调用并将记录插入同步字典\列表。 之前有人做过吗?任何有效的方式尽快结束通话? 谢谢......

6 个答案:

答案 0 :(得分:6)

考虑在求助线程之前优化查询。

根据我的经验,当多线程的初学者实现线程时,它通常不会提高性能。更糟糕的是,它通常会引入难以调试的细微错误。

首先优化查询,您可能会发现不需要线程。

即使你实现了它们,最终你会让SQL Server做太多工作,而线程请求只需要等待。

答案 1 :(得分:3)

基本错误是希望从多个线程插入数据库并使用连接,锁定过载服务器,并最终使其陷入困境。

如果您正在阅读数据,如果您发现查询速度更快,并且可以同时获取尽可能多的数据,那么您将会做得更好。

对我而言,似乎你的问题在其层面上无法解决 - 也许如果你详细说明你想做什么,你会得到更好的建议。

编辑:

我确实使用SQL作为队列一次 - 我记得 - 要出列,你必须使用第一个查询的结果来获取第二个的输入,所以线程是不可能的。或者,您必须在数据库中标记排队数据“已完成”,并且您的READ将变为UPDATE - >导致锁定。

如果您正在阅读,并且想要尽快做出反应,您可以使用DataReader,然后读取所有数据,并将处理组织成线程 - 读取100条记录,分叉线程并将其传递给它。 ..然后下一个记录等等。这样您就可以平衡资源使用情况。

答案 2 :(得分:1)

尝试使用DataReader异步读取数据;获取可以唯一标识数据库中的行的列.Populate Queue以保存返回的数据值(自定义对象)并运行工作线程以对队列执行任务。

你必须决定 应该实现多少工作线程 来执行任务,因为线程有自己的开销,如果没有正确实现可能是一场噩梦。

答案 3 :(得分:0)

目前尚不清楚您是选择1000个最近添加的行,还是选择特定列中值最高的1000行,也不清楚您的行是否可变 - 即行可能符合条件昨天排名前1000,但随后得到更新,以便今天不再符合资格。但是如果单个行不可变,则可以为TOP1000设置单独的表,并且当插入第1001行时,插入后触发器会将第1001行(但是您确定该行)移动到HISTORY表。这将使选择几乎瞬间完成:从TOP1000中选择*。当您需要查询TOP1000和HISTORY时,您只需将两个表与UNION组合,就好像它们是一个表一样。或者不是触发器,而是可以在事务中包装插入和第1001行删除。

不同的蠕虫,如果行变异,可以移入和移出前1000名。

答案 4 :(得分:0)

如果您真的需要,可以启动BGWorkers,它们单独连接到服务器并报告其进度。

我为一个精心设计的导出/导入应用程序做了同样的事情,以移动大约50GB的数据(4GB deflatestream'),除了我只使用BGWorker连续完成工作,而不是同时执行,而不会锁定UI线程。

答案 5 :(得分:0)

public struct BillingData
{
    public int CustomerTrackID, CustomerID;
    public DateTime BillingDate;
}

public Queue<BillingData> customerQueue = new Queue<BillingData>();
volatile static int ThreadProcessCount = 0;
readonly static object threadprocesslock = new object();
readonly static object queuelock = new object();
readonly static object countlock = new object();
AsyncCallback asyncCallback

// Pulling the Data Aync from the Database
private void StartProcess()
{
  SqlCommand command = SQLHelper.GetCommand("GetRecordsByBillingTrackID");
  command.Connection = SQLHelper.GetConnection("Con");SQLHelper.DeriveParameters(command);
  command.Parameters["@TrackID"].Value = trackid;
  asyncCallback = new AsyncCallback(FetchData);
  command.BeginExecuteXmlReader(asyncCallback, command);
}

public void FetchData(IAsyncResult c1)
    {
        SqlCommand comm1 = (SqlCommand)c1.AsyncState;
        System.Xml.XmlReader xr = comm1.EndExecuteXmlReader(c1);
        xr.Read();
        string data = "";
        while (!xr.EOF)
        {
        data = xr.ReadOuterXml();
        XmlDocument dom = new XmlDocument();
        dom.LoadXml("<data>" + data + "</data>");
        BillingData billingData;
        billingData.CustomerTrackID = Convert.ToInt32(dom.FirstChild.ChildNodes[0].Attributes["CustomerTrackID"].Value);
        billingData.CustomerID = Convert.ToInt32(dom.FirstChild.ChildNodes[0].Attributes["CustomerID"].Value);
        billingData.BillingDate = Convert.ToDateTime(dom.FirstChild.ChildNodes[0].Attributes["BillingDate"].Value);
lock (queuelock)
{
   if (!customerQueue.Contains(billingData))
   {
     customerQueue.Enqueue(billingData);
   }
}
   AssignThreadProcessToTheCustomer();


}



xr.Close();



}



// Assign the Threads based on the data pulled
private void AssignThreadProcessToTheCustomer()
{
int TotalProcessThreads =  5;
int TotalCustomersPerThread = 5;
if (ThreadProcessCount < TotalProcessThreads)
{
int ThreadsNeeded = (customerQueue.Count % TotalCustomersPerThread == 0) ?    (customerQueue.Count / TotalCustomersPerThread) : (customerQueue.Count / TotalCustomersPerThread + 1);
int count = 0;
 if (ThreadsNeeded > ThreadProcessCount)
{
 count = ThreadsNeeded - ThreadProcessCount;
 if ((count + ThreadProcessCount) > TotalProcessThreads)
    count = TotalProcessThreads - ThreadProcessCount;
}
for (int i = 0; i < count; i++)
{
ThreadProcess objThreadProcess = new ThreadProcess(this);
ThreadPool.QueueUserWorkItem(objThreadProcess.BillingEngineThreadPoolCallBack, count);
lock (threadprocesslock)
{
 ThreadProcessCount++;
}
}

public void BillingEngineThreadPoolCallBack(object threadContext)
{
BillingData? billingData = null;
while (true)
{
   lock (queuelock)
   {
      billingData = ProcessCustomerQueue();
   }
 if (billingData != null)
{
 StartBilling(billingData.Value);
}
else
 break;

More....
}