集合数据结构将我从痛苦的世界中解救出来

2020-10-18 23:09:40

当我们在学校里被教授集合论时,我想知道为什么他们用了整整一章的数学来教我们如何将项目分类。具有相似属性的实体组合在一起不是很明显吗?

十多年后,我意识到,当他们教给你一些东西时,也许整个主题并不重要,重要的是关于这个主题的一小部分信息,这些信息将来可能会派上用场。

为什么这个看似微不足道的事实会成为我精神健全的关键呢?更重要的是,它如何帮助我防止代码库变成意大利面?‍。

我正在构建一个客户支持工具,CS工程师可以在其中解决从多个沟通渠道提出的客户问题。

因此,根据其设计,我们需要一个UI元素来指示每个通道有多少对话处于无人值守状态。

正如您可能已经猜到的那样,浏览器页面的每次刷新都会检索此信息,因此在每次刷新时通过对话数据库为每个支持渠道运行计数查询效率不是很高。

当然,一开始这可能不是什么大事,但如果我们将规模扩大到我们计划使用该产品的一半,这样的查询将比我希望的更快地在背后咬我一口。

我想到的解决方案是缓存每个支持渠道的打开对话计数。起初这是有道理的,但当我再想一想时,它很快就变成了一场噩梦。

好的,我计划在缓存中保存打开的对话的计数,这意味着每次打开对话时,我会递增计数,而每次关闭对话时,我会递减计数,这是不是意味着,每次打开对话时,我都会递增计数,而每次关闭对话时,我会递减计数?

好的,这听起来不是很糟糕,但是我如何触发这个递增/递减操作呢?我想每当数据库中的会话更新时,我都可以这样做。

但是等等,我不能在每次更新对话时都触发它,如果对话是打开的,但对话中的一些其他数据在状态仍为打开时被更新,该怎么办?

只有当状态从打开更改为关闭或从关闭更改为打开时,我是否必须编写IF-ELSE条件才能运行递增/递减操作?

如果会话更新完成运行,但递增/递减操作由于某种原因失败,或反之亦然,会发生什么情况?如何在不损坏数据的情况下重新运行更新?

如果您还没有猜到我带着这篇文章要去哪里,这里有一个很好的视觉帮助。

那么集合论是如何让我走出困境的呢?如果我为每个支持渠道维护一组属于开放对话的ID,会怎么样?

现在我要做的就是:在每次会话更新时,如果状态为打开,则将该会话ID添加到集合中,如果状态为关闭,则将其从集合中删除。

当然,该集合将存储在RAM上,是的,与仅为每个支持通道存储单个计数相比,它将占用更多内存,但我们所做的只是存储ID,而不是整个数据对象,因此它在内存方面几乎不会花费太多成本。

这对解决问题有什么帮助呢?如果你想要打开对话的数量,只要得到集合中元素的数量,如果你愿意的话,就是集合的基数。

因为一个集合不能有重复项。无论我将一个项目添加到集合中多少次,唯一的对话ID只能添加一次,同样,一旦执行了删除操作,对同一对话ID的后续删除操作也不会对数据产生影响。数据已经变成了幂等项。

这意味着在执行会话更新操作时不需要if-Else条件。我所需要做的就是根据更新后对话的状态执行添加/删除操作。

是否有显式状态更新并不重要。每次更新都可以触发此逻辑,并且数据保持不损坏。如果任何操作中途失败,只需重新运行会话更新即可。

要做到这一点,最明显的方法是对每个支持通道使用类似这样的JSON字典数据结构。

如果需要将会话添加到集合中,请使用关键字作为会话ID更新字典,并将值设置为true或1。对于删除操作,请首先检查会话ID是否已在字典中,然后将其从字典中删除。如果它还不存在,请不要执行任何操作。

该字典现在可以存储在内存中的变量中,并在整个应用程序中使用。要获取支持频道的打开对话计数,只需获取字典中存在的关键字数量即可。

显然,其他人在我之前就意识到了集合作为数据结构的重要性,因为Redis-一个用于缓存数据的优雅的内存数据库-已经提供了集合数据结构以及允许您在集合中添加、删除和查找项目数量的操作。

我强烈建议在您的应用程序中使用它,而不是在变量中维护字典,因为Redis的优秀人员已经解决了由于维护内存缓存而可能出现的许多其他问题。

也许重温我们小时候学到的一些概念是有价值的,看看它们是否仍然相关,是否能以某种方式应用于现实世界。

至少,这会给我们一个了结,给我们灌输希望,也许我们学到的一切在当时看起来毫无用处,但在我们的生活中,可能会有某些方面派上用场,我们只需要保持我们的眼睛,耳朵,最重要的是我们的思想开放。