【问题标题】:Slow down execution of bulk inserts减慢批量插入的执行速度
【发布时间】:2018-05-15 08:47:44
【问题描述】:

我们有一个位于 S1 定价层的 Azure SQL 数据库。我们的站点被大量缓存,因此数据库命中率绝对是最低的。平均 DTU 使用率仅为约 1.5%,这非常好,因为我们的数据库成本只是旧网站上的一小部分(20 英镑/月对 400 英镑/月!)

然而,在网站上,我们确实有需要插入大约 10 万条记录的小脚本(用户执行操作时的通知,例如创建新教程)。

触发后,DTU 在 3-5 分钟内达到 100% 峰值。

脚本只是一个调用插入的循环:

using(var db = new DBContext())
{
    foreach(var userID in userIDs)
    {
        db.ExecuteCommand(
        "INSERT INTO UserNotifications " +
        "(ForUserID, Date, ForObjectTypeID, ForObjectID, TypeID, Count, MetaData1) 
        VALUES ({0}, {1}, NULL, {2}, {3}, {4}, {5}, {6})",
        userID, DateTime.Now.ToUniversalTime(), forObjectID, (byte)type, 1, metaData1.Value
        );
    }
}
  • 还有比这更快的插入方法吗?
  • 此外,减慢此脚本执行速度的最佳方法是什么,这样 DTU 的使用才不会阻塞所有内容?

【问题讨论】:

  • 我很困惑。您想要一种更快的插入方式,但又想减慢执行速度?
  • @Nkosi:Op 希望查询不要使用整个 DTU
  • 我不确定,如果您的查询并行运行,如果您的查询并行运行,您最终可能会使用更多资源,您可以尝试在插入期间将 mdop 更改为 1 以查看您的DTU消耗降低ALTER DATABASE SCOPED CONFIGURATION
  • 也可以看到这个答案:stackoverflow.com/questions/5940225/…
  • 抱歉,澄清一下,我想知道是否有办法加快插入性能,以及降低执行速度的最佳方法是确保不会消耗所有 DTU

标签: asp.net sql-server azure-sql-database bulkinsert


【解决方案1】:

您每次插入都做一行 - 这效率不高。

TVP 类似于反向数据读取器,并且效率很高。

低级技术是一次插入 900 行(最多 1000 行)。仅此一项就可能效率高出 400 倍。

StringBuilder sb = new StringBuilder();
string insert = "INSERT INTO UserNotifications " +
                "(ForUserID, Date, ForObjectTypeID, ForObjectID, TypeID, Count, MetaData1) " +  
                "VALUES ";
sb.AppendLine(insert);
int count = 0;
using(var db = new DBContext())
{        
    foreach(var userID in userIDs)
    {
        sb.AppendLine(string.Format(({0}, {1}, NULL, {2}, {3}, {4}, {5}, {6}), ",
                      userID, DateTime.Now.ToUniversalTime(), forObjectID, (byte)type, 1, metaData1.Value);
        count++;
        if (count = 990) 
        {
            db.ExecuteCommand(sb.ToString());
            count = 0;
            sb.Clear();
            sb.AppendLine(insert); 
            //can sleep here to throttle down cpu 
        }            
    }
    if (count > 0) 
    {
        db.ExecuteCommand(sb.ToString());
    }
}

【讨论】:

    【解决方案2】:

    您可以同时插入 100 个实体,而不是逐个实体地插入实体,将实体包装在 JSON 中并编写一个使用它的存储过程,如本例所示:

    INSERT INTO [dbo].[AISecurityLogs]
        ([IpAddress], [TimeRange], [Requests], [LogId])
        SELECT *, LogId = @logId
        FROM OPENJSON ( @json )  
        WITH (   
            IpAddress varchar(15) '$.IpAddress',  
            TimeRange DATETIME '$.TimeRange',  
            Requests int '$.Requests'
         )
    

    要减慢执行速度并且不丢失任何内容,您可以将日志放入队列中,然后使用 azure Job 读取此信息,这允许您配置读取间隔,并按照我之前写的那样插入数据库. 这种方法允许大负载(我在生产环境中有几个),如果代理或数据库出现问题,消息将存储在队列中,直到您将它们移至数据库。

    【讨论】:

    • 我会使用表类型参数而不是 JSON;至少它会是强类型的。见这里:sommarskog.se/arrays-in-sql-2008.html
    • 如果您使用强类型语言生成 JSON,则没有问题。
    猜你喜欢
    • 1970-01-01
    • 2013-04-16
    • 2011-08-07
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    • 2016-05-22
    • 2013-10-14
    • 2020-02-22
    相关资源
    最近更新 更多