乐趣区

关于后端:基于ElasticSearch的搜索平台在哈啰出行的应用

明天我要分享的主题是《基于 ElasticSearch 的搜寻平台在哈啰出行的利用》。置信大家对 ElasticSearch 都有肯定的理解。明天次要分享一下基于 ElasticSearch 的搜寻在出行行业的利用,其中会波及到一些 ES 相干的技术细节。最初从大的方面来分享落地平台的一些教训和集体的一些感触,心愿明天的分享可能给大家带来一些思考和启发。

建设背景

首先我想介绍一下咱们团队的整体业务,不晓得当初有多少人装置了咱们哈啰出行的 APP,没有装置咱们的 APP 应该至多也用过咱们哈啰出行的服务。

我猜测很多人对于哈啰出行的印象还停留在共享单车的时代。其实远远不止,目前哈啰出行的业务,不仅仅有共享单车,共享助力车,咱们还有规模还比拟不错的四轮业务,次要是逆风车和打车。咱们还能够在哈啰出行的 APP 间接订火车票和酒店,还能够买景点门票。哈啰出行还造电动车售卖,它的无钥匙体验十分的好。另外还有租车等各种各样的生存服务类的业务,大家有趣味能够去体验一下哈啰出行的业务。正是因为有各种各样的业务状态,才给了咱们中台和平台一个非常广阔的成长空间。

咱们团队目前负责的业务有四轮的匹配引擎,这个待会会讲到。而后两轮有智能调度引擎。可能很多人不晓得调度是什么意思,调度的意思就是用算法的能力,把这个站点的单车搬往另外一个中央,投放到另外一个站点,用算法的能力取得一个全局收益的最大化。而后咱们还有以平台的能力提供的搜索引擎和举荐引擎,咱们还有语音辨认服务,有超过 200 维的深度模型的在线排序打分服务。

当然咱们这些能力都是依靠于更底层的中台能力,比方咱们的存储平台给咱们提供存储服务。而后咱们还依赖了模型平台,特色平台、计算平台、数据开发平台等等这些能力都是咱们的一个底座。

2019 年我刚来哈啰出行的时候还没有搜寻平台,那个时候各个厂商曾经都有本人的搜寻平台。哈啰出行的各业务想要用搜寻服务的时候,他就会去找运维去拉 10 台机器或者十几台机器,而后本人搭一个 ES 集群,而后往这个群里同步数据。而后可能还要做一些相干的性能优化,业务可能把这个工程搭起来就曾经十分艰难,更不要说提供一些算法能力。所以咱们能够从这个图下面能够看到,2019 年哈啰的 ES 集群新增数量靠近 30 个。我就想能不能把我的搜寻教训用上,而后把这些业务常常用的这些性能形象积淀成平台,以平台的能力缩小业务的反复开发,这样的话就晋升业务的迭代效率。

同时把利用算法的同学也拉进来,为搜寻和举荐,提供算法能力。这样就能够为业务晋升工程效率和业务的算法成果。有了这个指标之后,我就开始着手构建搜寻平台。建平台其实是十分不容易的一件事件。之前在阿里的时候我也尝试做过平台,然而失败了。而后我就总结了一下失败的教训,我了解是这样。

第一点就是业务团队对咱们中台信任感缺失。一个景象是在互联网行业人员的流动性很强。大厂外面的一个常见的做法是起一个我的项目,而后我的项目上线之后也降职了,降职完了当前这个我的项目就没人管了,这个十分常见。

第二点业务团队基本上都有开发人力的冗余。他有能力去开发这个性能,他基本上就不会把这个事件交给内部团队,这个也是咱们做平台所遇到的一个窘境。所以我在做之前就整顿了一个大略的思路:

我先做一个场景,先把一个场景给攻关下来。而后我拿着胜利的案例,再笼络更多的业务。更多的业务带来了各式各样的业务需要,他会给我提各种各样的需要,在满足他们需要的同时,咱们平台的能力失去了一个很大的晋升。

平台的能力晋升之后,业务就更违心接入平台,这样造成良性循环。

建设的过程与挑战

场景一:司乘匹配引擎

于是咱们就开始找第一个场景,找到了四轮业务的逆风车场景。

乘客在哈啰 APP 上发单,乘客填入本人的终点、起点和登程工夫,而后造成了一个海量的乘客订单池。接着司机要来找单了,他也会到 APP 下面去发一个单,而后这个订单蕴含终点、起点和登程工夫。匹配引擎就会用司机的信息去海量的乘客订单外面去找最顺路的订单,而后依照顺路水平排序,供司机筛选。这个是咱们十分常见的一个场景。

当然乘客也能够反过来找司机,乘客找到司机当前只能发邀请,不能去做别的。而后还有其余的业务模式,比如说咱们司机能够看左近的乘客到哪个目的地的人最多,而后通过这种订单池的模式去选单,大家能够本人去体验一下。

原来的司乘匹配架构的外围问题,次要有两点。第一点原来是基于 PG 数据库来实现,它是一个离线计算的过程。在司机发单的一刻,触发一次计算把以后最顺路的或者是最适宜的乘客给匹配进去,而后存到缓存外面去。而后后续司机看到了就是缓存的后果,显然这个外面十分大的弊病,缓存之后新产生的一些乘客的订单,即便是再顺路,他也没有方法去让司机看到,这是最大的一个弊病。

而后我感觉第二个弊病是基于 PG 数据库的架构,横向扩大能力受到肯定限度。所以咱们就想到了能不能以 ES 用搜寻的这种技术栈来解决以后的架构问题。

基于 ES 匹配架构降级:匹配引擎

解决这个问题我感觉首先外围的要解决几个问题,第一个问题是咱们的数据同步怎么做?业务的数据是存储在数据库外面的,它不会间接往你的搜索引擎外面去写,这些数据库外面数据怎么在业务无感知的状况下做到一个秒级的实时同步,这是咱们首先要解决的一个问题。

数据同步:技术计划落地

第二个问题就是业务有很多业务计算逻辑,比如说顺路度怎么算,过滤的逻辑怎么实现,而后这些逻辑能不能在咱们 ES 集群面跑起来,这个也是一个问号。

而后第三个方面的话就是咱们的一个稳定性该如何保障?咱们晓得新旧零碎切换很容易呈现问题,尤其是在咱们这种外围交易链路外面稳定性十分重要。

咱们首先解决数据同步的问题。

过后大部分公司会抉择用 Java 开发,订阅 kafka 的数据,往搜索引擎写,这是最间接和简略的做法。

然而其实这外面要思考的问题很多,为什么我过后选了 Flink 作为咱们的技术选型?一方面阿里在 2017 年的时候,搜索引擎的数据同步曾经切到 Flink 上了。

我次要是看中了 Flink 的两点,第一点的话就是分布式的高可用和高扩大的能力,高可用的话就是 Flink 他本人有 failover 的一个机制,在故障产生的时候,他能够本人做故障转移和故障复原。他还有 Exactly Once 的能力,最初只管咱们只用到了 at latest once。

第二个方面,我感觉 Flink 形象能力很高,它下层的 API table 能够间接把业务的逻辑以 SQL 的模式提交上来的,这样的话就给了咱们把这个场景推广到更多场景的一个可能。

解决技术选型问题之后,咱们最初是怎么实现?批量的数据是通过批处理,从 PG 数据库读取。而后实时的数据是数据库通过 binlog 到 kafka。而后生产 kafka 外面的数据,而后实时的写到 ES 外面去,是不是有了 Flink 这个银弹之后,咱们什么都不必做就能够用了,其实也不是的。

在咱们这个场景上面其实要解决很多的问题,首先在我的集体的了解 Flink 更擅长于解决 log 类型的数据,log 和业务类型有什么差异?Log 类型的数据它是 append 的模式,它的数据在 Flink 的表外面是不可更改的,而咱们的业务场景它的数据是频繁的更新的,须要数据不停的更新。

技术挑战:Flink 双流 JOIN 的难题

这个时候,咱们看一下 Flink 的场景上面 join 会有哪些问题。右边的那些图就是 Flink 的基本原理,就是 Flink join 的时候它有很多窗口。而后左边下面这个图说的意思就是两个表的一个 join 过程。左表的数据来了之后,会跟左边的 state 区域进行 join,而后左边的数据来了当前会跟右边的 state 区进行 join。如果 join 不上就放到 state 的区,这里就很显著有一个很大的问题,就是咱们的窗口得开多大。而后咱们这个业务数据有效期是十分长的,大略 4~16 天。你这个窗口放 4~16 天,而后不停的追加,内存可能很快就会爆掉。

而后第二个问题,state 区外面它的数据是 append 模式的,同一条数据 (主键雷同) 就会产生 10 条甚至 20 条数据。这样的话一旦 join,就产生一个笛卡尔乘积的规模的写入,对 ES 的压力还是十分大的,那么怎么解决这个问题呢?

咱们就想了一个方法,就是写入了数据 insert 的音讯,咱们就间接把它放到 state 区外面存起来,这个数据是无限的。而后进来之后它还会产生 join,完了当前间接写到 ES。而后对于更新的数据咱们是独自把它拉进去,利用的是 ES 的一个局部更新的能力写入到 ES 集群。

而后左表的数据来了当前,我就更新左表的那些字段,右表的数据来了当前我就更新右表的一些字段,互不打搅,这样的话就解决了咱们方才那个问题。

同时在咱们基本上用的都是 Flink 的下层 API,这样的话想达到一个通用的目标。

做完这些之后是不是就能够上线了?当然不是。咱们解决第二个问题,第二个问题就是业务的逻辑怎么办?

技术创新:定制化引擎插件

业务的逻辑咱们是以 ES 的插件,而后发到咱们 ES 集群外面去的。上面的公式是咱们计算顺路度的一个外围的公式。能够去专利发明外面搜一下,外面有很多计算顺路水平的计算方法,而后咱们把这个算法集成到咱们 ES 集群外面,利用 ES 集群的分布式计算的算力去实现计算。计算完了之后用顺路度进行排序,这样的话咱们就能够找到最顺路的订单。

之后咱们还对这个插件的公布做了一些优化。咱们晓得 ES 的插件更新是须要重启 ES 集群的,对咱们的稳定性挫伤是十分大的,咱们起初做到的就是业务插件公布是能够热公布的,不必重启 ES 集群。而后做完了这些之后,咱们筹备上线了,业务成果还不错。

上线的过程中也遇到一点问题,把流量切 50% 的时候还顺利,但切到 100% 的时候就呈现了一些大量的超时,首先能够思考的就是你的机器容量有余,能想到的最快的方法就是加机器。然而过后咱们还发现一个细节,就是咱们的 CPU 其实没有到 100%。即便无奈到 100%,至多应该能到 70 到 80%,但其实 CPU 始终没有超过 50% 的,这里就给了咱们很大的一个疑点,咱们就围绕这个疑点去找这个问题,这外面必定是有什么问题的。

我了解的性能优化的一个过程,最间接的方法就是去查监控,而后看你的瓶颈在哪里。

技术挑战:引擎性能调优

其中最常见的咱们 CPU 上不去,一个就是锁,第二个的话就是等 IO。然而过后咱们遇到一个艰难,就是咱们的监控零碎不太欠缺,监控零碎的各项指标没有什么特地,或者基本看不出来。过后咱们配套工具也不是太欠缺,就连压测工具都是咱们本人去搭建的,压测环境是咱们本人搭的,工具是咱们本人找的,所以过后我甚至还狐疑过压测工具是不是不对,并用了两套压测形式来进行验证。最初发现都是 CPU 只能到 50%,这就是一个很大的疑点。其实在做 ES 性能优化的时候,还比拟难的一点是 ES 的配置项十分多,有很多的配置项跟性能都是密切相关的,包含你分片数量的设置,包含你的 jvm,gc 的这种设置对性能可能都会有影响,然而到底是哪个选项有问题,过后始终试了很多种办法,如果说监控零碎看不出来,剖析不进去,你只能是去尝试,而后去猜测和验证。

我感觉性能优化其实就是一个剖析,猜测,而后验证这么一个过程。起初通过咱们两天的一个寻找,最终还是发现一点货色,ES 外面有一个配置项,他是做磁盘性能优化的,这个配置项如果是日志集群其实是没有任何问题的。然而在咱们这个场景下就是咱们的业务数据,业务数据里的点集的数据特地的大。

点集数据是什么?就是用户发的一个订单外面有个终点有个起点,通过高德导航的一个门路布局之后生成一条布局门路,这个门路的话大略是 10 米或者 5 米一个点,而后一个点的话是一个经度和一个纬度。而后经纬度的话基本上是一个 5~7 位的浮点数,这个数据的话存下来就跟十分的大,咱们的很多 IO 都是 CPU 在期待磁盘外面读取点集的数据。找到之后咱们就用 ES 的 mmap 来优化数据加载过程,以及起初咱们对这个门路进行了抽稀和压缩。门路的抽稀是什么意思?就是咱们在做顺路度计算的时候,如果这两个点之间是很长的一段距离都是直线,那么直线两头的点都能够去掉,比如说这个点到另外一个点外面,直线有两公里或者三公里,这外面的点都能够删掉而不影响最终的计算结果。

这这个事件做完之后,咱们的吞吐量就下来了,吞吐量间接晋升了 4 倍,均匀响应工夫也升高了大略 50%,而后 CPU 也终于能打满。在这里我就分享一下本人的一些感想:你本人在做性能优化,或者是遇到一个故障,或者是在做告警解决的时候,你有没有想过你的问题有没有一些正当的解释,就外面的所有的细节都不要放过,可能这个细节外面就暗藏了很多未知的坑,未来就是定时炸弹,明天不暴发,未来有一天会暴发。所以你不要放过任何一个细节,每一个细节都应该有它一个正当的逻辑解释。

最初在这里再聊一下稳定性,稳定性是咱们陈词滥调的问题,稳定性的话题基本上能够再开一个课堂,再讲 50 分钟都不为过。

稳定性计划

我了解的稳定性,集体总结的稳定性三板斧就是监控、告警加预案。监控就是你配置的监控大盘,告警产生之后,你有没有去看,有没有去认真的剖析。而后告警风暴的时候,你有没有去调整告警的阈值,让他可能在出故障的时候呈现,不该呈现的时候不呈现。而后预案在你呈现真正的呈现故障之后,你本人去能不能有一个开关或者是有一个配置,能够间接把故障给打消掉或者降级。

以前都是靠我集体的一些经验总结,起初发现谷歌发了一篇论文,把这些经验总结成为一个实践的模式。右边金字塔的图案就是谷歌的论文外面摘抄进去的,有趣味的能够去搜一下。

在咱们这个场景上面,咱们以前是三板斧,起初我又特意加了一板斧叫四板斧,增加了一个人的因素,为什么我增加这么一个人的因素呢?

这里我能够给大家分享一个故事:去年 720 的时候,咱们哈啰单车产生了大面积的故障,我置信应该很多人受到了影响,因为过后很多人下班都早退被罚款。

而后故障复原的工夫特地长,为什么特地长呢?过后我听到一个有意思的事件,前天早晨有一个外围开发回家的时候,他说当天早晨回家的时候下雨了,放心下雨了淋湿笔记本电脑,就没把笔记本电脑带回去。后果第二天产生故障的时候,他被堵在出租车上,什么事件都做不了,而后导致故障复原的工夫特地长。我感觉这个是一个人很重要的一个因素。起初公司产生了大量的资损,对吧?这个资损不晓得能买多少笔记本,切实是得失相当。

咱们还能够在网上看到,在火车站外面有个程序员,常常一坐下来就拿个笔记本进去开始解决生产故障。还有咱们印象很深的,当年新浪微博鹿晗颁布恋情,而后新浪的工程师在婚礼现场去扩容和解决故障的,那个事件置信大家都还历历在目。其实这外面就和人有很大的一个因素。无论你的实践再强,最初都是落实到人下面的。咱们做这种在线零碎,7×24 小时 OnCall 应该所根本具备的一个素质,我感觉是这样的。

而后咱们这个场景上面的监控,起初咱们是怎么做的,咱们能够看到这个坐标图,坐标图的横坐标的话从左到右的话,集群、单机和索引维度,咱们的监控的力度是越来越细。而后从下往上看,就是咱们从系统监控到业务监控,咱们的监控是越来越具体,而后在做监控项的时候,其实也是有一些技巧,咱们最外围的黄金指标有没有笼罩到。右下角 Table 就是谷歌论文外面的一个黄金指标。罕用的吞吐量,提早和谬误三个。

而后咱们在做零碎设计的时候也思考到零碎稳定性。咱们选用了 Flink 作为咱们的技术选型,同时咱们的索引除了在线服务的以外,咱们在我的后盾外面点击一个重建索引的按钮之后,能够马上去重建拉起一条新的索引。因为在故障产生产生之后数据库外面的数据是全量的残缺的,而后这个时候你能够疾速的做一个故障复原,把所有的读流量切到新建的一个索引。同时咱们索引能够是多个索引并行的。新的索引有问题切回旧索引,旧索引有问题能够切回新的。这就是咱们零碎外面做的稳定性的设计。

基本上说完这些之后,出行的四轮出行的场景基本上就 ok 了。咱们过后业务成果还是不错的,咱们的完单量晋升了靠近 50%,而后接单人数晋升了靠近 40%。

前期的话我就做了一些搜寻平台推广的事件,次要以技术分享的模式去让大家晓得我搜寻平台所具备的一个能力,而后去找业务去聊,让他理解我的搜寻平台,并且让他接进来。

而后接下来的这些业务又给我提了很多需要,包含它的数据是存储在 mysql 库外面的,它的数据不是在 kafka 外面,是 rocketmq 的,以及它的数据 binlog 的格局也是不一样的,这些场景咱们都做了都兼容了。所以咱们前期接入的业务也越来越多,这样的话就造成了一个十分好的良性循环。

成绩及将来瞻望

整个我的项目下来,咱们的完单量晋升了 49.8%,接单人数晋升了 37%,获得了不错的问题。

同时,咱们也制订可搜寻平台前期的打算。

将来的话次要是两个方面的,咱们后期做的一些工作,还只是实现了搜寻工程的一些事件,让搜寻的工程用起来很容易很不便,然而叠加算法的能力还是靠咱们人肉的开发的。比如说当初咱们加一个用意辨认的组件,或者是加一个拼写纠错的组件,咱们还须要去拉 NLP 的同学,而后让他们给咱们开发一个模型,在线去调用,这样的话其实是一个非常复杂的过程,我冀望未来的话在咱们平台下面有很多的算法组件,包含搜寻前的包含搜寻后的一个精排的组件,都能够随时的去筛选和应用。而后稳定性方面的话,咱们正在做异地多活的话,今年年底应该会上线。

本文系哈啰技术团队出品,未经许可,不得进行商业性转载或者应用。非商业目标转载或应用本文内容,敬请注明“内容转载自哈啰技术团队”。

退出移动版