十二赞的商品搜索实现

其实要说很惭愧,虽然我是搜索出身人士,但是十二赞的搜索功能其实在目前是做得非常弱鸡的。不过还是介绍一下这个弱鸡的系统。

十二赞的搜索是基于elasticsearch的。因为业务量较小,所以,到目前为止,还没有用上elasticsearch强大可怕的集群功能。之所以觉得可以拿出来分享一下,是因为这个文案的投入是比较小的,效果尚可接受。

对于在小程序上开店的商家来说,一般商品数不多,很少于超过200的,所以在召加回率和精度上,我们只需要满足召回率就行了。即便搜索结果不够精准,因为商品少,用户一眼扫一下也能找到他想要的商品,搜索只是帮他从原有的上百个商品中把范围缩小到十分之一,就已经很有效了。
另外,也是因为团队小,我们的方案也特别注重精简,能不自己去写代码实现的,就不自己去写代码实现。

好了,上方案。
我们的商品数据是存在MySQL数据库中的,就用的阿里云的RDS。我们运行了一个go-mysql-elasticsearch,这个程序启动时会运行一次mysql_dump,把数据导出进入elasticsearch,然后接下来就会作为MySQL的一个slave,不断地读取MySQL的binlog,同步进elastichsearch。跟我们其他的服务一样,我们把这个elasticsearch和go-mysql-elasticsearch也封装进了一个docker 之中,只对外暴露9200端口来提供服务。

我们之前的ppt介绍过,十二赞的各种内部业务都已经微服务化,这个elasticsearch的服务,也不例外,它工作在es-sec.z.12zan.net:3000上,后端的实例可以有多个es的实例,通过http接口来请求elastichsearch的http接口时,网关 server会自动定向到负载最低的elasticsearch实例来提供服务。

这个方案运行不久,我们就发现一个问题,当我们的MySQL数据库的商品表新增了字段时,所有的后续的数据更新都无法同步的elasticsearch中去了。因为MySQL表的字段比elasticsearch中的字段多了(字段减少啊变更什么的都会导致这个问题)。

谢天谢地,因为Docker化,我们可以轻而易举地解决这个问题。我们的方案,当MySQL的数据表schema变化时,老的elasticsearch实例不变,继续提供服务,但是有缺陷,因为新增的数据没有同步进来;同时,我们新启一个Docker实例,这个实例启动约1分钟之后就同步进来了所有的商品数据,这时是两个elasticsearch的实例都在es-sec.z.12zan.net:3000上对外提供http接口服务,其中一个的数据是老的,一个实例的数据是全的。这时候我们关掉老的Docker上的实例就好了,就实现了索引的切换。用户可能会感受到新商品没有马上在搜索结果里体现,感觉搜索里的商品更新有延迟,但是基本上发现这个延迟的概率很小。

其实,淘宝的搜索的索引切换也有这么一个过程,不过要复杂的多,每天夜里要生成一个全量的索引,切换掉前一天的索引;然后另外有一个实时的搜索引擎,数据全放在内存里,只同步当天的数据变更,对外提供服务的接口将两份数据合并。