分布式搜索引擎Elasticsearch
- elasticsearch:一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能
1. 初识elasticsearch
1.1 认识和安装






1.2.倒排索引
elasticsearch之所以有如此高性能的搜索表现,得益于底层的倒排索引技术。什么是倒排索引?
倒排索引的概念是基于MySQL这样的正向索引而言的。
1.2.1.正向索引
我们先来回顾一下正向索引。例如有一张名为tb_goods的表:
其中的id字段已经创建了索引,由于索引底层采用了B+树结构,因此我们根据id搜索的速度会非常快。但是其他字段例如title,只在叶子节点上存在。
因此要根据title搜索的时候只能遍历树中的每一个叶子节点,判断title数据是否符合要求。
比如用户的SQL语句为:select * from tb_goods where title like '%手机%';

综上,根据id精确匹配时,可以走索引,查询效率较高。而当搜索条件为模糊匹配时,由于索引无法生效,导致从索引查询退化为全表扫描,效率很差。
因此,正向索引适合于根据索引字段的精确搜索,不适合基于部分词条的模糊匹配。
而倒排索引恰好解决的就是根据部分词条模糊匹配的问题。
1.2.2.倒排索引
倒排索引中有两个非常重要的概念:
- 文档(
Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息 - 词条(
Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条
创建倒排索引是对正向索引的一种特殊处理和应用,流程如下:
将每一个文档的数据利用分词算法根据语义拆分,得到一个个词条
创建表,每行数据包括词条、词条所在文档id、位置等信息
因为词条唯一性,可以给词条创建正向索引
此时形成的这张以词条为索引的表,就是倒排索引表,两者对比如下:


虽然要先查询倒排索引,再查询正向索引,但是无论是词条、还是文档id都建立了索引,查询速度非常快!无需全表扫描。
1.2.3.正向和倒排
那么为什么一个叫做正向索引,一个叫做倒排索引呢?
- 正向索引是最传统的,根据id索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程。
- 而倒排索引则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的id,然后根据id获取文档。是根据词条找文档的过程。
- 正向索引通常用于精确查询,支持多字段索引和排序,但对于非索引字段或部分匹配查询的效率较低。
- 倒排索引则擅长快速全文搜索和模糊查询,但只能处理基于词条的检索,不支持直接排序和字段级别的查询。
1.3.基础概念
elasticsearch中有很多独有的概念,与mysql中略有差别,但也有相似之处。
1.3.1.文档和字段
elasticsearch是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中:

因此,原本数据库中的一行数据就是ES中的一个JSON文档;而数据库中每行数据都包含很多列,这些列就转换为JSON文档中的字段(Field)。
1.3.2.索引和映射
随着业务发展,需要在es中存储的文档也会越来越多,比如有商品的文档、用户的文档、订单文档等等:

所有文档都散乱存放显然非常混乱,也不方便管理。因此,我们要将类型相同的文档集中在一起管理,称为索引(Index)。例如:

所有用户文档,就可以组织在一起,称为用户的索引;
所有商品的文档,可以组织在一起,称为商品的索引;
所有订单的文档,可以组织在一起,称为订单的索引;
因此,我们可以把索引当做是数据库中的表。
数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。
1.3.3.mysql与elasticsearch
我们统一的把mysql与elasticsearch的概念做一下对比:
| MySQL | Elasticsearch | 说明 |
|---|---|---|
| Table | Index | 索引(index),就是文档的集合,类似数据库的表(table) |
| Row | Document | 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |
| Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
| Schema | Mapping | Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |
| SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |
那是不是说,我们学习了elasticsearch就不再需要mysql了呢?并不是如此,两者各自有自己的擅长之处:
- Mysql:擅长事务类型操作,可以确保数据的安全和一致性
- Elasticsearch:擅长海量数据的搜索、分析、计算
因此在企业中,往往是两者结合使用:
- 对安全性要求较高的写操作,使用mysql实现
- 对查询性能要求较高的搜索需求,使用elasticsearch实现
- 两者再基于某种方式,实现数据的同步,保证一致性

1.4.IK分词器




2.索引库操作
Index就类似数据库表,Mapping映射就类似表的结构。我们要向es中存储数据,必须先创建Index和Mapping
2.1.Mapping映射属性


2.2.索引库的CRUD
由于Elasticsearch采用的是Restful风格的API,因此其请求方式和路径相对都比较规范,而且请求参数也都采用JSON风格。
我们直接基于Kibana的DevTools来编写请求做测试,由于有语法提示,会非常方便。
2.2.1.创建索引库和映射


2.2.2.查询索引库

2.2.3.修改索引库
倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改mapping。
虽然无法修改mapping中已有的字段,但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。因此修改索引库能做的就是向索引库中添加新字段,或者更新索引库的基础属性。

2.2.4.删除索引库

2.2.5.总结
索引库操作有哪些?
- 创建索引库:PUT /索引库名
- 查询索引库:GET /索引库名
- 删除索引库:DELETE /索引库名
- 修改索引库,添加字段:PUT /索引库名/_mapping
可以看到,对索引库的操作基本遵循的Restful的风格,因此API接口非常统一,方便记忆
3.文档操作
有了索引库,接下来就可以向索引库中添加数据了。
Elasticsearch中的数据其实就是JSON风格的文档。操作文档自然包括增、删、改、查等几种常见操作,我们分别来学习。
3.1.新增文档

3.2.查询文档
根据rest风格,新增是post,查询应该是get,不过查询一般都需要条件,这里我们把文档id带上。

3.3.删除文档
删除使用DELETE请求,同样,需要根据id进行删除:

3.4.修改文档
修改有两种方式:
- 全量修改:直接覆盖原来的文档
- 局部修改:修改文档中的部分字段
3.4.1.全量修改
全量修改是覆盖原来的文档,其本质是两步操作:
- 根据指定的id删除文档
- 新增一个相同id的文档
注意:如果根据id删除时,id不存在,第二步的新增也会执行,也就从修改变成了新增操作了。


3.4.2.局部修改
局部修改是只修改指定id匹配的文档中的部分字段。

3.5.批处理
批处理采用POST请求,基本语法如下:

create:仅在 ID 不存在时创建文档到索引 test,ID 为 3,正确
3.6.总结
文档操作有哪些?
4.RestClient操作索引库
ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。



参与搜索的可以先copy到all,先copy两个。
在定义一个mapping的时候,考虑字段的名字,字段的数据类型。
- 是否分词,分词就是text,部分词就是keyword,分词器是什么。
- 如果下那个多个字段一起搜索可以用copyto。
- 是否参与搜索,不参与index:false。
- 地理坐标的类型geopoint,把经纬度匹配在一起。
4.0 初始化RestHighLevelClient
由于ES目前最新版本是8.8,提供了全新版本的客户端,老版本的客户端已经被标记为过时。而我们采用的是7.12版本,因此只能使用老版本客户端:


4.1 创建索引库



索引库创建没问题 注意不要导错包
1 | |

4.2 删除索引库
1 | |

4.3 查询索引库信息
1 | |
4.4 判断索引库是否存在
1 | |

4.5 总结
JavaRestClient操作elasticsearch的流程基本类似。核心是
client.indices()方法来获取索引库的操作对象。索引库操作的基本步骤:
- 初始化
RestHighLevelClient- 创建XxxIndexRequest。XXX是
Create、Get、Delete- 准备请求参数(
Create时需要,其它是无参,可以省略)- 发送请求。调用
RestHighLevelClient的indices().xxx()方法,xxx是create、exists、delete
5.RestClient操作文档
索引库准备好以后,就可以操作文档了。为了与索引库操作分离,我们再次创建一个测试类,做两件事情:
初始化RestHighLevelClient
我们的商品数据在数据库,需要利用IHotelService去查询,所以注入这个接口
新增酒店数据到hotel索引库 从数据库里查询酒店数据,把酒店数据转换成索引库所需要的格式,然后把酒店数据写到索引库上

5.1 初始化JavaRestClien(即RestHighLevelClient)
1 | |
5.2 添加酒店数据到索引库
1 | |



5.3 根据id查询酒店数据
1 | |



5.4 根据id修改酒店数据
1 | |


5.5 根据id删除文档数据
1 | |
新查询


5.6 批量导入文档
在之前的案例中,我们都是操作单个文档。而数据库中的商品数据实际会达到数十万条,某些项目中可能达到数百万条。
我们如果要将这些数据导入索引库,肯定不能逐条导入,而是采用批处理方案。
1 | |


5.7 总结
文档操作的基本步骤:
- 初始化RestHighLevelClient
- 创建XxxRequest。XXX是Index、Get、Update、Delete
- 准备参数(Index和Update时需要)
- 发送请求。调用RestHighLevelClient的.xxx()方法,
xxx是index、get、update、delete、bulk- 解析结果(Get时需要)
