聊一聊关于聊天记录的存储 您所在的位置:网站首页 qq文稿和数据是聊天记录吗 聊一聊关于聊天记录的存储

聊一聊关于聊天记录的存储

2023-09-11 13:39| 来源: 网络整理| 查看: 265

背景

即时通讯(Instant Messaging),也就是我们常说的 IM,其实在很多业务场景上都会有或多或少的应用,有的会是核心,有的会是辅助。

既然是聊天,那么必然就会产生聊天记录,而且聊天记录随着人数的增加和时间的推移,很容易出现爆炸式的增长,这个对存储其实压力是很大的。

举个大家都很熟悉的例子,一个群聊,几分钟不看,再打开就是 99+ 的未读消息。

把即时通讯这个技术,放到医疗环境下,也是同样适用的。

患者去线下医院看病,肯定离不开和医生的问答,这些问答,对系统来说,其实都是聊天记录。

如果把这个场景放到线上进行,也就是正常的和我们在微信聊天那样了。

说了那么多有的没的,也算是把大背景交代了一下,那么接下来就看看这个聊天记录存储的选型吧。

技术选型

既然要存储,那么肯定就会有很多选择,关系型数据库,非关系型数据库等。

当然这个很大程度上是和具体业务场景挂钩的,离开了业务场景,基本就是在空谈。

在医患关系里面的聊天记录,是一个十分十分核心的内容,并且必需要长期保存,不能丢,可查询。

并且这些聊天记录是和某次问诊强关联的,所以单独拿出几条聊天记录出来,是没有意义的,因为他们没有关联,串联不起来。

按照以往的经验看,一次问诊,医生和患者之间的聊天记录在 50 条之内的占据了大部分,超过50条的,占少数。

这个和其他的 IM 情景可能不太一样。

MySQL

业务开始,大概率就是选用 MySQL 去存了这些数据了,单库单表,但是这种情况下很容易达到单表千万和上亿的级别。

后面面临的基本就是分库分表的操作了。

分库分表,基本就是根据问诊号去进行哈希,然后放到不同的库不同的表。

这里会有一个不确定因素,分多少个库和分多少张表?

分的多,囊中羞涩;分的少,再一次达到量级的时候又要重新分,大动干戈,这个时候最怕的就是动到了哈希的规则。

所以选 MySQL 的话,到了中后期确实还是会有一点力不从心。线性扩展对这一块还是非常重要的。

如果想改动小,避免分库分表,或许可以试试 TiDB,但是它要的配置,也不是中小企业所能接受的,80% 以上的概率会被 Pass 掉。

docs.pingcap.com/zh/tidb/sta…

Cassandra

Cassandra 是一个分布式、无中心、弹性可扩展的 NoSQL 数据库,基于 Amazon Dynamo 的分布式设计和 Google Bigtable 的数据模型。

cassandra.apache.org/doc/latest/…

为什么考虑选型 Cassandra 呢? 对上面说的医患场景,严格上是属于写多读少的,查询基本只会基于问诊号去查询,这个是相对比较明确的。

Discord 在 2017 年的时候有一篇博客讲述了他们是怎么存储数十亿消息记录的 ,说的比较详细了。

blog.discord.com/how-discord…

其实他们选数据库的诉求,也是符合大部分涉及 IM 这一块的。

老黄也是从这里受到了启发,认识了这个数据库。

经常拿来比较的话,应该是 HBase,一个在国内火,一个在国外受欢迎。

可以看看这个对比,了解一下两者的异同: www.scnsoft.com/blog/cassan…

如果选择要用 Cassandra, 那么数据模型的设计,一定是所有环节中最为重要的一步,如果这一步没有做好的话,那后面基本上会是灾难级别,基本不能愉快的玩耍。

那么对医患关系里面的这个聊天模型其实比较简单。

CREATE TABLE IF NOT EXISTS messages( inq_id text, send_time bigint, sender_id text, sender_role tinyint, msg_type tinyint, msg_body text, PRIMARY KEY (inq_id, send_time) ) WITH CLUSTERING ORDER BY (send_time ASC)

对照正常的 IM 群聊, 这个问诊号 (inq_id) 就可以认为是一个群聊,一个频道。

为什么没有消息Id这样的字段呢?多来源,非自研,无实际意义。

下面再来看看如何在 C# 里面进行操作, 这里用的是 DataStax 提供的 CassandraCSharpDriver 客户端。

写入:

var cluster = Cassandra.Cluster.Builder() .AddContactPoints("127.0.0.1") .WithDefaultKeyspace("messaging") .Build(); var inqId = "xxxxxx"; var sendTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); var senderId = "xxxx"; var senderRole = 1; var msgType = 1; var msgBody = "zzzz"; string INSERT_SQL = @" INSERT INTO messages(inq_id, send_time, sender_id, sender_role, msg_type, msg_body) VALUES (?, ?, ?, ?, ?, ?) "; var session = cluster.Connect(); var stmt = session.Prepare(INSERT_SQL).Bind(inqId, sendTime, senderId, senderRole, msgType, msgBody)); session.Execute(stmt);

读取:

var cluster = Cassandra.Cluster.Builder() .AddContactPoints("127.0.0.1") .WithDefaultKeyspace("messaging") .Build(); var inqId = "xxxxxx"; string GET_MSG_SQL = @" SELECT * FROM messages WHERE inq_id = ? "; var session = cluster.Connect(); var stmt = session.Prepare(GET_MSG_SQL).Bind(inqId); var rowset = session.Execute(stmt); Console.WriteLine("患者\t\t\t\t\t医生"); foreach (var row in rowset) { // 解析从 cassandra 中返回的行 var msg_body = row.GetValue("msg_body"); var sender_role = row.GetValue("sender_role"); if (sender_role == 0) { Console.WriteLine($"{msg_body}\t\t\t\t\t"); } else { Console.WriteLine($"\t\t\t\t\t{msg_body}"); } } 写在最后

存储的选择其实还是有点门道的,根据不同的应用场景,找出比较适合当前场景的几个方案,再选择一个成本没这么高的。

Cassandra 对聊天记录这个场景的存储还是有一定优势的,可以应对高速的数据增长,而不用在业务代码层做过多的适配;部署相对简单,无特殊依赖,运维成本相对较低。

关注我的公众号「不才老黄」,第一时间和您分享老黄的所见所闻。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有