1、简介
Elasticsearch是一个高度可扩展的、开源的、基于 Lucene 的全文搜索和分析引擎。它允许您快速,近实时地存储,搜索和分析大量数据,并支持多租户。
Elasticsearch也使用Java开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。
不过,Elasticsearch 不仅仅是 Lucene 和全文搜索,我们还能这样去描述它:
- 分布式的实时文件存储,每个字段都被索引并可被搜索
- 分布式的实时分析搜索引擎
- 可以扩展到上百台服务器,处理PB级结构化或非结构化数据
而且,所有的这些功能被集成到一个服务里面,你的应用可以通过简单的RESTful API、各种语言的客户端甚至命令行与之交互。
- Solr、ES区别
全文检索、搜索、分析。基于lucene
- Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
- Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
- Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供;
- Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch-----附近的人
、Elasticsearch的交互方式
1、基于HTTP协议,以JSON为数据交互格式的RESTful API
GET POST PUT DELETE HEAD
port:9200
2、Elasticsearch官方提供了多种程序语言的客户端—java,Javascript,.NET,PHP,Perl,Python,以及 Ruby——还有很多由社区提供的客户端和插件
port:9300
kibana 可视化工具
Elasticsearch数据存储方式
(1)面向文档
Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往完全不同,这也是Elasticsearch能够执行复杂的全文搜索的原因之一。
(2)每个文档是JSON
ELasticsearch使用Javascript对象符号(JavaScript Object Notation),也就是JSON,作为文档序列化格式。JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。它简洁、简单且容易阅读。
以下使用JSON文档来表示一个用户对象
Mysql数据与ES数据转化
- 元数据
创建文档语句
PUT atguigu/doc
{
“name”:”zhangsan”,
“age”:10
}
_index:文档所在索引名称
_type:文档所在类型名称
_id:文档唯一id
_uid:组合id,由_type和_id组成(6.x后,_type不再起作用,同_id)
_source:文档的原始Json数据,包括每个字段的内容
_all:将所有字段内容整合起来,默认禁用(用于对所有字段内容检索)
(2)名词解释
- 索引 index(库)
一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。
- 类型 type(表)
Es6之后,一个index中只能有一个type
在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。
- 字段Field
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
- document
一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。
#! Deprecation: [types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.
{
"_index" : "index",
"_type" : "yxl",
"_id" : "1",
"_version" : 2,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"id" : "1",
"name" : "zhangsan"
}
}
搜索:
1、ES : GET /atguigu/doc/1
2、ES : GET /megacorp/employee/_search
3、全文检索 ES : GET /megacorp/employee/_search?q=haha
4、模糊检索 ES : GET /megacorp/employee/_search?q=hello
5、聚合
GET /atguigu/doc/_search
{
"aggs": {
"all_interests": {
"terms": { "field": "interests" }
}
}
}
es原理
1、正排索引和倒排索引
- 内存结构
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
B+Tree
每个文档字段都有自己的倒排索引
分词器
分词是指将文本转换成一系列单词(term or token)的过程,也可以叫做文本分析,在es里面称为Analysis
分词器(Analyzer) | 特点 |
Standard(es默认) | 支持多语言,按词切分并做小写处理 |
Simple | 按照非字母切分,小写处理 |
Whitespace | 按照空格来切分 |
Stop | 去除语气助词,如the、an、的、这等 |
Keyword | 不分词 |
Pattern | 正则分词,默认\w+,即非字词符号做分割符 |
Language | 常见语言的分词器(30+) |
分词器名称 | 介绍 | 特点 | 地址 |
IK | 实现中英文单词切分 | 自定义词库 | https://github.com/medcl/elasticsearch-analysis-ik |
Jieba | python流行分词系统,支持分词和词性标注 | 支持繁体、自定义、并行分词 | http://github.com/sing1ee/elasticsearch-jieba-plugin |
Hanlp | 由一系列模型于算法组成的java工具包 | 普及自然语言处理在生产环境中的应用 | https://github.com/hankcs/HanLP |
THULAC | 清华大学中文词法分析工具包 | 具有中文分词和词性标注功能 | https://github.com/microbun/elasticsearch-thulac-plugin |
K分词器
- IK分词器的安装
下载与安装的ES相对应的版本
2)解压,将解压后的elasticsearch文件夹拷贝到elasticsearch-5.6.8\plugins下,并重命名文件夹为analysis-ik
3)重新启动ElasticSearch,即可加载IK分词器
IK提供了两个分词算法ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word为最细粒度划
Mapping
- 作用:
定义数据库中的表的结构的定义,通过mapping来控制索引存储数据的设置
- 定义Index下的字段名(Field Name)
- 定义字段的类型,比如数值型、字符串型、布尔型等
- 定义倒排索引相关的配置,比如documentId、记录position、打分等
- 获取索引mapping
不进行配置时,自动创建的mapping
请求:
GET /atguigu/_mapping
数据类型
- 核心数据类型
字符串型:text、keyword
数值型:long、integer、short、byte、double、float、half_float、scaled_float
日期类型:date
布尔类型:boolean
二进制类型:binary
范围类型:integer_range、float_range、long_range、double_range、date_range
- 复杂数据类型
数组类型:array
对象类型:object
嵌套类型:nested object
- 地理位置数据类型
geo_point(点)、geo_shape(形状)
批量创建文档
POST test_search_index/doc/_bulk
{
"index":{
"_id":1
}
}
{
"username":"alfred way",
"job":"java engineer",
"age":18,
"birth":"1991-12-15",
"isMarried":false
}
{
"index":{
"_id":2
}
}
{
"username":"alfred",
"job":"java senior engineer and java specialist",
"age":28,
"birth":"1980-05-07",
"isMarried":true
}
Search API(URI)
GET /_search #查询所有索引文档
GET /my_index/_search #查询指定索引文档
GET /my_index1,my_index2/_search #多索引查询
GET /my_index/_search?q=keyword&df=user&sort=age:asc&from=4&size=10&timeout=1s
q : 指定查询的语句,例如q=aa或q=user:aa
df:q中不指定字段默认查询的字段,如果不指定,es会查询所有字段
Sort:排序,asc升序,desc降序
timeout:指定超时时间,默认不超时
from,size:用于分页
term:Alfred way等效于alfred or way
GET test_search_index/_search?q=username:alfred way #alfred OR way
phrase:”Alfred way” 词语查询,要求先后顺序
GET test_search_index/_search?q=username:"alfred way"
Group分组设定(),使用括号指定匹配的规则
(quick OR brown)AND fox:通过括号指定匹配的优先级
status:(active OR pending) title:(full text search):把关键词当成一个整体
GET test_search_index/_search?q=username:(alfred OR way)
布尔操作符
(1)AND(&&),OR(||),NOT(!)
例如:name:(tom NOT lee)
#表示name字段中可以包含tom但一定不包含lee
- +、-分别对应must和must_not
例如:name:(tom +lee -alfred)
#表示name字段中,一定包含lee,一定不包含alfred,可以包含tom
- 范围查询,支持数值和日期
- 区间:闭区间:[],开区间:{}
age:[1 TO 10] #1<=age<=10
age:[1 TO 10} #1<=age<10
age:[1 TO ] #1<=age
age:[* TO 10] #age<=10
- 算术符号写法
age:>=1
age:(>=1&&<=10)或者age:(+>=1 +<=10)
- 通配符查询
?:1个字符
*:0或多个字符
例如:name:t?m
name:tom*
name:t*m
注意:通配符匹配执行效率低,且占用较多内存,不建议使用,如无特殊要求,不要讲?/*放在最前面
- 模糊匹配fuzzy query
name:roam~1 [0,1,2]
匹配与roam差1个character的词,比如foam、roam等
****?q=name:foam~1
结果:foam、roam
- 近似度查询proximity search
“fox quick”~5
以term为单位进行差异比较,比如”quick fox” “quick brown fox”
****?q=name:“quick fox”~1
quick brown fox
DSL
filter | 只过滤符合条件的文档,不计算相关性得分 |
must | 文档必须符合must中的所有条件,会影响相关性得分 |
must_not | 文档中必须不符合must_not中的所有条件 |
should | 文档可以符合should中的条件,会影响相关性得分 |
GET test_search_index/_search
{
"query": {
"bool": {
"must": [
{}
],
"must_not": [
{}
],
"should": [
{}
],
"filter": [
{}
]
}
}
}
相关性算分
相关性算分:指文档与查询语句间的相关度,通过倒排索引可以获取与查询语句相匹配的文档列表
如何将最符合用户查询需求的文档放到前列呢?
本质问题是一个排序的问题,排序的依据是相关性算分,确定倒排索引哪个文档排在前面
影响相关度算分的参数:
- TF(Term Frequency):词频,即单词在文档中出现的次数,词频越高,相关度越高
- Document Frequency(DF):文档词频,即单词出现的文档数
- IDF(Inverse Document Frequency):逆向文档词频,与文档词频相反,即1/DF。即单词出现的文档数越少,相关度越高(如果一个单词在文档集出现越少,算为越重要单词)
- Field-length Norm:文档越短,相关度越高
查看算分
GET test_search_index/_search
{
"explain": true,
"query":{
"match": {
"username": {
"query":"alfred way",
"operator":"and"
}
}
}
}
2、基本概念
Elasticsearch是一个近乎实时(NRT)的搜索平台。这意味着从索引文档到可搜索文档的时间有一点延迟(通常是一秒)。通常有集群,节点,分片,副本等概念。
①集群(cluster)
集群(cluster)是一组具有相同cluster.name
的节点集合,他们协同工作,共享数据并提供故障转移和扩展功能,当然一个节点也可以组成一个集群。
集群由唯一名称标识,默认情况下为“elasticsearch”。此名称很重要,因为如果节点设置为按名称加入集群的话,则该节点只能是集群的一部分。
确保不同的环境中使用不同的集群名称,否则最终会导致节点加入错误的集群。
集群健康状态
集群状态通过 绿,黄,红 来标识
- 绿色 - 一切都很好(集群功能齐全)。
- 黄色 - 所有数据均可用,但尚未分配一些副本(集群功能齐全)。
- 红色 - 某些数据由于某种原因不可用(集群部分功能)。
②节点Node
节点,一个运行的 ES 实例就是一个节点,节点存储数据并参与集群的索引和搜索功能。
就像集群一样,节点由名称标识,默认情况下,该名称是在启动时分配给节点的随机通用唯一标识符(UUID)。如果不需要默认值,可以定义所需的任何节点名称。此名称对于管理目的非常重要,您可以在其中识别网络中哪些服务器与 Elasticsearch 集群中的哪些节点相对应。
可以将节点配置为按集群名称加入特定集群。默认情况下,每个节点都设置为加入一个名为 cluster 的 elasticsearch 集群,这意味着如果您在网络上启动了许多节点并且假设它们可以相互发现 - 它们将自动形成并加入一个名为 elasticsearch 的集群。
③索引 index
索引是具有某些类似特征的文档集合。例如,您可以拥有店铺数据的索引,商品的一个索引以及订单数据的一个索引。
索引由名称标识(必须全部小写),此名称用于在对其中的文档执行索引,搜索,更新和删除操作时引用索引。
④类型 type
类型,曾经是索引的逻辑类别/分区,允许您在同一索引中存储不同类型的文档,例如,一种类型用于用户,另一种类型用于博客帖子。
⑤文档 document
文档是可以建立索引的基本信息单元。例如,您可以为单个客户提供文档,为单个产品提供一个文档,为单个订单提供一个文档。该文档以JSON(JavaScript Object Notation)表示,JSON是一种普遍存在的互联网数据交换格式。
⑥ 分片shards
索引可能存储大量可能超过单个节点的硬件限制的数据。例如,占用1TB磁盘空间的十亿个文档的单个索引可能不适合单个节点的磁盘,或者可能太慢而无法单独从单个节点提供搜索请求。
为了解决这个问题,Elasticsearch 提供了将索引细分为多个称为分片的功能。创建索引时,只需定义所需的分片数即可。每个分片本身都是一个功能齐全且独立的“索引”,可以托管在集群中的任何节点上。
设置分片的目的及原因主要是:
- 它允许您水平拆分/缩放内容量
- 它允许您跨分片(可能在多个节点上)分布和并行化操作,从而提高性能/吞吐量
分片的分布方式以及如何将其文档聚合回搜索请求的机制完全由 Elasticsearch 管理,对用户而言是透明的。
在可能随时发生故障的网络/云环境中,分片非常有用,建议使用故障转移机制,以防分片/节点以某种方式脱机或因任何原因消失。为此,Elasticsearch 允许您将索引的分片的一个或多个副本制作成所谓的副本分片或简称副本。
⑦副本 replicasedit
副本,是对分片的复制。目的是为了当分片/节点发生故障时提供高可用性,它允许您扩展搜索量/吞吐量,因为可以在所有副本上并行执行搜索。
总而言之,每个索引可以拆分为多个分片。索引也可以复制为零次(表示没有副本)或更多次。复制之后,每个索引将具有主分片(从原始分片复制而来的)和复制分片(主分片的副本)。
可以在创建索引时为每个索引定义分片和副本的数量。创建索引后,您也可以随时动态更改副本数。您可以使用_shrink
和 _splitAPI
更改现有索引的分片数,但这不是一项轻松的任务,所以预先计划正确数量的分片是最佳方法。
默认情况下,Elasticsearch 中的每个索引都分配了5个主分片和1个副本,这意味着如果集群中至少有两个节点,则索引将包含5个主分片和另外5个副本分片(1个完整副本),总计为每个索引10个分片。
副本是乘法,越多越浪费,但也越保险。分片是除法,分片越多,单分片数据就越少也越分散。
另外,我们可以画一个对比图来类比传统关系型数据库:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
索引」含义的区分
- 索引(名词) 如上文所述,一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是 indices 或 indexes。
- 索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。这很像SQL中的INSERT关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档。
- 倒排索引 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。
目前与 elasticsearch 交互主要有两种方式:Client API 和 RESTful API。
Client API方式:
Elasticsearch 为以下语言提供了官方客户端 --Groovy、JavaScript、.NET、 PHP、 Perl、 Python 和 Ruby--还有很多社区提供的客户端和插件,所有这些都可以在 Elasticsearch Clients 中找到。后面再开一篇来详细说明。
RESTful API with JSON over HTTP:
所有其他语言可以使用 RESTful API 通过端口 9200 和 Elasticsearch 进行通信,你可以用你最喜爱的 web 客户端访问 Elasticsearch 。事实上,正如你所看到的,你甚至可以使用 curl 命令来和 Elasticsearch 交互。