赞
踩
简单来说就是接口改造;
其实接受这个项目的时候,还是认为比较简单的,因为该项目是已经有大佬通过golang写好了,项目也正在生产环境运行。我只需要将golang的代码看一遍,使用python将接口功能完成再实现一边即可;
那么就有人会问了,既然已经实现了,为啥还要多此一举再用python写一遍呢?问得好,我带着你的这个问题,问道了俺们的leader,答案是:因为之前实现的逻辑是直接查询的MongoDB数据库,存在数据不安全的问题,现在需要将之前的逻辑进行一次封装,由俺们实现接口功能,再通过原有的代码调用俺们的接口,来复现功能。
大概的意思如图所示:
功能得实现,用户体验也不能下降。
需要实现的功能有四个:
因为俺们是接手项目,所以必须得在动工之前搞清楚整个业务逻辑,中间得店铺推荐算法就不介绍了,但是通过一周的了解,却发现了很多存在的问题。
首先,在远古时期的美团店铺doris数据库数据一共有三千六百万左右,但是现在已经不能使用了,不是因为数据脏了,而是负责数据入库的大数据工程师当时没有按照建表要求将数据分别入库,而是全插到一张表了,虽然不知道后来他们是怎么实现功能的,但目前来说,已经是不能使用了。
其次,业务查询的MongoDB数据库数据,通过查询总量发现只有 一千一百万,显然数据量不对,差了将近两千万的量,这就面临着一个问题,就是要重新清洗数据,那么就又有一个问题,清洗的数据还不能自定义,需要按照原来的旧数据格式清洗。
最后,现存正在使用的一千一百万数据,必须原封不动的进行保存,且这批数据需要清洗到doris的不同表,按照需求进行保存,待清洗的三千六百万数据,需要进行去重,不得与正在使用的数据重复,然后也要清洗到doris。新老数据整合完成后,再一并发送到ES。
PS: 这里将 一千一百万的业务正在使用数据简称为 老数据
将 三千六百万的远古数据称为 新数据
由上面分析得知:
新老数据是不同的结构,业务数据是已经进过清洗的,而新数据还是原始数据的状态。
到这里,大家应该已经知道了整个查询的框架即:ES + Doris
由ES提供搜索的功能(批量查),由Doris返回店铺的详情(点查)
Doris 到还好,使用起来和Mysql类似,只需要注意将 新老数据的字段 拆分成入库的表字段即可,因为新老数据的格式不同,所以需要写两份清洗数据代码。
问题就在于 ES,现在要考虑的就是怎么将两份数据合并,且不重复,然后发送到ES用于功能实现的查询。
下面俺们进入踩坑实例:
三千多万的数据,说多不多,说少不少,原始数据中包含了超过70个字段,俺们不可能将所有的字段全部上传到es。
es的搜索引擎严重依赖于底层的filesystem cache,尽量让内存可以容纳所有的indx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。
如果我们业务需要进行检索的字段只有几个,而es上传的数据有好几十个,那么就会导致大量的字段是不需要被检索的,结果硬是占据了es机器上的filesystem cache的空间,单条数据的数据量越大,就会导致filesystem cahce能缓存的数据就越少。
所以俺们在上传数据之前就需要设计好,哪些字段是必须检索的,从而判断哪些字段进行上传,避免性能下降。
确认好了需要上传的字段后,我就开始了清洗数据和上传数据,等到测试查询的时候,发现 通过 经纬度和范围进行查询的时候,无论怎么查询,都无法成功,并且回进行报错:
failed to find geo field [location]
通过查阅资料得知:如果需要使用 经纬度进行es检索,则必须要在数据插入之前,设置字段的类型,如:
存放 经纬度的字段为 location ,那么就必须将该字段设置为 “type”: “geo_point”
同时 还有一个需要注意的地方,也是我踩的坑,如果在设置字段类型的时候,手欠加了一个逗号,如下:
那么,index 依旧会创建成功,但是 location 字段的 type 不会设置成功。
这里也提一句,关于关键字分词的坑,
"name": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
这段代码的意思对 name 这个字段进行属性设置,同时设置 检索属性:
ik_max_word:会对文本做最细 力度的拆分
ik_smart:会对文本做最粗粒度的拆分
但是如果 俺们将name 的属性 设置为 keyword,则index 生产时会进行报错。
正确的做法是将 nam 的属性 设置为 text,如下
原因是因为:
text:可分词,不参与聚合
keyword:不可分词,数据会作为完整字段进行匹配,可参与聚合
因为也是刚接触es不久,所以对于查询的使用还不是很熟练,中间遇到了几次查询出错的bug,相信大家多看几次和多尝试几次也能基本解决。
我遇到的问题如下:
根据python的书写习惯,我们创建字典的时候会使用 key[value] 的格式,我也是这么做的,于是写下了下面的一段部分查询代码:
_range["range"]["contactCount"]["eq"] = 0
_query["query"]["bool"]["must"] = must
这样写的代码,在进行es 查询的时候,就会到 KeyError 错误,因为俺们使用了 “query”,“range” 这两个关键字。
正确的写法如下:
_range = {
"range": {
"contactCount": {
"eq": 0
}
}
}
_query = {
"from": page,
"size": 20,
"query": {
"bool": {
"must": must
},
}
}
这样写的话,再查询就不会报错。
第二个坑是对于 bool 的使用:
bool 一共有 四个可选方法:
must
返回的文档必须满足must子句的条件,并且参与计算分值
filter
返回的文档必须满足filter子句的条件。但是不会像Must一样,参与计算分值
should
返回的文档可能满足should子句的条件。在一个Bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回。minimum_should_match参数定义了至少满足几个子句。
must_nout
返回的文档必须不满足must_not定义的条件。
俺这边使用的是 must ,因为是搜索查询,所以必须要满足 所选的所有条件。
所以在进行了很多次的测试之后,我们只需要将所有的查询条件 全部放入 must 即可完成满足条件的查询,无论有多少的查询条件,只要是全部都得满足就全部放入 must 列表。
以上,就是本期的内容,仅供大家参考。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。