在elasticsearch中存储聊天消息的最佳方式

dal*_*ley 7 messaging chat store instant elasticsearch

我们目前正在我们的平台上实施Instant Messaging系统.我们需要为用户提供聊天记录,并能够显示用户拥有的最后5个对话(在Facebook上预览).

事实上,我们必须考虑如何存储所有这些数据.

我们正在使用Elasticsearch,我们认为这可以成为存储聊天消息并使其高度可用于读取操作的可靠解决方案.

我们的问题是,Elasticsearch中最好的数据结构是什么,以便我们的读取操作可以快速而不是太重.

我们想到了很多解决方案,这可能是我们想出的最好的解决方案.

我们的消息表示可能是:

{ 
   "ID" : 1,
   "sender" : "john",
   "receiver" : "doe",
   "content" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
   "date" : "timestamp"
}
Run Code Online (Sandbox Code Playgroud)

我们可以使用嵌套对象在对话中存储消息:

 {
     "ID" : 317,
     "participants" : "john, doe",
     "date" : "timestamp of the last received message",
     "messages": [
         {
            "ID": "49753",
            "sender" : "john", 
            "receiver" : "doe",
            "content" : " Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
            "date" : "timestamp" 
         },
         {
            "ID": "49754",
            "sender" : "doe", 
            "receiver" :"john",
            "content" : " Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
            "date" : "timestamp" 
         },....
               ]
}
Run Code Online (Sandbox Code Playgroud)

我们希望得到您对此解决方案的反馈意见,如果您有任何改进,我们也会提供您的解决方案.

提前致谢

Pet*_*ses 12

注意:这个建议的解决方案不仅从快速读取的角度(如OP的要求),而且还着眼于最小化索引开销.嵌套文档及其父项被写为单个块,因此在嵌套提议中添加每个附加"消息"将导致该对话中的所有先前消息和对话数据也被重新编制索引.

这是我对Facebook实现消息的一般方法的猜测(如果你使用Elasticsearch做类似的事情)

在此输入图像描述

预览 :(在Messages导航栏下拉列表中,以及" 消息"页面的左侧栏)

使用以下内容显示最近对话的摘要:

  • 三位最近参与者的综合爆头 - 最近三位对话参与者的有序列表.
  • 如果> 3,其他参与者的数量
  • 对话中最近消息的时间戳
  • 对话中最新消息的片段

消息窗格 :( 消息页面的中心列)

  • 显示对话中的所有消息
  • 消息窗格还重新用于消息搜索结果,显示包含搜索词的所有消息.

搜索框:

  • Typeahead :( 使用匹配的参与者名称完成对话)
  • 搜索:( 使用邮件正文中的匹配文本搜索邮件)

驱动预览的数据结构可能在conversation索引中(每个对话包含一个文档).每次将消息添加到对话时,都会更新这些文档.(非常类似于嵌套示例文档的父记录).

conversation数据源仅用于绘制预览(对话参与者的快速过滤,以确保您只能看到您参与的对话).

 {
     "ID" : 317,
     "participant_ids": [123456789, 987654321],
     "participant_names: ["John Doe", "Jane Doe"],
     "last_message_snippet" : " Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
     "last_message_timestamp" : "timestamp of the last received message",
 }
Run Code Online (Sandbox Code Playgroud)

这里没有嵌套b/c只需要最新的会话摘要,而不是消息.

性能会很快,因为不需要进行评分,只需对[当前用户]进行过滤,participant_ids然后按降序排序last_message_timestamp.

您可以使用participant_names字段上的Elasticsearch Term Suggester复制预先输入功能.

较少数量的conversation文档(与message文档相比)将有助于索引更新这个频繁的大规模功能.

为了进一步扩展此功能,可以使用索引每时间帧索引策略(例如,时间帧由对话的典型半衰期确定,作为示例).


显示消息中的特定conversation,你会查询message携带您的邮件文件的例子指数,但与该参考conversation

 {
     "ID" : 4828274,
     "conversation_id": 317,
     "conversation_participant_ids": [123456789, 987654321],
     "sender_id": 123456789,
     "sender_name: "John Doe",
     "message" : " Lorem ipsum dolor sit amet, consectetur adipiscing elit",
     "message_timestamp" : <timestamp>,
 }
Run Code Online (Sandbox Code Playgroud)

性能会很快,因为不需要进行评分,只需要过滤 conversation_id和降序排序message_timestamp.

搜索 信息跨交谈,你只需要建立索引的message字段.(遵循Facebook实施).

搜索查询将是[当前用户]过滤的搜索词,conversation_participant_ids其中降序排序依次为message_timestamp.

为了在检索对话的消息时最大限度地减少搜索集群中的串扰,您需要确保利用Elasticsearch的routing参数(在索引请求上)明确地在同一个分片上共同定位对话的所有消息,索引新消息时使用conversation_idas作为routing值.


注意:对于实现可能主要由具有文本搜索功能的其他文档存储或关系数据库构建的解决方案,Elasticsearch可能会变得过度.通过规范化conversationmessage在上面的例子中,不再依赖于Elasticsearch中的"嵌套".

此实现的弹性搜索优势包括过滤搜索结果的高效缓存,快速自动完成和快速文本搜索,但Elasticsearch的一个弱点是需要足够的内存来舒适地容纳所有索引数据.

Messaging应用程序的性能特征要求只能以任何频率访问或搜索最新的消息,因此在某些时候,如果您的应用程序需要扩展,您应该计划一种方法来存档较旧的,而不是最近的 - 在"冷存储"中访问的消息使得它们需要较少的应用程序资源,但仍然可以足够快地"解冻"以提供关键字搜索而没有过多的延迟.