减慢批量插入的执行
Slow down execution of bulk inserts
我们有一个位于 S1 定价层的 Azure SQL 数据库。我们的站点缓存非常多,因此数据库命中率绝对最低。平均 DTU 使用率仅为 ~1.5%,这很好,因为我们的数据库成本只是我们旧网站上的一小部分(20 英镑 p/m 对比 400 英镑 p/m!)
然而,在网站上,我们确实有需要插入大约 10 万条记录的小脚本(当有人执行创建新教程等操作时的用户通知)。
触发后,DTU 的峰值会达到 100%,持续约 3-5 分钟。
该脚本只是一个调用插入的循环:
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 的使用不会阻塞一切的最佳方法是什么?
您可以同时插入 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 作业读取此信息,该作业允许您配置读取间隔,并像我一样插入数据库写过。
这种方法允许很大的负载(我在生产环境中有几个),如果代理或数据库出现问题,消息将存储在队列中,直到您将它们移动到数据库。
您每次插入一行 - 这效率不高。
A 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());
}
}
我们有一个位于 S1 定价层的 Azure SQL 数据库。我们的站点缓存非常多,因此数据库命中率绝对最低。平均 DTU 使用率仅为 ~1.5%,这很好,因为我们的数据库成本只是我们旧网站上的一小部分(20 英镑 p/m 对比 400 英镑 p/m!)
然而,在网站上,我们确实有需要插入大约 10 万条记录的小脚本(当有人执行创建新教程等操作时的用户通知)。
触发后,DTU 的峰值会达到 100%,持续约 3-5 分钟。
该脚本只是一个调用插入的循环:
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 的使用不会阻塞一切的最佳方法是什么?
您可以同时插入 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 作业读取此信息,该作业允许您配置读取间隔,并像我一样插入数据库写过。 这种方法允许很大的负载(我在生产环境中有几个),如果代理或数据库出现问题,消息将存储在队列中,直到您将它们移动到数据库。
您每次插入一行 - 这效率不高。
A 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());
}
}