ElasticSearch入门实战
ElasticSearch简介
ElasticSearch(简称ES)
Elasticsearch是用Java开发并且是当前最流行的开源的企业级搜索引擎。
能够达到实时搜索,稳定,可靠,快速,安装使用方便。
客户端支持Java、.NET(C#)、PHP、Python、Ruby等多种语言。
官方网站: https://www.elastic.co/
下载地址:https://www.elastic.co/cn/start
ElasticSearch与Lucene的关系
Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库(框架)
但是想要使用Lucene,必须使用Java来作为开发语言并将其直接集成到你的应用中,并且Lucene的配置及使用非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
Lucene缺点:
1)只能在Java项目中使用,并且要以jar包的方式直接集成项目中.
2)使用非常复杂-创建索引和搜索索引代码繁杂
3)不支持集群环境-索引数据不同步(不支持大型项目)
4)索引数据如果太多就不行,索引库和应用所在同一个服务器,共同占用硬盘.共用空间少.
上述Lucene框架中的缺点,ES全部都能解决.
Lucene全文检索框架
什么是全文检索
全文检索是指:
- 通过一个程序扫描文本中的每一个单词,针对单词建立索引,并保存该单词在文本中的位置、以及出现的次数
- 用户查询时,通过之前建立好的索引来查询,将索引中单词对应的文本位置、出现的次数返回给用户,因为有了具体文本的位置,所以就可以将具体内容读取出来了
分词原理之倒排索引
1 | hello what world ====> hello |
Elasticsearch中的核心概念
索引 index
一个索引就是一个拥有几分相似特征的文档的集合。比如说,可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引
一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字
映射 mapping
ElasticSearch中的映射(Mapping)用来定义一个文档
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分词器、是否被索引等等,这些都是映射里面可以设置的
字段Field
相当于是数据表的字段|列
字段类型 Type
每一个字段都应该有一个对应的类型,例如:Text、Keyword、Byte等
文档 document
一个文档是一个可被索引的基础信息单元,类似一条记录。文档以JSON(Javascript Object Notation)格式来表示;
集群 cluster
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能
节点 node
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中
这意味着,如果在网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中
在一个集群里,可以拥有任意多个节点。而且,如果当前网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
分片和副本 shards&replicas
分片
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢
为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片
当创建一个索引的时候,可以指定你想要的分片的数量
每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上
分片很重要,主要有两方面的原因
允许水平分割/扩展你的内容容量
允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
- 至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户来说,这些都是透明的
副本
- 在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做副本分片,或者直接叫副本
- 副本之所以重要,有两个主要原因
1) 在分片/节点失败的情况下,提供了高可用性。
注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的
2) 扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行
每个索引可以被分成多个分片。一个索引有0个或者多个副本
一旦设置了副本,每个索引就有了主分片和副本分片,分片和副本的数量可以在索引
创建的时候指定
在索引创建之后,可以在任何时候动态地改变副本的数量,但是不能改变分片的数量
分词器
1 | POST _analyze |
1 | POST _analyze |
1 | POST _analyze |
指定IK分词器作为默认分词器
ES的默认分词设置是standard,这个在中文分词时就比较尴尬了,会单字拆分,比如我搜索关键词“清华大学”,这时候会按“清”,“华”,“大”,“学”去分词,然后搜出来的都是些“清清的河水”,“中华儿女”,“地大物博”,“学而不思则罔”之类的莫名其妙的结果,这里我们就想把这个分词方式修改一下,于是呢,就想到了ik分词器,有两种ik_smart和ik_max_word。
ik_smart会将“清华大学”整个分为一个词,而ik_max_word会将“清华大学”分为“清华大学”,“清华”和“大学”,按需选其中之一就可以了。
修改默认分词方法(这里修改school_index索引的默认分词为:ik_max_word):
1 | PUT /school_index # school_index:索引 |
ES数据管理
ES数据管理概述
ES是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。
然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。
在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
ES使用JSON作为文档序列化格式。
JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。
ES存储的一个员工文档的格式示例:
1 | { |
基本操作
- 创建索引
格式: PUT /索引名称
1 | 举例: PUT /es_db |
- 查询索引
格式: GET /索引名称
1 | 举例: GET /es_db |
- 删除索引
格式: DELETE /索引名称
1 | 举例: DELETE /es_db |
- 添加文档
格式: PUT /索引名称/类型/id
1 | 举例: |
- 修改文档
格式: PUT /索引名称/类型/id
1 | PUT /es_db/_doc/1 |
注意:POST和PUT都能起到创建/更新的作用
1、需要注意的是==PUT==需要对一个具体的资源进行操作也就是要确定id才能进行==更新/创建,而==POST==是可以针对整个资源集合进行操作的,如果不写id就由ES生成一个唯一id进行==创建==新文档,如果填了id那就针对这个id的文档进行创建/更新
2、PUT只会将json数据都进行替换, POST只会更新相同字段的值
3、PUT与DELETE都是幂等性操作, 即不论操作多少次, 结果都一样
- 查询文档
格式: GET /索引名称/类型/id
1 | 举例: GET /es_db/_doc/1 |
- 删除文档
格式: DELETE /索引名称/类型/id
1 | 举例: DELETE /es_db/_doc/1 |
查询操作
查询当前类型中的所有文档 _search
1 | 格式: GET /索引名称/类型/_search |
条件查询, 如要查询age等于28岁的 _search?q=:**
1 | 格式: GET /索引名称/类型/_search?q=*:*** |
范围查询, 如要查询age在25至26岁之间的 _search?q=*[ TO **] 注意: TO 必须为大写
1 | 格式: GET /索引名称/类型/_search?q=***[25 TO 26] |
根据多个ID进行批量查询 _mget
1 | 格式: GET /索引名称/类型/_mget |
查询年龄小于等于28岁的 :<=
1 | 格式: GET /索引名称/类型/_search?q=age:<=** |
查询年龄大于28前的 :>
1 | 格式: GET /索引名称/类型/_search?q=age:>** |
分页查询 from=&size=
1 | 格式: GET /索引名称/类型/_search?q=age[25 TO 26]&from=0&size=1 |
对查询结果只输出某些字段 _source=字段,字段
1 | 格式: GET /索引名称/类型/_search?_source=字段,字段 |
对查询结果排序 sort=字段:desc/asc
1 | 格式: GET /索引名称/类型/_search?sort=字段 desc |
文档批量操作
这里多个文档是指,批量操作多个文档,搜索查询文档将在之后的章节讲解
批量获取文档数据
批量获取文档数据是通过_mget的API来实现的
(1)在URL中不指定index和type
请求方式:GET
请求地址:_mget
功能说明 : 可以通过ID批量获取不同index和type的数据
请求参数:
- docs : 文档数组参数
- _index : 指定index
- _type : 指定type
- _id : 指定id
- _source : 指定要查询的字段
1 | GET _mget |
(2)在URL中指定index
请求方式:GET
请求地址://_mget
功能说明 : 可以通过ID批量获取不同index和type的数据
请求参数:
- docs : 文档数组参数
- _index : 指定index
- _type : 指定type
- _id : 指定id
- _source : 指定要查询的字段
1 | GET /es_db/_mget |
(3)在URL中指定index和type
请求方式:GET
请求地址:///_mget
功能说明 : 可以通过ID批量获取不同index和type的数据
请求参数:
- docs : 文档数组参数
- _index : 指定index
- _type : 指定type
- _id : 指定id
- _source : 指定要查询的字段
1 | GET /es_db/_doc/_mget |
批量操作文档数据
批量对文档进行写操作是通过_bulk的API来实现的
请求方式:POST
请求地址:_bulk
请求参数:通过_bulk操作文档,一般至少有两行参数(或偶数行参数)
- 第一行参数为指定操作的类型及操作的对象(index,type和id)
- 第二行参数才是操作的数据
参数类似于:
1 | {"actionName":{"_index":"indexName", "_type":"typeName","_id":"id"}} |
- actionName:表示操作类型,主要有create,index,delete和update
(1)批量创建文档create
1 | POST _bulk |
(2)普通创建或全量替换index
1 | POST _bulk |
- 如果原文档不存在,则是创建
- 如果原文档存在,则是替换(全量修改原文档)
(3)批量删除delete
1 | POST _bulk |
(4)批量修改update
1 | POST _bulk |
DSL语言高级查询
Query DSL概述
Domain Specific Language
领域专用语言
Elasticsearch provides a ful1 Query DSL based on JSON to define queries
Elasticsearch提供了基于JSON的DSL来定义查询。
DSL由叶子查询子句和复合查询子句两种子句组成。
无查询条件
无查询条件是查询所有,默认是查询所有的,或者使用match_all表示所有
1 | GET /es_db/_doc/_search |
有查询条件
叶子条件查询(单字段查询条件)
模糊匹配
模糊匹配主要是针对文本类型的字段,文本类型的字段会对内容进行分词,对查询时,也会对搜索条件进行分词,然后通过倒排索引查找到匹配的数据,模糊匹配主要通过match等参数来实现
- match : 通过match关键词模糊匹配条件内容
- prefix : 前缀匹配
- regexp : 通过正则表达式来匹配数据
match的复杂用法
match条件还支持以下参数:
query : 指定匹配的值
operator : 匹配条件类型
- and : 条件分词后都要匹配
- or : 条件分词后有一个匹配即可(默认)
minmum_should_match : 指定最小匹配的数量
精确匹配
- term : 单个条件相等
- terms : 单个字段属于某个值数组内的值
- range : 字段属于某个范围内的值
- exists : 某个字段的值是否存在
- ids : 通过ID批量查询
组合条件查询(多条件查询)
组合条件查询是将叶子条件查询语句进行组合而形成的一个完整的查询条件
bool : 各条件之间有and,or或not的关系
- must : 各个条件都必须满足,即各条件是and的关系
- should : 各个条件有一个满足即可,即各条件是or的关系
- must_not : 不满足所有条件,即各条件是not的关系
- filter : 不计算相关度评分,它不计算_score即相关度评分,效率更高
constant_score : 不计算相关度评分
must/filter/shoud/must_not 等的子条件是通过 term/terms/range/ids/exists/match 等叶子条件为参数的
注:以上参数,当只有一个搜索条件时,must等对应的是一个对象,当是多个条件时,对应的是一个数组
连接查询(多文档合并查询)
- 父子文档查询:parent/child
- 嵌套文档查询: nested
查询DSL(query DSL)和过滤DSL(filter DSL)
它们两个的区别如下图:
query DSL
在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高么?”
如何验证匹配很好理解,如何计算相关度呢?ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
filter DSL
在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。过滤上下文 是在使用filter参数时候的执行环境,比如在bool查询中使用must_not或者filter另外,经常使用过滤器,ES会自动的缓存过滤器的内容,这对于查询来说,会提高很多性能。一些过滤的情况:
Query方式查询:案例
根据名称精确查询姓名 term, term查询不会对字段进行分词查询,会采用精确匹配
注意: 采用term精确查询, 查询字段映射类型属于为keyword.
1 | POST /es_db/_doc/_search |
1 | SQL: select * from student where name = 'admin' |
- 根据备注信息模糊查询 match, match会根据该字段的分词器,进行分词查询
1 | POST /es_db/_doc/_search |
1 | SQL: select * from user where address like '%广州%' limit 0, 2 |
- 多字段模糊匹配查询与精准查询 multi_match
1 | POST /es_db/_doc/_search |
1 | SQL: select * from student where name like '%张三%' or address like '%张三%' |
- 未指定字段条件查询 query_string , 含 AND 与 OR 条件
1 | POST /es_db/_doc/_search |
- 指定字段条件查询 query_string , 含 AND 与 OR 条件
1 | POST /es_db/_doc/_search |
- 范围查询
注:json请求字符串中部分字段的含义
range:范围关键字
gte 大于等于
lte 小于等于
gt 大于
lt 小于
now 当前时间
1 | POST /es_db/_doc/_search |
1 | SQL: select * from user where age between 25 and 28 |
- 分页、输出字段、排序综合查询
1 | POST /es_db/_doc/_search |
Filter过滤器方式查询
Filter过滤器方式查询,它的查询不会计算相关性分值,也不会对结果进行排序, 因此效率会高一点,查询的结果可以被缓存。
Filter Context 对数据进行过滤
1 | POST /es_db/_doc/_search |
总结
1. match
match:模糊匹配,需要指定字段名,但是输入会进行分词,比如”hello world”会进行拆分为hello和world,然后匹配,如果字段中包含hello或者world,或者都包含的结果都会被查询出来,也就是说match是一个部分匹配的模糊查询。查询条件相对来说比较宽松。
2. term
term: 这种查询和match在有些时候是等价的,比如我们查询单个的词hello,那么会和match查询结果一样,但是如果查询”hello world”,结果就相差很大,因为这个输入不会进行分词,就是说查询的时候,是查询字段分词结果中是否有”hello world”的字样,而不是查询字段中包含”hello world”的字样。当保存数据”hello world”时,elasticsearch会对字段内容进行分词,”hello world”会被分成hello和world,不存在”hello world”,因此这里的查询结果会为空。这也是term查询和match的区别。
3. match_phase
match_phase:会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样。以”hello world”为例,要求结果中必须包含hello和world,而且还要求他们是连着的,顺序也是固定的,hello that world不满足,world hello也不满足条件。
4. query_string
query_string:和match类似,但是match需要指定字段名,query_string是在所有字段中搜索,范围更广泛。
文档映射
1.ES中映射可以分为动态映射和静态映射
动态映射:
在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。
动态映射规则如下:
静态映射:
静态映射是在Elasticsearch中也可以事先定义好映射,包含文档的各字段类型、分词器等,这种方式称之为静态映射。
动态映射
删除原创建的索引
1 | DELETE /es_db |
创建索引
1 | PUT /es_db |
创建文档(ES根据数据类型, 会自动创建映射)
1 | PUT /es_db/_doc/1 |
获取文档映射
1 | GET /es_db/_mapping |
静态映射
删除原创建的索引
1 | DELETE /es_db |
创建索引
1 | PUT /es_db |
设置文档映射
1 | PUT /es_db |
根据静态映射创建文档
1 | PUT /es_db/_doc/1 |
获取文档映射
1 | GET /es_db/_mapping |
核心类型(Core datatype)
字符串:string,string类型包含 text 和 keyword。
text:该类型被用来索引长文本,在创建索引前会将这些文本进行分词,转化为词的组合,建立索引;允许es来检索这些词,text类型不能用来排序和聚合。
keyword:该类型不能分词,可以被用来检索过滤、排序和聚合,keyword类型不可用text进行分词模糊检索。
数值型:long、integer、short、byte、double、float
日期型:date
布尔型:boolean
keyword 与 text 映射类型的区别
将 book 字段设置为 keyword 映射 (只能精准查询, 不能分词查询,能聚合、排序)
1 | POST /es_db/_doc/_search |
将 book 字段设置为 text 映射能模糊查询, 能分词查询,不能聚合、排序)
1 | POST /es_db/_doc/_search |
创建静态映射时指定text类型的ik分词器
1.设置ik分词器的文档映射
先删除之前的es_db
再创建新的es_db
定义ik_smart的映射
1 | PUT /es_db |
对已存在的mapping映射进行修改
具体方法
1)如果要推倒现有的映射, 你得重新建立一个静态索引
2)然后把之前索引里的数据导入到新的索引里
3)删除原创建的索引
4)为新索引起个别名, 为原索引名
1 | POST _reindex |
注意: 通过这几个步骤就实现了索引的平滑过渡,并且是零停机
Elasticsearch乐观并发控制
ES新版本(7.x)不使用version进行并发版本控制 if_seq_no=版本值&if_primary_term=文档位置
_seq_no:文档版本号,作用同_version
_primary_term:文档所在位置
1 | POST /es_sc/_search |