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

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


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

相关推荐
Asthenia04128 分钟前
用Pandas轻松搞定Excel数据提取:新手也能学会的Python技巧
后端
uhakadotcom9 分钟前
Google Cloud Dataproc:简化大数据处理的强大工具
后端·算法·面试
兰亭序咖啡18 分钟前
学透Spring Boot — 018. 优雅支持多种响应格式
java·spring boot·后端
小雨凉如水22 分钟前
docker 常用命令
java·docker·eureka
审计侠25 分钟前
Go语言-初学者日记(八):构建、部署与 Docker 化
开发语言·后端·golang
AskHarries37 分钟前
如何开通google Free Tier长期免费云服务器(1C/1G)
后端
码界筑梦坊40 分钟前
基于Django的二手交易校园购物系统
大数据·后端·python·信息可视化·django
高山流水&上善41 分钟前
医药档案区块链系统
java·springboot
东方珵蕴42 分钟前
Logo语言的区块链
开发语言·后端·golang
烛阴43 分钟前
从零到RESTful API:Express路由设计速成手册
javascript·后端·express