首页 > 解决方案 > 减慢批量插入的执行速度

问题描述

我们有一个位于 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
        );
    }
}

标签: asp.netsql-serverazure-sql-databasebulkinsert

解决方案


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

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());
    }
}

推荐阅读