urb*_*sky 6 .net c# rdbms projection event-sourcing
我目前正在研究C#中使用CQRS和事件源的原型,并且在我对SQL数据库的预测中遇到了性能瓶颈.
我的第一个原型是使用Entity Framework 6构建的,代码优先.这个选择主要是为了开始,因为读取方将受益于LINQ.
每个(适用的)事件都由多个投影使用,这些投影可以创建或更新相应的实体.
这样的投影目前看起来像这样:
public async Task HandleAsync(ItemPlacedIntoStock @event)
{
var bookingList = new BookingList();
bookingList.Date = @event.Date;
bookingList.DeltaItemQuantity = @event.Quantity;
bookingList.IncomingItemQuantity = @event.Quantity;
bookingList.OutgoingItemQuantity = 0;
bookingList.Item = @event.Item;
bookingList.Location = @event.Location;
bookingList.Warehouse = @event.Warehouse;
using (var repository = new BookingListRepository())
{
repository.Add(bookingList);
await repository.Save();
}
}
Run Code Online (Sandbox Code Playgroud)
这不是很好的表现,很可能是因为我调用DbContext.SaveChanges()了这个IRepository.Save()方法.每个活动一个.
我接下来应该探索哪些选择?我不想花几天时间追逐那些可能只是稍微好一点的想法.
我目前看到以下选项:
我希望看到数百万个事件,因为我们计划采用大型遗留应用程序并以事件的形式迁移数据.新的预测也会经常添加,因此处理速度是一个实际问题.
基准:
更新的基准
TableAdapter(每次迭代时都是新的DataSet和新的TableAdapter):~2.500次插入/秒.没有使用投影管道进行测试,而是单独测试TableAdapter,SELECT插入后不插入:~3000插入/秒
TableAdapter10.84行的单线程ADO.NET 批量插入(单个数据集,内存中10.000行):> 10.000次插入/秒(我的样本大小和窗口太小)在批量提交和改进我的整体投影引擎时,即使使用实体框架,我也看到了几个数量级的性能改进。
这是通过使用以下技术和工具实现的:
TransformBlock并行BoundedCapacity度超过 100。最大并行度为Environment.ProcessorCount(即 4 或 8)。我看到队列大小为 100-200 与 10 时性能大幅提高:从每秒 200-300 个事件增加到 10,000 个事件。这很可能意味着 10 的缓冲区导致过多的欠载,从而过于频繁地提交工作单元。ActionBlock批量大小为 1000 个事件、队列大小为 200 的值是实验结果。这还显示了通过独立调整每个投影的这些值来进一步改进的选项。当使用 10.000 的批量大小时,为每个事件添加新行的投影会显着减慢 - 而仅更新一些实体的其他投影则受益于较大的批量大小。
反序列化队列大小对于良好的性能也至关重要。
所以,TL;DR:
实体框架的速度足够快,每秒可以处理多达 10,000 次修改 - 每个并行线程。利用您的工作单元并避免提交每一个更改 - 尤其是在 CQRS 中,其中投影是对数据进行任何更改的唯一线程。适当地交错并行任务,不要盲目地把async所有事情都做完。