使用多线程从SQL Server中的单个表读取

时间:2012-06-02 00:43:29

标签: c# sql-server

使用多个线程解决SQL Server中单个表的读取的最佳方法,并确保不使用c#

在不同的线程中读取相同的记录两次

提前感谢您的帮助

4 个答案:

答案 0 :(得分:2)

您是否尝试并行读取表中的记录以加快数据的检索速度,或者您只是担心线程访问相同数据时数据损坏?

像MsSQL这样的数据库管理系统非常好地处理并发性,因此如果您有多个线程读取同一个表,那么在这方面的线程安全性不是您必须关注的。

如果要并行读取数据而不重叠,可以使用分页运行SQL命令,并让每个线程获取不同的页面。你可以说20个线程同时读取20个不同的页面,并且可以保证它们不会读取相同的行。然后你可以连接数据。页面大小越大,创建线程的性能就越高。

efficient way to implement paging

答案 1 :(得分:0)

假设依赖于SQL Server,您可能会查看SQL Server Service Broker功能以为您提供排队。需要注意的一点是,目前SQL Server Service Broker在SQL Azure上不可用,因此如果您计划迁移到Azure云可能会出现问题。

无论如何 - 使用SQL Server Service Broker,并发访问权限在数据库引擎层进行管理。另一种方法是让一个线程读取数据库,然后以消息作为输入调度线程。这比尝试在数据库中使用事务确保消息不会被读取两次稍微容易一些。

就像我说的那样,SQL Server Service Broker可能就是这样。或者适当的外部排队机制。

答案 2 :(得分:0)

解决方案1:
我假设您正在尝试处理或从大表中提取数据。如果我被分配了这个任务,我会先看看分页。如果你试图在线程之间分割工作。因此,线程1处理页面0到10,线程2处理页面11到20等...或者您可以使用实际的rownumber批处理行。所以在你的存储过程中你会这样做;

WITH result_set AS (
  SELECT
    ROW_NUMBER() OVER (ORDER BY <ordering>) AS [row_number],
    x, y, z
  FROM
    table
  WHERE
    <search-clauses>
) SELECT
  *
FROM
  result_set
WHERE
  [row_number] BETWEEN @IN_Thread_Row_Start AND @IN_Thread_Row_End;

另一个更有效的选择是,如果你有一个自然键,或者一个好的代理人,就是使用它进行翻页并让线程传入关键参数而不是它感兴趣的记录(或页码) )。

对此解决方案的直接关注将是:

  1. ROW_NUMBER表现
  2. CTE表现(我相信它们存储在内存中)
  3. 因此,如果这是我要解决的问题,我会通过密钥查看分页。

    解决方案2:
    第二种解决方案是在行处理时标记行,虚拟锁定它们,即如果您具有数据写入者权限。因此,您的表将有一个名为Processed或Locked的字段,因为您的线程选择了行,它们将更新为Locked = 1;

    然后,您从其他线程中选择仅选择未锁定的行。当您的流程完成并处理完所有行后,您可以重置锁定。

    很难说一些试验会有什么效果...... GL

答案 3 :(得分:0)

这个问题已经超级老了,但仍然非常相关,我花了很多时间找到这个解决方案,所以我认为id会发布给其他发生这种情况的人。当使用sql表作为队列而不是msmq时,这是很常见的。

解决方案(经过大量调查后)很简单,可以通过在ssms中打开2个选项卡进行测试,每个选项卡运行自己的事务来模拟多个进程/线程命中同一个表。

快速回答是这样的:关键是在你的选择上使用updlock和readpast提示。

为了说明没有重复的读取工作,请查看这个简单的例子。

- 在ssms的标签1上

begin tran
SELECT TOP 1 ordno FROM table_queue WITH (updlock, readpast) 

- 在ssms中的标签2上

begin tran
SELECT TOP 1 ordno FROM table_queue WITH (updlock, readpast) 

您会注意到第一个选定的记录已被锁定,并且不会被第二个选项卡/进程上的select语句复制。

现在在现实世界中你不会像上面的简单例子那样在你的桌子上执行选择。您将更新记录为&#34; isprocessing = 1&#34;或者类似的东西,如果你使用你的表作为队列。上面的代码只是证明了这允许并发读取而不重复。

因此在现实世界中(如果您将表用作队列并使用多个服务处理此队列),您可能会在子查询中执行select更新语句。

像这样。

begin tran
    update table_queue set processing= 1 where myId in
    (
        SELECT TOP 50  myId FROM table_queue WITH (updlock, readpast)
    )

commit tran

您还可以将yoru update语句与输出关键字结合使用,这样您就可以获得现在已锁定的所有ID的列表(处理= 1),以便您可以使用它们。

如果您使用表格作为队列处理数据,这将确保您不会在select语句中复制记录而无需分页或其他任何内容。

此解决方案正在企业级应用程序中进行测试,当我们在许多不同的盒子上运行的许多服务监视时,我们在select语句中经历了大量重复。