搜索中台的三次演变——自研的第二代

背景

上一代使用的阿里云的智能开放搜索OpenSearch,成本高,不够灵活、简单。
对比阿里云opensearch具体优势有:
●opensearch按照索引、索引数据量及TPS、QPS收费,费用较高,且超过QPS后会搜索异常,不能有效应对双十一这样的查询高峰
●提供界面操作,业务方自己可以灵活修改索引结构
●实时数据同步:无需业务方调接口同步数据

目标:降低用户使用搜索的门槛,让搜索人人可用,人人会用。

介绍

使用方法和opensearch类似,由于是内部系统,就不一一说明步骤了,重点介绍下数据链路

索引设计

索引通过可视化的方式设置各个字段的mapping,不支持自定义field mapping。可设置的选项包括是否索引,选择对应的分词器,是否全文检索等。
在这里插入图片描述

索引的实现原理

索引的实现不是直接基于es实现的,基于上面提到的三个设置分别说明其实现原理

  1. 是否索引:和es一致,映射到es的索引上就是对应的field mapping,index=true
  2. 选择分词器:这里选择了分词器后,并不会在es的索引对应的field mapping设置分词器,而是在数据写入时分词
  3. 是否全文检索:选择了之后,会加入到全文检索字段中

2和3的实现,请继续阅读下面的 数据写入

数据写入

写入方式

提功了两种方式的写入:

  • 使用api写入:提供api接口,可以通过api将数据写入
  • 基于canal二次开发,实时数据同步

写入时分词

分词没有使用es的分词功能,而是在负责数据写入的应用中完成的。

负责数据写入的应用中,引用或拷贝了开源的分词器,有标准分词器、ik分词器、拼音分词器等等。分词的操作直接在该应用中进行,分出的词会写入es索引中

举例:配置了分词和索引

举例某个索引的字段name配置了 分词器为标准分词
name=lazy dog 分词的结果为[ lazy, dog ],将分词结果拼接为lazy\tdog,写入到es的索引字段analyze_name_own

analyze_name_own这个字段的index=true,分词器是按照\t分词的自定义分词器,索引结构如下

{
    "mappings": {
        "_doc": {
            "name": {
                "type": "keyword"
            },
            "analyze_name_own": {
                "type": "text",
                "analyzer": "按\t分词的分词器"
            }
        }
    }
}

es中该文档内容如下

{
	...
	"name": "lazy dog",
	"analyze_name_own": "lazy\tdog"
	...
}
举例:配置了分词和索引,选择了全文检索

选择了全文检索后,会将所有选择了全文检索的字段分出的词,拷贝到字段analyze_all中,
name和nickname都选择了全文检索
name=lazy dog 分词的结果为[ lazy, dog ]
nickname=Brown-Foxes 分词的结果为[ brown, foxes ]
将name和nickname的分词结果使用\t拼接,得到lazy\tdog\tbrown\tfoxes,写入到es索引的analyze_all字段中
analyze_all这个字段的index=true,分词器是按照\t分词的自定义分词器,索引结构如下

{
    "mappings": {
        "_doc": {
            "name": {
                "type": "keyword"
            },
            "nickname": {
                "type": "keyword"
            },
            "analyze_all": {
                "type": "text",
                "analyzer": "按\t分词的分词器"
            }
        }
    }
}

es中该文档内容如下

{
	...
	"name": "lazy dog",
	"nickname": "Brown-Foxes",
	"analyze_all": "lazy\tdog\tbrown\tfoxes"
	...
}

数据变更处理的流程

收到数据变更的消息后,会根据不同的索引数据同步配置,将数据写入到es。部分场景数据同步性能低下,经常出现堆积的情况。
在这里插入图片描述

慢查询原因主要如下:

  • 收到更新消息时,部分文档需要计算analyze_all的值,会先从es中查询该文档,然后再更新
  • 多表情况,部分场景更新使用update_by_queryupdate_by_scriptdelete_by_script操作的
  • 顺序处理数据变更消息,消息过多时,无法弹性
  • 其他一些设计…

数据查询

封装的查询语法

es集群并不直接对外使用,不能直接使用es的query-dsl语法查询,封装了一套查询语法,个别用例如下:

  • analyze(name)=\"hello world\" AND analyze(name,\"OR\")=\"elastic kafka\"
  • field6>=\"2019-01-01 00:00:00\"
  • field1 IN [3,6,....] AND field2 PREFIX \"abc\" AND field3 NOT IN [\"hello\", \"world\"]
  • keyword=\"boy\"

负责查询的应用,会将自定义语法的dsl翻译为es的dsl,然后将请求发送到es集群,返回查询结果。
将自定义语法的dsl翻译为es的dsl是使用antlr4实现的,demo可以参考es-sql-demo

分词的处理

数据写入时,字段的分词是做了处理的,因此查询时也要处理。

某个字段的全文检索

对某个字段的全文检索,转换为es query dsl时,需要使用该字段对应的analyze_xx_own替换,
例如analyze(name)=\"hello world\" 转换之后为

{
    "match": {
        "analyze_name_own": "hello\tworld"
    }
}

输入的hello world,会先分词得到hello\tworld

全文检索

用法keyword=\"boy\",转换为es query dsl时如下

{
    "match": {
        "analyze_all": "boy"
    }
}

优缺点

相较于原生的es,使用确实简单,数据写入和索引结构通过可视化的控制台都可以配置。不需要理解底层的实现原理,只需要知道哪些字段是需要分词的,需要查询的就可以,基础的功能也都提供了。

缺点主要有3点吧

  1. 数据同步时的分词:
  • 分词器不是可插拔的插件,而是编码的,也不支持对分词器的参数配置,导致个别业务场景,哪怕是修改个分词器的参数都需要单独开发分词器
  • 分词器不可调试,没有提供调试分词器的入口
  • 性能低问题,经常出现消息堆积
  1. 查询层面:
  • 自定义的查询语法,仅覆盖率一小部分es的api,很多功能不可用
  • 慢查询无法优化
  1. 设计层面:
  • 索引结构不可自定义,很多es原生的功能无法使用

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lanicc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值