背景介绍
最近在做一个采购商城搜索的功能,核心功能是
根据用户录入的关键字在商品库中根据全称匹配进行检索得到结果
方案比较
目标
在 不超过 100万 的数据集合中,根据用户录入的 关键字 和 条件 检索到结果
方案
全部数据量不大,最直接的想法 SQL like 查询,示例如下
select * from mati where mati_full_name like '%电脑%'
然而,在实际实践中发现一些问题
用户输入的内容可能比较复杂,关键字需要根据符号可以进行分割组合,例如 ”华为 电脑 白色”,即使这样简单的一个组合,就需要全文检索数据库至少6次,性能下降的比较厉害
原理
倒排索引
索引或者正排索引,想必都不陌生,例如书本的目录,查找的顺序是
目录 ==> 页码 ==> 内容
试想一下,如果数据量比较大,例如在 《Elasticsearch实战与原理解析》这本书中找出包含 Elasticsearch 关键字的页面,这将是个非常巨大的工作量,需要一次次的翻找目录及页码才可以定位到内容,是否可以直接根据某个单词找到页码再找到内容
单词 ==> 页码 ==> 内容
索引结构
假如现有三份数据文档,
生成的倒排索引的结果大致如下
这种结构由文档中所有不重复词的列表构成,对于其中每个词都有一个文档ID与之关联。这种由词来确定记录的位置的结构就是倒排索引,带有倒排索引的文件我们称为倒排文件,词典和倒排列表是实现快速检索的重要基石。
查找词条
上面简单介绍了倒排索引的基本结构,在进行实际搜索前,还面临一个问题:如何从词典中高效的查询到词条,特别是词条比较多的时候?
稍微回忆下,当记录数很多的时候,Mysql 是如何快速找到需要的记录呢?答案是索引,如果查询条件使用索引,查询效率将会非常高,Mysql 使用 B+ 树来实现索引。ES的做法也是类似的,即创建 Term Index。
ElasticSearch 内部的 Term Index 是用的变种的trie树(前缀树/字典树/单词查找树),即FST(Finite State Transducer),trie树只共享了前缀,而 FST 既共享前缀也共享后缀,更加的节省空间。FST 在兼顾查询性能的情况下,占用空间更小: 1)查询速度快。O(len(str))的查询时间复杂度。2)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
例如:
核心流程
词-索引 不包含所有的 词,包含的是 词 的一些前缀,通过索引可以快速地定位到词典 的某个偏移,然后从这个偏移位置再往后顺序查找到指定词,在根据指定词定位到内容。
落地实践
资源申请
类型 |
计算公式 |
存储资源 |
存储容量 ≈ 源数据 × (1 + 副本数量) × 1.45 ×(1 + 预留空间) ≈ 源数据 × (1 + 副本数量) × 1.67 |
计算资源 |
峰值线程数 ≈ 每秒峰值检索请求数 * 每个请求的平均响应时间(毫秒)/1000 线程队列大小 ≈ (每个节点的物理 cpu 核数 * 每核的线程数 * 3 / 2)+ 1 总数据节点个数 ≈ 峰值线程数 / 线程队列大小 ×(1 + 预留空间) |
结构/类型/语法
以 ES 7.0 以上版本为例,介绍下常用的结构定义和类型,更多的内容可以参考官方文档
结构
MySQL |
ElasticSearch |
Table |
Index |
Row |
Document |
Column |
Field |
Schema |
Mapping |
类型
分类 |
类型 |
说明 |
简单类型 |
字符串类型 |
text: 分词检索 keyword:精确匹配 |
数值类型 |
byte, short, integer, long, float, double ... |
|
布尔值类型 |
可以接受表示真、假的字符串或数字: 真值: true, "true", "on", "yes", "1" 假值: false, "false", "off", "no", "0", ""(空字符串), 0.0, 0 |
|
时间类型 |
没有单独的时间类型,可以是 1. 包含格式化日期的字符串, "2018-10-01", 或"2018/10/01 12:10:30". 2. 代表时间毫秒数的长整型数字. 3. 代表时间秒数的整数. |
|
范围类型 |
用来进行范围搜索,主要为数字,时间,ip等值 |
|
复杂类型 |
数组类型 |
没有专门的数组类型, 直接使用[]定义即可 必须是同一种数据类型 |
对象类型 |
文档可以包含内部对象, 内部对象也可以包含内部对象 |
|
嵌套类型 |
主要用来解决对象类型的不足,如果需要对子对象进行索引, 且保留数组中对象的独立性,则需要使用嵌套类型 |
语法
类型 |
说明 |
插入 |
PUT /{index}/_doc/{id} { "field": "value", ... } |
获取 |
GET /{index}/_doc/{id} |
更新 |
POST /{index}/_update/{id} { "doc":{ // 在doc字段中指定需要更新的字段 // 需要更新的字段列表 } } |
删除 |
DELETE /{index}/_doc/{id} |
查询 |
GET /{index}/_search { "from" : 0, // 返回搜索结果的开始位置 "size" : 10, // 分页大小,一次返回多少数据 "_source" :[ ...需要返回的字段数组... ], "query" : { ...query子句... }, // 查询条件 "aggs" : { ..aggs子句.. }, // 聚合条件 "sort" : { ..sort子句.. } //排序条件 } |
数据接入
日常业务中,我们会使用 MYSQL 用做 OLTP,需要将 MYSQL 的数据同步到 ElasticSearch,一般来说,同步数据的方式有以下几种:
方案 |
优点 |
缺点 |
代码双写 |
1. 实现简单 2. 数据实时性较好 |
1. 容易造成代码冗杂和不必要的藕合,数据结构修改的开发和测试成本高 2. 事务一致性不容易保障,例如运营数据库,则需要同时同步更新 ElasticSearch 数据 |
定时任务同步 |
1. 与数据库业务解藕 2. 实现也比较简单 |
1. 数据实时性不足 2. 批量查询的SQL性能不佳 |
基于binlog事件 |
1. 与数据库业务解藕 2. 数据实时性较好 |
1. 实现复杂度比较高 |
标签:...,倒排,实践,查询,索引,ElasticSearch,类型,分享 From: https://www.cnblogs.com/SLchuck/p/17083337.html