【问题标题】:Cache issues for message queue消息队列的缓存问题
【发布时间】:2021-10-20 11:23:58
【问题描述】:

如果我有一个缓存 (IMemoryCache) 字典。在我的应用程序的某个时刻,我从缓存中获取了一些 Id 的列表,然后我使用这个列表将它插入到数据库中。有时,此列表可能会在插入之前被其他任务更改 - 这会引发异常。为避免从缓存中获取列表后立即发生这种情况,我正在制作一份副本。但有时列表中的对象可以在插入之前被其他任务更改 - 这就是抛出异常。为了避免这种情况,我可以为列表中的每个对象创建一个副本,并从复制的对象创建一个新列表,但这有点不吸引人。

也许其他解决方案是从缓存中获取列表并创建一个锁,然后在锁内完成插入数据库流。是否有其他解决方案或解决此类问题的最佳方法?

一些解释:这是一个长链问题。我正在使用 Cassandra DB 进行存储。上面的 Cache 以及 DB 中的 Table 都与消息队列有关。如果我从 DB (Cassandra) 中删除了很多内容,它会在表中生成很多墓碑,并且在某些时候,如果我通过阈值,它将无法查询表。我通过将表的结构从每条未读消息 1 行更改为每个用户 1 行并带有列列表来解决这个问题。但这现在给缓存带来了问题。为什么要使用缓存?好吧,因为 Cassandra 在写操作上真的很快,但对于读操作却不是真的(如果我有 10.000 个打开的 Web 套接字到 2 个服务器实例和 1 个 cassandra 节点,读/写只是挂起,我的服务器进入线程池饥饿状态,不断获得更多线程但不做任何工作),所以我在启动时读取一次,然后只使用缓存中的列表而不是数据库中的列表将数据写入 Cassandra,以避免从数据库中重新读取它们。通过做这一切,我遇到了一致性、奇怪的缓存层和异步突变的问题。任何建议都会有所帮助。

【问题讨论】:

    标签: c# asp.net-core caching cassandra


    【解决方案1】:

    对我来说,您已经说过的显而易见的事情是制作 List 对象的副本,以便在您遍历列表时它不会被更改。

    作为旁注,我不同意 Cassandra 读取速度不快的说法。我与数百家使用 Cassandra 的公司合作,其中很多公司在 6-8 毫秒之间的读取延迟上具有 95% 的 SLA。全面披露:我是 DataStax 的 Cassandra 爱好者。 ?

    Cassandra 专为在数十台商品服务器上运行的互联网规模而设计。最佳实践是部署具有至少 3 个节点且复制因子为 3 的集群,以实现高可用性和最佳性能。 2 个具有单节点 C* 后端的应用程序实例不会削减它,因为您实际上是在对数据库进行 DDoS 攻击。干杯!

    【讨论】:

    • 你认为这就是泳池饥饿的原因吗?我的意思是我的服务器上有 10.000 个打开的 Web 套接字,它们在几秒钟内处理 100.000 条消息(如果我们取出导致问题的缓存,那么所有这些消息都以某种方式直接与数据库交互,写入 1表,从中读取,然后添加到另一个)导致 Cassandra 过载?但是为什么我没有从 Cassandra 得到超时,而是得到一个连续生成线程的服务器实例,而不做任何工作?
    • 池饥饿是由其他原因引起的,现在要么超时,要么“所有主机尝试查询失败”,所以我猜单个节点的 100.000 次写入/读取太多了。
    • 这很大程度上取决于数据模型、访问模式、磁盘 IO 等。但就像我说的,这相当于对单个节点进行 DDoSing。水平缩放的时间。干杯!
    【解决方案2】:

    如果我正确理解您的问题,您有一个 Dictionary<string, List> 可以将某些内容作为缓存存储在应用程序中。问题是内部的List 在您进行一些操作与之交互之前已被另一个任务更改。

    如果是这样,我建议您采取另一种方法。即使是 ConcurrentDictionary<string, List> 也不适合您的用例

    例如:

    您有一个包含对象 A 和 B 的 List 2 任务仍然可以一个接一个地把你的清单拿出来。任务 1 添加对象 C 然后调用 .Set(),任务 2 添加对象 D 然后在 IMemoryCache 实例上调用 .Set()

    结果可能是 A,B,C 或 A,B,D 的列表。

    另一种方法:

    使用静态HashSet<string> 存储您的密钥,然后使用ConcurrentQueue 将每个List 存储在IMemoryCache 中。然后每个任务都可以安全地排队。 取出数据时,建议你看看take all data from ConcurrentQueue atomicity,然后随心所欲。

    【讨论】:

    • 是的,但是队列意味着我确信添加到队列的第一条消息将是第一条被取出的消息。这是最好的情况,但是如果用户发送 2 条消息的确认,并且一条消息有一些延迟,第二条消息的 ack 先到达,但我从队列中获取第一条消息并将其设置为已读。
    • 这里我不太明白...如果您需要在队列中修改对象中的数据,我们可以使用 linq,而不仅仅是经典的EnqueueTryDequeue 方式,就像您在列表中查找和修改对象的方式一样。 (如果有更好的方法,可以分享一下吗(XD))。即使使用 linq 也更安全,因为我们也得到了一些保证,因为它使用了 Enumerate 的私有实现版本
    猜你喜欢
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    • 2016-10-23
    • 2021-11-18
    • 2018-02-05
    • 1970-01-01
    相关资源
    最近更新 更多