开发者社区> 问答> 正文

批处理来自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),但是与此同时,我们希望拥有一种使用和处理数据库表中队列的标准方法。

展开
收起
心有灵_夕 2019-12-23 10:01:50 875 0
0 条回答
写回答
取消 提交回答
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载