3ms找出微信「共同好友」,揭秘其背后的技术推手

1、引言

在微信里,关注了某个公众号或者进了某个群,经常会显示你有几个共同的朋友。如图:

作为一个技术出身的人,必然要探究一下其背后技术。如果是自己去实现应该怎么实现呢?

2、抽丝剥茧

我们要探究两个圈子的共同的朋友,首先我们得有两个圈子,以上面的关注的公众号编程朝花夕拾为例。一个是我自己的朋友圈子,另一个去圈子则是关注这个公众号的圈子,共同的朋友就是两个圈子共同的朋友。

使用数学思想就可以将两个圈子抽象成集合(A,B),朋友抽象成集合中的元素。公共的朋友就是两个集合的交集(A ∩ B)。

至此,就演变成两个集合的运算了。

3、Java中的集合运算

集合的运算我们需要借助apache 的工具类,maven以来如下:

xml 复制代码
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>${latest.version}</version>
</dependency>

集合的交集(A∩B)

两个集合都有的元素:

java 复制代码
@Test
void contextLoads() {
    // 我的朋友圈
    List<String> myFriends = List.of(
            "张君宝","杨过","郭靖",
            "黄蓉","赵敏","周芷若",
            "李莫愁","小龙女","令狐冲"
    );
    // 公众号朋友圈
    List<String> gZHFriends = List.of(
            "张君宝","黄蓉","赵敏",
            "周芷若","令狐冲", "胡斐",
            "欧阳锋", "李寻欢", "林平之",
            "岳不群", "任我行"
    );

    // 共同的朋友
    Collection<String> commonFriends = CollectionUtils.intersection(myFriends, gZHFriends);
    // 输出结果:[赵敏, 张君宝, 令狐冲, 黄蓉, 周芷若]
    System.out.println(commonFriends);
}

集合的并集(A∪B)

两个集合所有的元素

java 复制代码
@Test
void union() {
    // 我的朋友圈
    List<String> myFriends = List.of(
            "张君宝","杨过","郭靖",
            "黄蓉","赵敏","周芷若",
            "李莫愁","小龙女","令狐冲"
    );
    // 公众号朋友圈
    List<String> gZHFriends = List.of(
            "张君宝","黄蓉","赵敏",
            "周芷若","令狐冲", "胡斐",
            "欧阳锋", "李寻欢", "林平之",
            "岳不群", "任我行"
    );

    // 两个所有的朋友
    Collection<String> unionFriends = CollectionUtils.union(myFriends, gZHFriends);
    // 输出结果:
    // [任我行, 小龙女, 赵敏, 张君宝, 岳不群, 李莫愁, 令狐冲, 
    //  杨过, 黄蓉, 欧阳锋, 李寻欢, 郭靖, 周芷若, 胡斐, 林平之]
    System.out.println(unionFriends);
}

集合的差集(A-B)

集合A中不包含集合B的所有元素

java 复制代码
@Test
void subtract() {
    // 我的朋友圈
    List<String> myFriends = List.of(
            "张君宝","杨过","郭靖",
            "黄蓉","赵敏","周芷若",
            "李莫愁","小龙女","令狐冲"
    );
    // 公众号朋友圈
    List<String> gZHFriends = List.of(
            "张君宝","黄蓉","赵敏",
            "周芷若","令狐冲", "胡斐",
            "欧阳锋", "李寻欢", "林平之",
            "岳不群", "任我行"
    );

    // 共同的朋友
    Collection<String> subtractFriends = CollectionUtils.subtract(myFriends, gZHFriends);
    // 输出结果:[杨过, 郭靖, 李莫愁, 小龙女]
    System.out.println(subtractFriends);
}

思考

这个基于内存的集合运算,如果是你,你会用在微信这个产品上么?

效果是可以实现的,但是最大的缺陷就是基于内存,如果遇到宕机或重启,数据造成数据的丢失。或者说每次启动都需要将各个圈子的数据加载到内存中才可以了。微信的数据量如此庞大,纯内存能不能支撑呢,会不会造成内存溢出呢?这些都是有可能的。

很显然这种方案,可以但是不那么合适。那么有没有什么其他更好的办法呢?那就得拿出我们的大杀器Redis,轻松应对。

4、Redis实现海量关系数据实时计算

SetZSetRedis中的无序集合和有序集合都可以实现集合的运算,本章以Set集合为例。

数据准备

shell 复制代码
# 添加我的朋友圈
sadd myFriends "张君宝" "杨过" "郭靖" "黄蓉" "赵敏" "周芷若" "李莫愁" "小龙女" "令狐冲"

# 添加公众号的朋友圈
sadd gZHFriends "张君宝" "黄蓉" "赵敏"  "周芷若" "令狐冲" "胡斐" "欧阳锋" "李寻欢" "林平之" "岳不群" "任我行"

交集(A∩B)

shell 复制代码
# 共同的朋友
sinter myFriends gZHFriends

因为Redis客户端的问题,展示成16进制的内容了,翻译之后和集合运算的结果相同。

并集(A∪B)

shell 复制代码
# 所有的朋友
sunion myFriends gZHFriends

这里就不做字符的翻译了,合计15个朋友,同集合运算的结果一致。

差集(A-B)

shell 复制代码
# A中不包含B中的所有朋友
sdiff myFriends gZHFriends

同样获取的结果和集合运算一致。

Zset同样可以实现,并且能够保持有序。

5、小结

看似小小的功能,里面其实蕴藏着许多设计思想。看似每一种方式都可以实现,但总有场景不适合的,这也是技术选型的难点。

Redis有其得天独厚的优势,不仅因为它是基于内存计算的,更重要的是它有持久化的功能,RBD和AOF为其保驾护航。速度又快,更不拍数据的丢失。

这就是一个程序员的日常和思考。


关注我的公众号:【编程朝花夕拾】,可获取首发内容。

相关推荐
mldong2 小时前
C# 开发者也有自己的轻量工作流引擎了:NuGet 装包,5 分钟跑通一条审批流
后端·c#·.net
xcl09253 小时前
幼儿托育系统开发实战:从需求分析到上线全流程指南
java·大数据·需求分析
吴声子夜歌3 小时前
ApacheCommons——commons-cli(命令行参数解析)
java·开发语言·apache
小小猪的春天3 小时前
Java 手写第一个 MCP Server:Spring AI MCP 半小时跑通
java·人工智能·spring boot·ai编程
find1star3 小时前
LeetCode 141:环形链表
java·算法·leetcode·链表
爱学堂IT分享4 小时前
鹤翔老师pmp项目管理培训认证考试课 (直播录播题库送学时证明)【共58课时】-51CTO学堂
后端
今天AI了吗4 小时前
DeepSeek Harness 深度解析:从评测架构到实战落地
java·网络·数据库·人工智能·架构·java-ee
王的宝库4 小时前
Go 项目结构:从单文件到标准工程布局
开发语言·后端·golang
许彰午4 小时前
27-AuthService登录链路
java·低代码·架构
泡海椒4 小时前
JQuick-Curl 性能分析:并发场景下的性能表现与调优,第三方接口调用不只要快写也要稳跑
java·开发语言·okhttp