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

背景

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

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

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

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

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

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

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

技术选型

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

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

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

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

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

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

MySQL

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

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

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

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

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

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

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

https://docs.pingcap.com/zh/tidb/stable/hardware-and-software-requirements

Cassandra

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

https://cassandra.apache.org/doc/latest/architecture/overview.html

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

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

https://blog.discord.com/how-discord-stores-billions-of-messages-7fa6ec7ee4c7

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

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

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

可以看看这个对比,了解一下两者的异同:https://www.scnsoft.com/blog/cassandra-vs-hbase

如果选择要用 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<string>("msg_body");
    var sender_role = row.GetValue<sbyte>("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 对聊天记录这个场景的存储还是有一定优势的,可以应对高速的数据增长,而不用在业务代码层做过多的适配;部署相对简单,无特殊依赖,运维成本相对较低。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于存储聊天记录,MongoDB是一个非常适合的选择。MongoDB是一个开源的NoSQL数据库,它使用文档模型来存储数据。你可以创建一个集合(collection)来存储聊天记录,并将每个聊天记录作为一个文档(document)插入到集合中。 在MongoDB中,你可以创建一个聊天记录集合,并定义适当的字段来存储记录的信息,例如发言者、时间戳、内容等。每个聊天记录将作为一个文档插入到集合中。这样,你可以使用MongoDB的查询功能来检索、过滤和分析聊天记录。 下面是一个简单的示例,展示了如何使用Python的pymongo库将聊天记录插入到MongoDB中: ```python import pymongo # 连接MongoDB数据库 client = pymongo.MongoClient('<mongodb连接字符串>') # 获取聊天记录集合 chat_collection = client['<数据库名>']['<集合名>'] # 插入一条聊天记录 chat_record = { 'speaker': 'Alice', 'timestamp': '2021-01-01 12:00:00', 'content': 'Hello, how are you?' } chat_collection.insert_one(chat_record) # 查询聊天记录 results = chat_collection.find({'speaker': 'Alice'}) for result in results: print(result) ``` 在上面的示例中,你需要替换`<mongodb连接字符串>`、`<数据库名>`和`<集合名>`为你自己的MongoDB连接信息和集合名称。然后,你可以使用`insert_one()`方法插入一条聊天记录,并使用`find()`方法查询特定条件的聊天记录。 请注意,这只是一个简单的示例,你可以根据自己的需求进行扩展和优化。另外,确保在使用MongoDB时遵循安全规范,例如进行适当的身份验证和权限管理,以保护聊天记录的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值