使用双Bloom过滤器优化几千万用户产品推荐


Klaviyo分享了他们如何使用双Bloom过滤器优化产品推荐引擎,以有效地排除已经从营销活动中购买的商品。他们不需要查询每个收件人两年的购买数据,而是预先计算每个公司的每月和30天的Bloom过滤器,并将其存储在Redis中以进行快速会员检查。这大大减少了查询负载,改善了活动渲染时间,并在不给基础设施带来压力的情况下为BFCM等高流量事件平滑扩展。

布隆过滤器(Bloom Filter)这玩意儿可帮了Klaviyo公司大忙,让他们给几千万用户发个性化推荐邮件的时候,不再“卡壳”!

发邮件送推荐,为啥会“卡壳”?
你想啊,Klaviyo得给好多好多人发推广邮件,而且还要推荐每个人可能喜欢的东西。最关键的是,不能推荐人家已经买过的东西,不然多尴尬,感觉公司一点都不了解你。

但是,要检查一个人在过去两年里买过啥,这可不是个小工程。几千万用户,每人两年的购买记录,那数据量简直是天文数字!每次发邮件都要去翻这些历史记录,就像你要在一座巨大的图书馆里,快速找到每本书是不是某个人借过的。这图书馆太大了,找起来慢吞吞的,电脑都得累趴下。

在没有布隆过滤器之前,Klaviyo是怎么做的呢?

  • 它会去查过去两年的购买数据。
  • 每次查一小批人(比如500个收件人)的数据,然后把他们已经买过的商品过滤掉。
  • 但是!如果一个活动有几千万收件人,那就得重复这个过程几万次!
  • 每次查询,电脑都得扫描整个两年的大数据集,找出这些人的购买记录。
你想想,这就像你要找500个同学各自借过的书,每次都要把图书馆里所有的书都翻一遍!这速度,简直比你家99年的拨号上网还慢,而且电脑还容易“罢工”,特别是在“黑五网一”(BFCM,国外购物狂欢节)这种流量爆炸的时候,系统根本撑不住!

神秘的“黑科技”——布隆过滤器登场!
为了解决这个“卡壳”的问题,Klaviyo请出了一个超级厉害的“帮手”——布隆过滤器。

布隆过滤器是个啥?
简单来说,布隆过滤器就是一个超级快速的“查重神器”。它能帮你很快地回答一个问题:“这个东西,是不是在我的清单里?”

它有啥特点呢?

  • 速度快到飞起:检查一个东西在不在,几乎是瞬间完成。
  • 超级省空间:它不像传统的清单,把所有东西都完整地存起来。它用一种很聪明的方法,只用一点点内存就能记住一大堆东西。
  • 有个小毛病——可能“误报”:这个是重点!布隆过滤器可能会犯一点点小错误,它有时会告诉你:“这个东西在清单里!”但实际上,它可能不在。不过,它永远不会犯“漏报”的错误,也就是说,如果一个东西真的在清单里,它就一定会告诉你它在,绝对不会说不在。

布隆过滤器怎么工作的?
它就像一个特殊的“表格”,里面全是0和1。当你要往里面“记住”一个东西时,它会用几个“咒语”(哈希函数),把这个东西变成几个数字,然后在表格的对应位置上标记为1。

当你要检查一个东西在不在的时候,它会再次念同样的“咒语”,如果对应的位置都是1,它就说:“嗯,这个东西可能在!”。如果有一个位置不是1,它就肯定地说:“这个东西不在!”

举个例子:
你有个“我最喜欢的食物”布隆过滤器。

  1. 你把“披萨”加进去:它念咒语,比如把披萨映射到表格的第2位和第5位,然后把这两位标记为1。
  2. 你把“汉堡”加进去:它念咒语,比如把汉堡映射到表格的第1位和第5位,然后把这两位标记为1。
  3. 现在你问:“炸鸡在我喜欢的食物里吗?”它念咒语,比如炸鸡映射到表格的第3位和第6位。如果这两位都是0,它就说:“不在!”
  4. 你再问:“披萨在我喜欢的食物里吗?”它念咒语,映射到第2位和第5位。如果这两位都是1,它就说:“在!”

那“误报”是怎么来的呢?
因为这个表格是固定大小的,如果加的东西越来越多,不同的东西可能念咒语后,映射到表格的同一个位置,导致这个位置被标记为1。所以,当一个新东西来检查时,它对应的位置可能都是1,但实际上它并没有被真正地“记住”过,这就是“误报”。

Klaviyo怎么用布隆过滤器解决问题的?
Klaviyo用布隆过滤器来“记住”用户过去两年买过的所有商品。具体怎么做呢?

  1. 准备两个“黑科技清单”:
    • 两年清单:这个清单每个月更新一次,里面包含了用户过去两年的所有购买记录。因为大部分购买记录变化不大,所以不用频繁更新。
    • 30天清单:这个清单在每次发营销邮件前生成一次,里面只包含用户最近30天的购买记录。这样可以确保最新的购买也被考虑进去。
    • 为啥是两个? 就像你家里既有大的储物间(两年清单),放不常用的东西,也有个小抽屉(30天清单),放最近买的、常用的东西,这样找起来更方便,效率也更高。
  • 提前做好准备工作:
    • Klaviyo不再是每次都去查海量的购买数据,而是一次性把所有公司的购买记录都拉出来,然后生成这些布隆过滤器。这就像你提前把图书馆里所有书的借阅记录都整理好,而不是每次都去一本本翻。
  • 发邮件时,快速检查:
    • 当Klaviyo要给500个用户发推荐时,它会把要推荐的产品和用户的ID组合起来。
    • 然后,这些组合就会被快速地扔进布隆过滤器里检查:这个用户是不是已经买过这个产品了?
    • 如果布隆过滤器说:“可能买过!”,Klaviyo就会把这个产品从推荐清单里剔除掉。

    为什么选择Redis?
    Klaviyo选择用Redis来存放布隆过滤器,原因很简单:

    • Redis是“闪电侠”:它速度超级快,数据都存在内存里,特别适合这种需要高速查询的场景。
    • 自带“黑科技”支持:Redis本身就支持布隆过滤器,用起来非常方便,不用自己从头开始搭。
    • 批量处理,更快!:Klaviyo还用了个小技巧,不是一个一个地查,而是把几百万个检查请求“打包”起来,一起扔给Redis处理,这样效率更高,就像一次性送一大车快递,比一个一个送快多了。

    成果喜人,皆大欢喜!
    用了布隆过滤器后,Klaviyo的推荐系统发生了翻天覆地的变化:

    • 查询变快了:以前每次都得扫描两年的购买记录,现在大部分数据一个月才处理一次。
    • 邮件生成速度变快了:因为检查是否已购买更快了,所以生成营销邮件的时间也大大缩短。
    • 能处理更多用户了:系统可以轻松应对几百万用户的营销活动,再也不怕“黑五网一”了。
    • 数据团队不再“发飙”了:以前因为系统压力大,数据团队老是收到警报,现在终于消停了,甚至还收不到“友好提醒”了!

    总结一下:
    布隆过滤器就像一个高效又节省空间的“侦探”,它可能偶尔会有点小糊涂(误报),但绝对不会漏掉任何信息。在Klaviyo的例子里,即使偶尔“误报”,把一个没买过的东西误以为买过了,然后从推荐里拿掉,风险也很小,比系统卡死好太多了!

    这个故事告诉我们一个重要的道理:提前做好准备,把费力的计算提前完成,然后多次使用计算结果,这样能大大提高效率! 这就像你做数学题,如果能把中间的复杂步骤提前算好,后面遇到类似的问题就可以直接用结果,省时省力!

    当然,这个系统还有提升的空间,但布隆过滤器已经为未来的优化打下了坚实的基础,让Klaviyo能够更好地为数千万用户提供个性化的服务!