不要这样做。除非您非常非常小心,否则 Cassandra 作为队列后端是一个糟糕的选择。您可以在Jonathan Ellis 博客文章“Cassandra 反模式:队列和类似队列的数据集”(这可能是您所暗示的帖子)中阅读更多原因。MySQL 也不是支持队列的好选择,我们是像 RabbitMQ 这样的真正队列产品,它很棒而且非常易于使用。
使用 Cassandra 作为队列存储的问题是:每次删除消息时,都会为该消息编写墓碑。每次您查询下一条消息时,Cassandra 都必须遍历那些墓碑和已删除的消息,并尝试确定少数未被删除的消息。对于任何类型的吞吐量,读取值的数量与实际实时消息的数量将是数十万比一。
调整 GC 宽限和其他参数将无济于事,因为这仅适用于压缩后墓碑会挂起多长时间,即使您将 CPU 专用于仅运行压缩,您仍然会有数万或更多的死活口粮. 在某些情况下,即使 GC 优雅为零墓碑也会在压缩后徘徊。
有一些方法可以减轻这些影响,它们在 Jonathan 的帖子中进行了概述,但这里有一个总结(我写这篇文章并不是为了鼓励你使用 Cassandra 作为队列后端,而是因为它解释了更多关于 Cassandra 工作的信息,并且应该可以帮助您理解为什么它不适合该问题):
为了避免墓碑问题,你不能继续使用相同的队列,因为它会比压缩更快地填满墓碑,并且你的性能将直接陷入困境。如果您向主键添加一个确定性且取决于时间的列,您可以避免一些性能问题,因为更少的 tombstone 有时间建立,并且 Cassandra 将能够完全删除旧行及其所有 tombstone。
每个队列使用单行也会创建一个热点。单个节点必须处理该队列,其余节点将处于空闲状态。您可能有很多队列,但其中一个可能会比其他人看到更多的流量,这意味着您获得了一个热点。通过向主键添加第二列,将队列分片到多个节点上。它可以是消息的散列(例如crc32(message) % 60
会创建 60 个分片,不要使用太小的数字)。当您想从所有分片中查找下一条消息并选择其中一个结果时,忽略其他结果。理想情况下,您会找到一种将其与依赖时间的东西结合起来的方法,这样您就可以在处理该问题时也解决该问题。
如果您在到达时间之后对消息进行排序(例如使用TIMEUUID
集群键)并且可以以某种方式跟踪已传递的最新消息,则可以执行查询以查找该消息之后的所有消息。这将意味着更少为 Cassandra 翻墓碑,但这不是灵丹妙药。
然后是承认问题。我不确定它们对您是否重要,但看起来您的架构中有某种锁定机制(我正在考虑retries
andsending
列)。这行不通。在 Cassandra 2.0 和它的比较和交换功能之前,没有办法让它正常工作。要实现锁定,您需要读取列的值,检查它是否没有被锁定,然后写下它现在应该被锁定。即使具有一致性级别,ALL
另一个应用程序节点也可以同时执行相同的操作,并且最终都认为他们锁定了消息。使用 Cassandra 2.0 中的 CAS 可以自动执行,但会以性能为代价。
StackOverflow 上还有一些关于 Cassandra 和队列的答案,请阅读它们(从以下内容开始:Table with heavy writes and some reads in Cassandra。主键搜索需要 30 秒。