【问题标题】:Parallel execution of different DbContexts is slower than nonparallel version不同 DbContexts 的并行执行比非并行版本慢
【发布时间】:2015-12-01 07:57:12
【问题描述】:

我有一个包含 2 列的简单表格(一列是 identity,另一列是 char 列):

CREATE TABLE [dbo].[tbl]
(
    [id] [INT] IDENTITY(1,1) NOT NULL,
    [col] [CHAR](32) NULL,
    CONSTRAINT [PK_tbl] PRIMARY KEY CLUSTERED ([id] ASC)
)

我们有一个函数可以执行一些长时间运行的操作。这是一些伪代码:

void doWork()
{
    using(context)
    {
        doLongPart1(context);
        ...
        doLongPartN(context);
    }
}

现在我正在尝试使用各自的上下文将它们隔离在单独的任务中。但令人惊讶的是,有任务的版本比没有任务的版本要花更多的时间。我在这里插入10,000 行。时间是:~54000ms 没有任务的版本和~57000ms 有任务的版本。我正在使用EF6.0,这是要重现的完整代码:

初始版本

static void Main(string[] args)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    var c = 10000;

    var c1 = new TestEntities();
    for (int i = 1; i < c / 2; i++)
        c1.tbls.Add(new tbl { col = i.ToString() });

    c1.SaveChanges();

    var c2 = new TestEntities();
    for (int i = c / 2; i < c; i++)
        c2.tbls.Add(new tbl { col = i.ToString() });

    c2.SaveChanges();

    stopwatch.Stop();

    Console.WriteLine(stopwatch.ElapsedMilliseconds);
    Console.ReadLine();
}

带任务的版本

static void Main(string[] args)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    var c = 10000;
    Task[] tasks = new Task[2];

    tasks[0] = Task.Run(() =>
    {
        var c1 = new TestEntities();

        for (int i = 1; i < c / 2; i++)
            c1.tbls.Add(new tbl { col = i.ToString() });
        c1.SaveChanges();
    });

    tasks[1] = Task.Run(() =>
    {
        var c2 = new TestEntities();

        for (int i = c / 2; i < c; i++)
            c2.tbls.Add(new tbl { col = i.ToString() });
        c2.SaveChanges();
    });

    Task.WaitAll(tasks);

    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);

    Console.ReadLine();
}   

我也尝试过通过存储过程来做到这一点:

CREATE PROC spTbl @s CHAR(32)
AS
    INSERT INTO dbo.tbl (col)
    VALUES (@s)

和代码:

static void Main(string[] args)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    var c = 10000; 
    Task[] tasks = new Task[2];

    tasks[0] = Task.Run(() =>
    {
        var c1 = new TestEntities();

        for (int i = 1; i < c / 2; i++)
            c1.spTbl(i.ToString());
    });

    tasks[1] = Task.Run(() =>
    {
        var c2 = new TestEntities();

        for (int i = c / 2; i < c; i++)
            c2.spTbl(i.ToString());
    });

    Task.WaitAll(tasks);
}

我什至尝试过配置 SQL Server:

sp_configure 'show advanced options', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
sp_configure 'max degree of parallelism', 8;
GO
RECONFIGURE WITH OVERRIDE;
GO

但是对我没有任何作用。谁能指出我正确的方向?

【问题讨论】:

  • 连接到数据库的开销和/或表上的锁很可能与此有关。
  • @LasseV.Karlsen,我正在与 DBA 核对这一点,很快就会通知你。我也会检查选择。选择不应该给我带来不同吗?
  • 好的,事实证明我刚刚达到了我的 Sql 引擎的最大值,所以并行执行没有区别。两人都在以最大可能的速度工作。此外,在数据库级别根本没有锁..

标签: c# entity-framework sql-server-2012 task-parallel-library


【解决方案1】:

c# 程序不会使您的数据库更快。

如果您的问题是您在数据库中的插入操作的速度,那么使其并行不会解除您的瓶颈,它仍然是您的数据库上的插入操作。

您通常可以看到两 (3) 件事

1) 数据库服务器中更快的驱动器/SSD(和或更多内存)

2) 减少索引的数量,因为这些都必须在插入操作中更新

对于某些操作,您甚至可以删除索引,插入您需要的所有数据,然后重新创建索引,因为您的索引只会在每次插入行时更新一次

更新:现在我更详细地查看了您的数字,1000 次查询/秒在我见过的应用程序中并不少见。

更新 2: 您可能会考虑的一种可能的解决方案是批量插入 https://efbulkinsert.codeplex.com/,因为即使使用存储过程,您的开销也在于查询数量而不是查询(假设您不这样做) t 有索引和计算列)

【讨论】:

  • 现在我意识到您实际上是正确的。在这种情况下,我达到了极限,并行性没有给我任何帮助。这只是一个测试,但在实际情况下它应该给我带来好处,因为有插入选择、计算、detetes 等。
猜你喜欢
  • 2016-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多