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为其保驾护航。速度又快,更不拍数据的丢失。

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


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

相关推荐
云姜.3 分钟前
线程和进程的关系
java·linux·jvm
是码龙不是码农5 分钟前
支付防重复下单|5 种幂等性设计方案(从初级到架构级)
java·架构·幂等性
曹牧5 分钟前
Spring Boot:如何在Java Controller中处理POST请求?
java·开发语言
heartbeat..5 分钟前
JVM 性能调优流程实战:从开发规范到生产应急排查
java·运维·jvm·性能优化·设计规范
WeiXiao_Hyy8 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇14 分钟前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
团子的二进制世界21 分钟前
G1垃圾收集器是如何工作的?
java·jvm·算法
long31626 分钟前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
rannn_1111 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习
灵感菇_1 小时前
Java HashMap全面解析
java·开发语言