Event Store 3.0 - 吞吐量/性能

时间:2011-11-22 21:45:24

标签: cqrs event-store

我一直在试验JOliver的Event Store 3.0作为项目中的潜在组件,并一直在尝试通过Event Store来衡量事件的吞吐量。

我开始使用一个简单的线束,它基本上通过for循环迭代创建一个新流,并将一个包含GUID id和字符串属性的非常简单的事件提交给MSSQL2K8 R2 DB。调度员本质上是一个无操作员。

这种方法设法在8路HP G6 DL380上运行~3K操作/秒,数据库在单独的32路G7 DL580上运行。测试机器没有资源限制,阻塞看起来是我的限制。

有没有人有过测量Event Store吞吐量的经验以及取得了哪些数据?我希望能够获得至少1个数量级的吞吐量,以使其成为可行的选择。

3 个答案:

答案 0 :(得分:7)

我同意阻止IO将成为最大的瓶颈。我可以在基准测试中看到的一个问题是您针对单个流进行操作。您的域中有多少聚合根,每秒3K +事件? EventStore的主要设计是针对多个聚合进行多线程操作,从而减少了对读取世界应用程序的争用和锁定。

另外,您使用的是什么序列化机制? JSON.NET?我还没有协议缓冲区实现,但每个基准测试表明PB在性能方面明显更快。对你的应用程序运行一个分析器来查看最大的瓶颈是多么有趣。

我注意到的另一件事是你在网络中引入网络跳跃,这会增加任何单个流的延迟(和阻塞时间)。如果您正在写入使用固态驱动器的本地SQL实例,那么与运行磁盘驱动器的远程SQL实例相比,我可以看到这些数字要高得多,并且数据和日志文件位于同一个盘片上。

最后,您的基准测试应用程序是使用System.Transactions还是默认为没有事务? (EventStore是安全的,不使用System.Transactions或任何类型的SQL事务。)

现在,尽管如此,我毫不怀疑EventStore中的某些区域可以通过一点点关注进行显着优化。事实上,我正在为3.1版本提供一些向后兼容的架构修订,以减少在单个提交操作期间在SQL Server(以及一般的RDBMS引擎)中执行的写入次数。

在开始作为3.x基础的2.x重写时,我遇到的一个最大的设计问题是异步,非阻塞IO的想法。我们都知道node.js和其他非阻塞Web服务器在一定数量级上击败了线程Web服务器。但是,调用者引入复杂性的可能性增加,必须加以考虑,因为它是大多数程序和库运行方式的根本转变。如果我们转移到一个规范的非阻塞模型,它将在4.x时间范围内更多。

底线:发布您的基准,以便我们可以看到瓶颈所在。

答案 1 :(得分:6)

优秀的问题马特(+1),我看到奥利弗先生自己回答为答案(+1)!

我想提出一种稍微不同的方法,我自己正在玩这个方法来帮助你看到每秒3000次提交的瓶颈。

CQRS模式,大多数使用JOliver的EventStore的人似乎都试图遵循,允许大量的"向外扩展"子模式。人们通常排队的第一个是事件提交自己,你看到了瓶颈。"排队"意味着从实际提交中卸载并将它们插入到一些写优化的非阻塞I / O进程或" queue"中。

我的解释是:

命令广播 - >命令处理程序 - >活动广播 - >事件处理程序 - >活动商店

这些模式中实际上有两个横向扩展点:命令处理程序事件处理程序。如上所述,大多数都是从事件处理程序部分扩展开始,或者在你的情况下将事件扩展到EventStore库,因为这通常是最大的瓶颈,因为需要在某个地方持久存在(例如Microsoft SQL Server数据库)。 p>

我自己正在使用一些不同的提供商来测试最佳性能以排队"这些提交。 CouchDB和.NET的AppFabric Cache(具有出色的GetAndLock()功能)。 [OT]我非常喜欢AppFabric的持久缓存功能,可让您创建冗余缓存服务器,在多台计算机上备份您的区域 - 因此,只要至少有1台服务器启动并运行,您的缓存就会保持活动状态。 / OT]

因此,假设您的事件处理程序不直接将提交写入EventStore。相反,你有一个处理程序将它们插入"队列"系统,例如Windows Azure Queue,CouchDB,Memcache,AppFabric Cache等。重点是选择一个几乎没有块的系统来排队事件,但内置冗余的东西是持久的(Memcache是​​我最不重要的)最喜欢的冗余选项)。您必须具有该冗余,如果服务器丢失,您仍然排队了该事件。

为了最终提交这个"排队事件",有几个选项。我喜欢Windows Azure的队列模式,因为很多"工人"你可以不断寻找队列中的工作。但它不一定是Windows Azure - 我使用" Queue"在本地代码中模仿了Azure的队列模式。和#34;工人角色"在后台线程中运行。它的扩展非常好。

假设你有10名工人不断地调查这个"队列"对于任何用户更新的事件(我通常为每个事件类型编写一个工作者角色,在监视每种类型的统计信息时使扩展更容易)。两个事件被插入到队列中,前两个工作者立即接收一个消息,并将它们(提交它们)直接插入到您的EventStore中 - 多线程,正如Jonathan在他的回答中提到的那样。您使用该模式的瓶颈将是您选择的任何数据库/事件库备份。假设您的EventStore正在使用MSSQL,瓶颈仍然是3,000 RPS。这没关系,因为系统的建立是为了赶上'当那些RPS下降到20000次爆发后的50 RPS。这是CQRS允许的自然模式:"最终一致性。"

我说过CQRS模式还有其他横向扩展模式。如上所述,另一个是命令处理程序(或命令事件)。这也是我所做的,特别是如果你有一个非常丰富的域域作为我的客户之一(每个命令上的几十个处理器密集型验证检查)。在这种情况下,我实际上将命令本身排队,以便在后台由一些辅助角色处理。这也为您提供了一个很好的扩展模式,因为现在您的整个后端,包括事件的EvetnStore提交,都可以进行线程化。

显然,缺点是您放弃了一些实时验证检查。我通过在构造我的域时通常将验证分为两类来解决这个问题。一个是Ajax或实时"轻量级"域中的验证(类似于预命令检查)。其他是硬故障验证检查,只在域中完成但不能用于实时检查。然后,您需要在Domain模型中进行代码失败。意思是,如果出现问题,总是编写出一条出路,通常以通知电子邮件的形式向用户发出错误信息。由于该排队命令不再阻止该用户,因此如果该命令失败,则需要通知他们。

您的验证检查需要转到'后端'将转到您的查询或"只读"数据库,riiiight?不要进入EventStore来检查一个独特的电子邮件地址。您正在针对前端查询的高可用只读数据存储区进行验证。哎呀,只有一个CouchDB文档专门用于系统中所有电子邮件地址的列表,作为CQRS的查询部分。

CQRS只是建议......如果你真的需要实时检查繁重的验证方法,那么你可以围绕它构建一个Query(只读)存储,并加速验证 - 在PreCommand阶段,在它之前被插入到队列中。很多灵活性。我甚至认为,验证诸如空用户名和空电子邮件之类的内容甚至不是域关注点,而是UI责任(卸载在域中进行实时验证的需要)。我已经构建了一些项目,我在MVC / MVVM ViewModels上进行了非常丰富的UI验证。当然,我的Domain有非常严格的验证,以确保它在处理之前是有效的。但是移动平庸的输入验证检查,或者我称之为“轻量级”#34;验证,直到ViewModel层为最终用户提供近乎即时的反馈,而不会进入我的域。 (还有一些技巧可以保持与您的域同步)。

总而言之,可能会考虑在提交之前排队这些事件。这与Jonathan在他的回答中提到的EventStore的多线程功能非常吻合。

答案 2 :(得分:0)

我们使用Eventstore使用Erlang / Elixir https://github.com/work-capital/elixir-cqrs-eventsourcing构建了一个用于大规模并发的小样板。我们仍然需要优化数据库连接,池化等...但是每个聚合具有多个数据库连接的一个进程的想法与您的需求一致。