Azure SQL批量插入:提速方法与降低DTU占用方案咨询
针对Azure SQL批量插入的优化方案
嘿,针对你的两个问题,我来分享下实际项目里常用的解决方案,应该能帮到你:
1. 更快的插入方法:替换循环单条插入为批量操作
你现在的循环单条插入方式,每一条都要单独和数据库建立连接、执行命令、提交小事务,这会产生大量的网络往返和数据库开销,也是DTU飙升的核心原因。推荐这几种更高效的方式:
方式一:EF的AddRange批量添加(简单易实现)
如果用的是Entity Framework,可以把所有要插入的实体先收集到列表里,再一次性调用AddRange和SaveChanges,这样EF会把多个插入合并成更少的数据库操作:
using(var db = new DBContext()) { var notifications = new List<UserNotification>(); foreach(var userID in userIDs) { notifications.Add(new UserNotification { ForUserID = userID, Date = DateTime.Now.ToUniversalTime(), ForObjectID = forObjectID, TypeID = (byte)type, Count = 1, MetaData1 = metaData1.Value }); } db.UserNotifications.AddRange(notifications); db.SaveChanges(); }
方式二:SqlBulkCopy(超大数据量下最优)
如果数据量超过10万级,SqlBulkCopy是效率最高的选择,它直接利用SQL Server的批量插入机制,能大幅减少DTU消耗:
// 先构造DataTable,结构和UserNotifications表匹配 var dt = new DataTable(); dt.Columns.Add("ForUserID", typeof(int)); // 替换成你的实际字段类型 dt.Columns.Add("Date", typeof(DateTime)); dt.Columns.Add("ForObjectID", typeof(int)); dt.Columns.Add("TypeID", typeof(byte)); dt.Columns.Add("Count", typeof(int)); dt.Columns.Add("MetaData1", typeof(string)); // 替换成你的实际类型 foreach(var userID in userIDs) { dt.Rows.Add( userID, DateTime.Now.ToUniversalTime(), forObjectID, (byte)type, 1, metaData1.Value ); } // 执行批量复制 using(var conn = new SqlConnection(db.Database.Connection.ConnectionString)) { conn.Open(); using(var bulkCopy = new SqlBulkCopy(conn)) { bulkCopy.DestinationTableName = "UserNotifications"; // 映射列名(如果DataTable和表列名一致可以省略) bulkCopy.ColumnMappings.Add("ForUserID", "ForUserID"); bulkCopy.ColumnMappings.Add("Date", "Date"); // ... 其他列映射 bulkCopy.WriteToServer(dt); } }
方式三:表值参数(TVP)
如果需要在插入时执行一些自定义逻辑,TVP是不错的选择,它允许你把数据集作为参数传递给存储过程,再在存储过程里批量插入:
- 先创建用户定义表类型:
CREATE TYPE UserNotificationType AS TABLE ( ForUserID INT, Date DATETIME, ForObjectID INT, TypeID TINYINT, Count INT, MetaData1 NVARCHAR(MAX) -- 替换成你的实际类型 );
- 创建批量插入的存储过程:
CREATE PROCEDURE BulkInsertUserNotifications @Notifications UserNotificationType READONLY AS BEGIN INSERT INTO UserNotifications (ForUserID, Date, ForObjectID, TypeID, Count, MetaData1) SELECT ForUserID, Date, ForObjectID, TypeID, Count, MetaData1 FROM @Notifications; END
- C#调用代码:
using(var db = new DBContext()) { var dt = new DataTable(); // 构造DataTable逻辑同SqlBulkCopy示例 // ... var parameter = new SqlParameter("@Notifications", dt); parameter.SqlDbType = SqlDbType.Structured; parameter.TypeName = "UserNotificationType"; db.Database.ExecuteSqlCommand("EXEC BulkInsertUserNotifications @Notifications", parameter); }
2. 放缓脚本执行,避免DTU占用过高
如果不想一次性批量插入(比如担心大事务锁表),或者需要严格控制DTU使用率,可以通过分批次插入+延迟的方式来节流:
方案:分批次处理,每批插入后暂停
把10万条数据分成若干小批次(比如每批1000条),每插入一批后暂停几秒,给数据库喘息的空间:
using(var db = new DBContext()) { int batchSize = 1000; int total = userIDs.Count(); for(int i = 0; i < total; i += batchSize) { var batchUserIDs = userIDs.Skip(i).Take(batchSize).ToList(); var notifications = new List<UserNotification>(); foreach(var userID in batchUserIDs) { notifications.Add(new UserNotification { ForUserID = userID, Date = DateTime.Now.ToUniversalTime(), ForObjectID = forObjectID, TypeID = (byte)type, Count = 1, MetaData1 = metaData1.Value }); } db.UserNotifications.AddRange(notifications); db.SaveChanges(); // 每批插入后暂停1秒(可根据DTU实际情况调整时长) System.Threading.Thread.Sleep(1000); } }
进阶:动态监控DTU使用率调整速度
如果想更智能,可以通过Azure Monitor的API获取当前DTU使用率,当使用率超过阈值时延长暂停时间,低于阈值时加快速度。不过这个实现稍复杂,适合对性能控制要求极高的场景。
另外,也可以调整Azure SQL的事务隔离级别(比如开启READ COMMITTED SNAPSHOT),减少锁竞争,但这个需要结合你的业务场景评估。
内容的提问来源于stack exchange,提问作者Tom Gullen
相关产品推荐
相关产品推荐

