繁体   English   中英

如何从 LINQ-to-SQL 大型数据集更新中获得更好的性能

[英]How to get better performance from LINQ-to-SQL large dataset update

我有一个 c# 控制台应用程序,它正在更新一个包含大约 320,000 条记录的数据库。 基本上它是在循环中加密每条记录中的密码,然后调用 DatabaseContext.SubmitChanges()。 代码的“更新”部分大约需要 20 秒。 我不得不 CTRL-C 应用程序,因为执行“SubmitChanges”部分需要 15 多分钟:这是时间敏感系统的一部分,不应关闭超过几分钟。 我运行了 SQL Profiler,每次更新都会看到这样的查询:

exec sp_executesql N'UPDATE [dbo].[PointRecord]
SET [PtPassword] = @p19
WHERE ([ID] = @p0) AND ([PtLocation] = @p1) AND ([PtIPAddress] = @p2) AND ([PtPort] = @p3) AND ([PtUsername] = @p4) AND ([PtPassword] = @p5) AND ([PtGWParam1] = @p6) AND ([PtGWParam2] = @p7) AND ([PtGWParam3] = @p8) AND ([PtGWParam4] = @p9) AND ([PtTag] = @p10) AND ([PtCapture] = @p11) AND ([PtGroup] = @p12) AND ([PtNumSuccess] = @p13) AND ([PtNumFailure] = @p14) AND ([PtControllerType] = @p15) AND ([PtControllerVersion] = @p16) AND ([PtAssocXMLGroupID] = @p17) AND ([PtErrorType] IS NULL) AND ([PtPollInterval] = @p18)',N'@p0 int,@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 nvarchar(4000),@p4 nvarchar(4000),@p5 nvarchar(4000),@p6 nvarchar(4000),@p7 nvarchar(4000),@p8 nvarchar(4000),@p9 nvarchar(4000),@p10 nvarchar(4000),@p11 int,@p12 nvarchar(4000),@p13 int,@p14 int,@p15 nvarchar(4000),@p16 nvarchar(4000),@p17 int,@p18 int,@p19 nvarchar(4000)',@p0=296987,@p1=N'1234 Anytown USA',@p2=N'10.16.31.20',@p3=N'80',@p4=N'username1',@p5=N'password1',@p6=N'loadmon.htm?PARM2=21',@p7=N'>Operating Mode',@p8=N'',@p9=N'',@p10=N'1234 Anytown USA\HLTH SERVICE LTS\Operating Modeloadmon',@p11=0,@p12=N'1234 Anytown USA',@p13=0,@p14=0,@p15=N'DeviceA',@p16=N'3.5.0.2019.0219',@p17=309,@p18=15,@p19=N'hk+MUoeVMG69pOB3DHYB8g=='

如您所见,“WHERE”部分要求每个字段都匹配,当这是一个索引表时,使用唯一的主键“ID”。 这真的很耗时。 有没有办法让它只使用“WHERE ID = [value]”?

我现在明白检查每个字段是 EF 中并发检查的要求。 要绕过,需要 LINQ 之外的方法。
我最终使用了 Petrov 先生和 Harvey 先生建议的变体,使用 ExecuteCommand,因为我正在更新数据库,而不是查询数据。
这是示例代码,以防其他人遇到类似问题。 它使用 LINQ 获取要更新的记录和用于用户反馈的记录计数。 它使用 ExecuteCommand 来更新记录。 我实际上更新了三个表(下面的示例中只显示了一个),因此使用了事务 object。 未显示 EncryptPassword 方法。 这是我用来更新记录的。 您应该将其替换为适合您需要的任何更新逻辑。

static void Main(string[] args)
{
    DatabaseHelpers.Initialize();
    if (DatabaseHelpers.PasswordsEncrypted)
    {
        Console.WriteLine("DatabaseHelpers indicates that passwords are already encrypted. Exiting.");
        return;
    }

    // Note that the DatabaseHelpers.DbContext is in a helper library,
    //  it is a copy of the auto-generated EF 'DataClasses1DataContext'.
    //  It has already been opened using a generated connection string
    //  (part of DatabaseHelpers.Initialize()).
    //  I have altered some of the variable names to hide confidential information.
    try
    {
        // show user what's happening
        Console.WriteLine("Encrypting passwords...");
        // flip switch on encryption methods
        DatabaseHelpers.PasswordsEncrypted = true;
        int recordCount = 0;

        // Note: Using LINQ to update the records causes an unacceptable delay because of the concurrency checking
        //  where the UPDATE statement (at SubmitChanges) checks EVERY field instead of just the ID
        //  and we don't care about that!
        // We have to set up an explicit transaction in order to use with context.ExecuteCommand statements
        //  start transaction - all or nothing
        DatabaseHelpers.DbContext.Transaction = DatabaseHelpers.DbContext.Connection.BeginTransaction();

        // update non-null and non-empty passwords in groups
        Console.Write("Updating RecordGroups");
        List<RecordGroup> recordGroups = (from p in DatabaseHelpers.DbContext.RecordGroups
                                    where p.RecordPassword != null && p.RecordPassword != string.Empty
                                    select p).ToList();
        recordCount = recordGroups.Count;
        foreach (RecordGroup rGroup in recordGroups)
        {
            // bypass LINQ-to-SQL
            DatabaseHelpers.DbContext.ExecuteCommand("UPDATE RecordGroup SET RecordPassword={0} WHERE ID={1}", DatabaseHelpers.EncryptPassword(rGroup.RecordPassword), rGroup.ID);
            Console.Write('.');
        }

        // show user what's happening
        Console.WriteLine("\nCommitting transaction...");
        DatabaseHelpers.DbContext.Transaction.Commit();

        // display results
        Console.WriteLine($"Updated {recordCount} RecordGroup passwords. Exiting.");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"\nThere was an error executing the password encryption process: {ex}");
        DatabaseHelpers.DbContext.Transaction.Rollback();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM