-
redis存放聊天消息设计
在获取聊天记录的业务中,首先,消息的具体内容是用hash结构,存放在redis中的
key是CHAT_MESSAGE_CONTENT_CACHE+{groupId},
hashKey 是messageId,
value是具体的消息内容。如下图所示
在查询历史记录时,则只会根据查询条件,到数据库查询所有满足条件的messageId,然后根据messageId,一个个获取消息的具体内容。获取操作是,先从redis中获取,如果没有,再查询数据库,查询到结果后就再放到redis中。
@Override
public List<ChatMsg> getMessageHistory(Integer count, String groupId, Long endTime) {
List<ChatMsg> chatMsgHistoryList = new ArrayList();
List<String> idList = Lists.newArrayList();
QueryWrapper<ChatMsg> queryWrapper = new QueryWrapper();
queryWrapper.select("message_id")
// .last("limit " + count)
.orderByDesc("created_time").lambda()
.eq(ChatMsg::getGroupId, groupId)
.lt(ChatMsg::getCreatedTime, DateUtil.date(endTime));
if (null != count) {
queryWrapper.last("limit " + count);
}
List<ChatMsg> chatMsgList = list(queryWrapper);
if (CollectionUtils.isNotEmpty(chatMsgList)) {
idList = chatMsgList.stream().map(ChatMsg::getMessageId).collect(Collectors.toList());
//获取消息的具体内容
for (String messageId : idList) {
chatMsgHistoryList.add(getByMessageId(groupId, messageId));
}
}
return chatMsgHistoryList;
}
可以看到getByMessageId方法,就是根据messageId 一个个获取消息具体内容(此处可以优化成一次性获取最好)。然后返回给前端
此处的几点思考:
- 为什么不把messageId也缓存?
因为聊天过程中,用户会不断的发送消息,不断的有新的messageId产生,如果messagId也缓存。会导致在分页获取聊天记录,和更新消息的时候,复杂度大大增加。为了这点性能导致代码复杂度增加太大,不值得。
因此在获取聊天消息(主要是分页获取聊天记录)的业务中,都是先直接到数据库查询所有符合条件的messagId。注意此处是只查询messageId 这一个字段。这样可以避免数据库查询大量的字段出来,导致性能的损耗,同时又可以保证数据的正确性,避免此处的业务代码太复杂。
- 为什么要单独缓存消息的内容呢?
因为在很多的功能中,比如获取历史记录,获取被合并消息等,都需要根据messageId 获取消息的具体内容,或更新缓存或删除缓存,单独缓存可以很方便的实现改成操作。
例如某条消息已读了,就直接把缓存中是已读属性直接删掉(只是删除某个属性),下一次查询该属性的时候,发现为空,才查询数据库,也只查询对应的一个字段出来。避免仅仅只是因为某个属性更新了,就得把所有字段都从数据库查询出来。
同事把对应的属性查询出来,set回缓存内,重新设置过期时间为10分钟。这样可以让查询频率最高的数据尽量久的驻留缓存,降低查询数据库的频率。