从 C# 类批处理存储过程调用



由于数据库表中的实体逐个更新,行数很高(超过一百万(,我们的应用程序中出现了性能问题。我们不断收到死锁受害者错误,因此很明显,表锁定行的时间比应有的时间长。

目前,我们实现了存储过程调用的可配置限制/时间阈值的手动批处理,以更新数据库中的实体。

简化的类如下所示:

public class EntityBatchUpdater
{
private readonly IRepository _repository;
private List<Entity> _batch = new List<Entity>();
private readonly Timer _batchPostingTimer;
private readonly int _batchSize;
private static readonly object _batchPostingLock = new object();
public EntityBatchUpdater(IRepository repository)
{
_repository = repository;
_batchSize = 1000; // configurable
string batchPostingPeriod = "00:01:00.00"; // configurable
_batchPostingTimer = new Timer
{
Interval = TimeSpan.Parse(batchPostingPeriod).TotalMilliseconds,
Enabled = true,
AutoReset = true,
};
_batchPostingTimer.Elapsed += OnTimedEvent;
}
public void Update(Entity entity)
{
try
{
lock (_batchPostingLock)
{
_batch.Add(entity);
if (_batch.Count == _batchSize)
{
EntityBatchUpdate();
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to insert batch {JsonConvert.SerializeObject(batch)}");
}
}
private void EntityBatchUpdate()
{
if (_batch.Count == 0)
{
return;
}              
try
{
var entityBatchXML = SerializePayload();
_repository.BatchUpdate(entityBatchXML);
_batch.Clear();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to insert batch; batchSize:{_batch.Count}");
}
}
private string SerializePayload()
{
using (var sw = new System.IO.StringWriter())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var serializer = new XmlSerializer(typeof(List<Entity>),
new XmlRootAttribute("ENTITY_BATCH"));
serializer.Serialize(sw, _batch, ns);
return sw.ToString();
}
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
EntityBatchUpdate();
}
}

目前,我们利用了 SQL Server 的快速 XML 处理,在调用实际过程时将有效负载序列化为 XML,以避免大量调用命中数据库。 我还考虑过创建一个表值参数来序列化我们需要发送到 proc 的数据,但我认为这不会显着提高性能。

我的问题是:您如何处理数据库上的此类大负载? 1.( 您是否使用 nuget 包/其他工具来为您处理批处理? 2.(您是否使用其他实践解决了这个问题?

编辑: 为了提供更多见解:我们目前正在应用程序中手动处理队列(因此有大量更新(,我们希望尽可能快速和可靠地完成它。 我们将来会转向更好的排队机制(RabbtiMQ 或 Kafka(,但与此同时,我们希望有一种标准方法来消费和处理来自数据库表的队列。

最令人困惑的部分是,您首先是从 C# 代码执行此工作。您处理> 1.000.000 条记录。这不是你应该在代码中做的工作,永远。我真的不明白你在用这些记录做什么,但作为一个一般人,只需在数据库中保持这种工作规模。始终尝试在数据库端执行尽可能多的过滤、插入、批量插入、批量更新等操作。

切勿将可能是 SQL 的内容移动到客户端程序。充其量,您增加了必须通过网络移动数据一次甚至两次的网络负载(对于更新(。在最坏的情况下,你会给比赛条件增加巨大的危险。而且在任何时候,您都没有机会击败数据库服务器进行相同操作所需的时间。

这似乎只是一个批量插入。SQL有一个单独的命令,任何值得它的DBMS磁盘空间都可以选择从各种文件格式导入数据,包括.csv。

最新更新