到目前为止,我已经使用 MySQL 完成了几乎所有事情,但我不喜欢手动分片数据并暂时维护所有这些。
我想构建一个像 Facebook 和 WhatsApp 一样的一对一聊天应用程序,如下图所示:
所以我们这里有两个部分。右侧部分仅显示聊天线程中的所有消息,左侧部分显示聊天线程以及最后一条消息的信息,以及您的聊天伙伴信息,例如姓名和图像等。
到目前为止,这就是我所拥有的:
卡桑德拉非常擅长写作和阅读。但由于墓碑的原因,删除数据的情况就不那么严重了。并且您不想设置gc_grace_seconds为 0,因为如果节点发生故障并发生删除,则修复完成后该删除的行可能会恢复。因此,我们最终可能会在节点进入集群之前删除该节点的所有数据。无论如何,据我所知,Cassandra 非常适合这个聊天应用程序的正确部分。由于消息将按插入时间存储和排序,并且排序永远不会改变。所以你只需写和读。这正是 Cassandra 所擅长的。
我有这些表来存储正确部分的消息:
CREATE TYPE user_data_for_message (
from_id INT,
to_id INT,
from_username TEXT,
to_username TEXT,
from_image_name TEXT,
to_image_name TEXT
);
CREATE TABLE message_by_thread_id (
message_id TIMEUUID,
thread_id UUID,
user_data FROZEN <user_data_for_message>,
message TEXT,
created_time INT,
is_viewed BOOLEAN,
PRIMARY KEY (thread_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
Run Code Online (Sandbox Code Playgroud)
在插入新消息之前,如果客户端未提供 thread_id,我可以检查两个用户之间是否存在线程。我可以像这样存储该信息:
CREATE TABLE message_thread_by_user_ids (
thread_id UUID,
user1_id INT,
user2_id INT,
PRIMARY KEY (user1_id, user2_id)
);
Run Code Online (Sandbox Code Playgroud)
我可以为每个线程存储两行,其中 user1 和 user2 的顺序相反,这样我只需要执行 1 次读取来检查是否存在。由于我不想在每次插入之前检查线程是否存在,因此我可以首先检查 Redis 中的用户之间是否存在线程,因为它在内存中并且速度更快。
我也可以像这样在 Redis 中保存上面相同的信息(不是像我在 Cassandra 中那样使用两种方法,而是一种节省内存的方法。我们可以执行两次 GET 来检查它):
SET user:1:user:2:message_thread_id 123e4567-e89b-12d3-a456-426655440000
Run Code Online (Sandbox Code Playgroud)
因此,在发送消息之前,我可以先在 Redis 中检查两个用户之间是否存在线程。如果在 Redis 中找不到,我可以在 Cassandra 中检查(以防 Redis 服务器在某个时候关闭并且没有保存它),如果存在线程,则只需使用该 thread_id 插入新消息,如果没有,则创建线程,然后将其插入表中:
message_thread_by_user_ids
Run Code Online (Sandbox Code Playgroud)
使用上面的 SET 命令将其插入到 Redis 中。最后将消息插入:
message_by_thread_id
Run Code Online (Sandbox Code Playgroud)
好吧,现在棘手的部分来了。聊天的左侧部分没有静态排序顺序。顺序一直在变化。如果对话中有新消息,则该对话将转到顶部。所以我还没有找到一种在 Cassandra 中建模而不进行删除和插入的好方法。我必须删除一行然后插入它以便表对行重新排序。要删除一行并在表中插入一行,每次发送消息对我来说听起来不是一个好主意,但我可能是错的,我对 Cassandra 没有经验。
所以我的想法是我可以将 Redis 用于左侧部分,但唯一的问题是,如果 Redis 服务器出现故障,那么左侧最近的聊天对话将丢失,即使聊天本身将保留在 Cassandra 中。用户需要重新发送消息才能再次出现对话。
我想我可以通过以下方式在 Redis 中做到这一点:
每次用户发送消息时,例如,如果用户 1 向用户 2 发送消息,我可以这样做:
ZADD user:1:message_thread_ids 1510624312 123e4567-e89b-12d3-a456-426655440000
ZADD user:2:message_thread_ids 1510624312 123e4567-e89b-12d3-a456-426655440000
Run Code Online (Sandbox Code Playgroud)
排序集将跟踪按 unix 时间戳排序的最近活动对话的线程 ID。
但另一个问题是,每次加载此窗口时,我都必须执行 ZRANGE,例如在左侧获取 20 个最近的对话,然后在 Cassandra 中执行 20 个带有 LIMIT 1 的单个 SELECT 语句,以获取有关该窗口的信息。最后一条消息发送,这可能不是那么有效。我想我可以使用 HMSET 保存 Redis 中最近 20 个活动对话的最后一条消息的信息,其中包含最相关的信息,例如消息本身修剪为仅 60 个字符、last_message 时间戳、from_username、to_username、from_id、to_id、from_image、 to_image 和 message_id。
HMSET thread:123e4567-e89b-12d3-a456-426655440000 <... message info ...>
Run Code Online (Sandbox Code Playgroud)
但现在我必须跟踪并删除 Redis 中不相关的哈希映射,因为我不想保留超过最近 20 个的哈希映射,因为它会快速耗尽内存。我将从 Redis 和内存中获取最新的 20 个,如果用户向下滚动,那么我将从 Cassandra 一次获取 10 个。另一个问题是,如果 Redis 服务器出现故障,如果该对话是一个全新的对话,我可能会丢失应用程序左侧的对话。
我认为通过这种方法,只需添加新节点,我就可以在 Cassandra 端每秒获得大量写入,而 Redis 每秒可以执行 200 000 - 800 000 次操作,因此不应该进行删除和向排序集添加内容一个限制。由于 Redis 服务器之间会有一些来回,我可以尝试管道化 Redis 命令或编写 Lua 脚本,以便我可以一次性将指令发送到 Redis。
这是一个好主意吗?如何解决应用程序左侧显示活动对话的问题?像我建议的那样在 Redis 中执行此操作是一个好主意还是我应该以不同的方式执行此操作?
两者都是很好的解决方案。但瓶颈可能在哪里?
1)Redis有内存限制,不能超过。此外,当服务器关闭时,您会丢失数据。
2) 在扩展方面,redis 使用带有分片的主从拓扑,而 Cassandra 使用环形拓扑,其中每个节点都具有相同的写入和读取能力。
在我看来,我宁愿使用 Cassandra,因为它不如 Redis 快,但足够快并且非常容易扩展。
这是一个好主意吗?如何解决应用程序左侧显示活动对话的问题?像我建议的那样在 Redis 中执行此操作是一个好主意还是我应该以不同的方式执行此操作?
你的用户如何互相写信,我想你是用 websocket 来做到这一点的,不是吗?如果是,只需跟踪套接字 ID 并在套接字断开连接时将其删除。
另一个问题是,在哪里以及如何检索某个人(照片左侧)的好友 ID?
| 归档时间: |
|
| 查看次数: |
2597 次 |
| 最近记录: |