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