1. Elasticsearch
Elasticsearch 是一个基于 Apache Lucene 的开源搜索引擎。无论在开源还是专有领域,Lucene 可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
特点:
-
-
-
可以扩展到上百台服务器,处理 PB 级结构化或非结构化数据
Elasticsearch 也使用 Java 开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。
ES 能做什么?
全文检索(全部字段)、模糊查询(搜索)、数据分析(提供分析语法,例如聚合)
2. Elasticsearch 使用案例
-
2013 年初,GitHub 抛弃了 Solr,采取 Elasticsearch 来做 PB 级的搜索。“GitHub 使用 Elasticsearch 搜索 20TB 的数据,包括 13 亿文件和 1300 亿行代码”
-
维基百科:启动以 Elasticsearch 为基础的核心搜索架构
-
百度:百度目前广泛使用 Elasticsearch 作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部 20 多个业务线(包括 casio、云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大 100 台机
-
-
3. Elasticsearch 对比 Solr
-
Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能
-
Solr 支持更多格式的数据,而 Elasticsearch 仅支持 json 文件格式
-
Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供
-
Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch
4. Elasticsearch 基本概念及架构
1) Elasticsearch 基本概念
1. 索引 index
-
一个索引就是一个拥有几分相似特征的文档的集合。比如说,可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引
-
一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字
-
2. 映射 mapping
-
Elasticsearch 中的映射(Mapping)用来定义一个文档
-
mapping 是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的
3. 字段 Field
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
4. 类型 Type
每一个字段都应该有一个对应的类型,例如:Text、Keyword、Byte 等
5. 文档 document
-
一个文档是一个可被索引的基础信息单元。比如,可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以 JSON(Javascript Object Notation)格式来表示,而 JSON 是一个到处存在的互联网数据交互格式
6. 集群 cluster
-
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能
-
一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”
-
这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群
7. 节点 node
-
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能
-
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中
-
这意味着,如果在网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中
-
在一个集群里,可以拥有任意多个节点。而且,如果当前网络中没有运行任何 Elasticsearch 节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
8. 分片和副本 shards&replicas
分片:
-
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有 10 亿文档的索引占据 1TB 的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢
-
为了解决这个问题,Elasticsearch 提供了将索引划分成多份的能力,这些份就叫做分片
-
-
每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上
-
-
-
允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由 Elasticsearch 管理的,对于作为用户来说,这些都是透明的
副本:
-
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch 允许你创建分片的一份或多份拷贝,这些拷贝叫做副本分片,或者直接叫副本
-
-
在分片/节点失败的情况下,提供了高可用性。注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的
-
扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行
每个索引可以被分成多个分片。一个索引有 0 个或者多个副本
一旦设置了副本,每个索引就有了主分片和副本分片,分片和副本的数量可以在索引创建的时候指定
在索引创建之后,可以在任何时候动态地改变副本的数量,但是不能改变分片的数量
2) Elasticsearch 基本架构
Gateway 是 ES 用来存储索引的文件系统,支持多种类型。
Gateway 的上层是一个分布式的 lucene 框架。
Lucene 之上是 ES 的模块,包括:索引模块、搜索模块、映射解析模块等。
ES 模块之上是 Discovery、Scripting 和第三方插件。
-
Discovery 是 ES 的节点发现模块,不同机器上的 ES 节点要组成集群需要进行消息通信,集群内部需要选举 master 节点,这些工作都是由 Discovery 模块完成。支持多种发现机制,如 Zen 、EC2、gce、Azure。
-
Scripting 用来支持在查询语句中插入 javascript、python 等脚本语言,scripting 模块负责解析这些脚本,使用脚本语句性能稍低。ES 也支持多种第三方插件。
再上层是 ES 的传输模块和 JMX.传输模块支持多种传输协议,如 Thrift、memecached、http,默认使用 http。JMX 是 java 的管理框架,用来管理 ES 应用。
最上层是 ES 提供给用户的接口,可以通过 RESTful 接口和 ES 集群进行交互。
Elasticsearch 基础使用
对 Elasticsearch 操作有多种工具,这里我们使用 Kibana,先简单介绍下。
1. Kibana 的基本介绍
Kibana 是一个开源的分析和可视化平台,设计用于和 Elasticsearch 一起工作。
可以用 Kibana 来搜索,查看,并和存储在 Elasticsearch 索引中的数据进行交互
。
可以轻松地执行高级数据分析,并且以各种图标、表格和地图的形式
可视化数据
。
Kibana 使得理解大量数据变得很容易。它简单的、基于浏览器的界面使你能够快速创建和共享动态仪表板,实时显示 Elasticsearch 查询的变化。
2. ES 操作前须知
1) REST 和 CURL 介绍:
REST:
REST(REpresentational State Transfer) 从字面意思看“表述性状态传输”,它通常是开发的一种约定,当所有的开发者都遵从这种约定的时候,可以大大简化开发的沟通成本。
REST 约定用 HTTP 的请求头 POST、GET、PUT、DELETE 正好可以对应 CRUD(Create、Read、Update、Delete)四种数据操作。
如下表所示:
HTTP 方法
|
数据处理
|
说明
|
POST
|
Create
|
新增一个没有 ID 的资源
|
GET
|
Read
|
获取一个资源
|
PUT
|
Update
|
更新一个资源。或新增一个含 ID 的资源(如果 ID 不存在)
|
DELETE
|
Delete
|
删除一个资源
|
CURL:
curl 是利用 URL 语法在命令行方式下工作的开源文件传输工具,使用 curl 可以简单实现常见的 get/post 请求。简单的认为是可以在命令行下面访问 url 的一个工具。在 centos 的默认库里面是有 curl 工具的,如果没有请 yum 安装即可。
curl 后面可跟参数:
-X 指定 http 的请求方法 有
HEAD
GET
POST
PUT
DELETE
-d 指定要传输的数据
-H 指定 http 请求头信息
Elasticsearch 提供了非常全面和强大的 REST API,通过这些 API,我们可以了解集群的信息。这些 API 可以做如下事情:
-
-
-
执行 CRUD(创建、读取、更新和删除)操作,可以对索引进行操作。
-
执行高级搜索操作,如分页、排序、过滤、脚本、聚合及其他操作。
2) ES 请求方式
首先说下请求 ES 的方式,后面例子都是这种方式:
请求参数:curl -X{PUT} http://{host}:{port}
-
例如:
curl -XPUT http://120.0.0.1:9200/blog1?pretty
-
在 Kibana 中可直接输入
PUT /blog1?pretty
body 参数:{参数内容}
{
"settings":{
"index":{
"number_of_shards":1,
"number_of_replicas":0
}
}
}
注意:
请求方法 GET 和 DELETE 上面介绍了,比较简单,不过多说,主要说下 POST 和 PUT 在 ES 中的使用:
POST 和 PUT 共同点:
-
ES 中的
PUT
和
POS
T
同样都具备创建和更新的功能;
POST 和 PUT 不同点:
-
PUT 需要精确到某一个资源文件,这样才能进行对数据的更新和创建操作;
-
POST 能对整个资源集合进行操作,如果没有指定具体修改的文件 id,那么 POST 指令会自动生成一个唯一的 id 进行文件的创建,并将数据写入该文件中。如果指定了文件 id,那么就会根据填写的参数对数据进行创建和更新操作;
-
PUT、GET、DELETE 是幂等的,由于同一条这样的指令,执行多次结果都一样。比如 PUT /uri/xxx 多次,那么结果和这条指令执行一次效果一样。
-
而 POST 是非幂等的,执行多次更改多次服务器状态。比如 POST /uri 多次,那么生成多个 UUID 的 document,执行多次效果和执行一次不一样
3. ES API 操作
1) 创建索引
ES 的索引是一个逻辑概念,它包括了分词列表及文档列表。
同一个索引中存储了相同类型的文档,它就相当于 MySQL 中的表,或相当于 Mongodb 中的集合
。
关于索引这个语:
索引(名词):ES 是基于 Lucene 构建的一个搜索服务,它要从索引库搜索符合条件索引数据。
索引(动词):索引库刚创建起来是空的,将数据添加到索引库的过程称为索引。
PUT http://node01:9200/索引名称?pretty
上面命令最后有个 pretty,什么意思呢,在任意的查询字符串中增加 pretty 参数,会让 Elasticsearch 美化输出(pretty-print) JSON 响应以便更加容易阅读。
打开 kibana,找到 dev tools
出现如下页面,左边是执行区,右边是结果返回区:
左边框中执行以下语句:
curl -XPUT http://node01:9200/blog001/?pretty
上述语句直接粘贴到 kibana 的 dev tools,如下左边所示,它会自动帮我们格式化语句,像 curl -X,和地址都自动隐藏了。
上面执行的命令并没有设置 body 参数,执行的就是默认参数。
执行如下带 body 参数的语句:
curl -XPUT http://node01:9200/blog2/?pretty
{
"settings":{
"index":{
"number_of_shards":1,
"number_of_replicas":0
}
}
}
number_of_shards
:设置分片的数量,在集群中通常设置多个分片,表示一个索引库将拆分成多片分别存储不同的结点,提高了 ES 的处理能力和高可用性,使用单机环境,这里设置为 1。
number_of_replicas
:设置副本的数量,设置副本是为了提高 ES 的高可靠性,单机环境设置为 0。
创建索引相当于关系数据库中的数据库还是表?
-
如果相当于数据库就表示一个索引库可以创建很多不同类型的文档,这在 ES 中也是允许的。
-
如果相当于表就表示一个索引库只能存储相同类型的文档,ES 官方建议
在一个索引库中只存储相同类型的文档。
2) 插入文档
ES 中的文档相当于 MySQL 数据库表中的数据
。
使用 PUT 将一个文档添加到
/_doc
(文档类型),并为该文档分配 ID 为 1。URL 路径显示为
index/doctype/ID(索引/文档类型/ID)
。
curl -XPUT http://node01:9200/blog01/_doc/1?pretty
{"id": "1", "title": "五分钟学大数据"}
注意
:
上面说的文档类型,也叫映射类型(type)
什么是映射类型?
映射类型 简单理解相当于关系数据库的表(不准确,后面会说),每个索引里都可以有一个或多个映射类型,映射类型是 index 中的一个逻辑数据分类,一个映射类型下的文档(document),都有相同的字段(field)。
ES 6.0 之前的版本有映射类型(type) 概念,但是官方将在 ES9.0 版本中彻底删除映射类型。
为什么要移除映射类型?
开始的时候,我们说“索引(index)”类似于 SQL 数据库中的“数据库”,将“类型(type)”等同于“表”。
这是一个糟糕的类比,并且导致了一些错误的假设。在 SQL 数据库中,表之间是相互独立的。一个表中的各列并不会影响到其它表中的同名的列。而在映射类型(mappingtype)中却不是这样的。
在同一个 Elasticsearch 索引中,其中不同映射类型中的同名字段在内部是由同一个 Lucene 字段来支持的。换句话说,假如有两个映射类型 user 和 tweet,user 类型中的 user_name 字段与 tweet 类型中的 user_name 字段是完全一样的,并且两个 user_name 字段在两个类型中必须具有相同的映射(定义)。
这会在某些情况下导致一些混乱,比如,在同一个索引中,当你想在其中的一个类型中将 deleted 字段作为 date 类型,而在另一个类型中将其作为 boolean 字段。
在此之上需要考虑一点,如果同一个索引中存储的各个实体如果只有很少或者根本没有同样的字段,这种情况会导致稀疏数据,并且会影响到 Lucene 的高效压缩数据的能力。
基于这些原因,将映射类型的概念从 Elasticsearch 中移除。
所以
:
在 ES6.0 及之后版本中要弱化映射类型的概念,尽量设置一个无任何业务含义的名字,本文档的所有映射类型都统一设为
_doc
。
3) 查询文档
curl -XGET http://node01:9200/blog01/_doc/1?pretty
4) 更新文档
curl -XPUT http://node01:9200/blog01/_doc/1?pretty
{"id": "1", "title": "【五分钟学大数据】"}
5) 搜索文档
GET /blog01/article/_search?q=title:公众号【五分钟学大数据】
返回值说明
1. Hits
返回结果中最重要的部分是 hits ,它包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档。
在 hits 数组中每个结果包含文档的
_index
、
_type
、
_id
,加上
_source
字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的 ID,需要你单独去获取文档。
每个结果还有一个
_score
,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照
_score
降序排列的。
max_score
值是与查询所匹配文档的
_score
的最大值。
2. took
took 值告诉我们执行整个搜索请求耗费了多少毫秒。
3. Shard
_shards
部分告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。
如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
4. timeout
timed_out
值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10 毫秒),或者 1s(1 秒):
GET /_search?timeout=10ms
在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
6) 删除文档
DELETE /blog01/article/1?pretty
7) 删除索引
DELETE /blog01?pretty
8) 创建映射
概念说明
:
在索引中每个文档都包括了一个或多个字段(field),
创建映射就是向索引库中创建字段及字段类型等的过程
,下面是 document 和 field 与关系数据库的概念的类比:
文档(Document)------Row 记录
字段(Field)------Columns 列
为什么要映射?
ES 中的文档等价于 java 中的对象,那么在 java 对象中有字段类型(比如 string、int、long 等),同理在 ES 索引中的具体字段也是有类型的。
像下面这个操作:
PUT /document/_doc/1
{
"title" : "Elasticsearch 怎么学",
"author" : "五分钟学大数据",
"titleScore" : 60
}
这种操作并没有指定字段类型,那么 Elasticsearch 会自动根据数据类型的格式识别字段的类型;
查看索引的字段类型:
GET /document/_mapping
可以发现 titleScore 的类型是 long。
然后在插入一条数据,注意此时 titleScore 的值不是 long 类型:
PUT /document/_doc/2
{
"title" : "Elasticsearch 怎么学",
"author" : "五分钟学大数据",
"titleScore" : 66.666
}
查询数据:
GET /document/_doc/2
我们会发现 ES 能存入,并没有报错(注意),这其实是一个问题
,因为如果后期 elaticsearch 对接 java 的时候,我们会写一个类对数据做封装,比如:
class Article{
private String title;
private String author;
private String titleScore
}
对于 titleScore,设置什么类型合适?使用 long 类型,那么后面肯定会有数据格式转换的异常 double-long
所以,我们如果能提前知道字段类型,那么最好使用 mapping 的映射管理,提前指定字段的类型,防止后续的程序问题
。
先删除之前创建的索引:
DELETE document
创建映射命令
创建索引,并指定映射:
PUT document
{
"mappings": {
"properties":
{
"title" : {"type": "text"} ,
"author" : {"type": "text"} ,
"titleScore" : {"type": "double"}
}
}
}
查看索引:
GET document/_mapping
对这个索引继续添加字段
:
POST /document/_mapping
{
"properties": {"number" : {"type": "text"}}
}
查看索引:
GET document/_mapping
9) 映射字段的类型
在 Elasticsearch 中,每一个字段都有一个类型(type)。以下为 Elasticsearch 中可以使用的类型:
分类
|
类型名称
|
说明
|
简单类型
|
text
|
需要进行全文检索的字段,通常使用 text 类型来对应正文、产品描述或者短文等
非结构化文本数据
。分词器先会将文本进行分词转换为词条列表。将来就可以基于词条来进行检索了。
文本字段不能用户排序、也很少用户聚合计算
。
|
简单类型
|
keyword
|
使用 keyword 来对应
结构化的数据
,如 ID、电子邮件地址、主机名、状态代码、邮政编码或标签。可以使用 keyword 来进行排序或聚合计算。**
注意:keyword 是不能进行分词的
**。
|
简单类型
|
date
|
保存格式化的日期数据,例如:2015-01-01 或者 2015/01/01 12:10:30。在 Elasticsearch 中,日期都将以字符串方式展示。可以给 date 指定格式:”format”: “yyyy-MM-dd HH:mm:ss”
|
简单类型
|
long/integer/short/byte
|
64 位整数/32 位整数/16 位整数/8 位整数
|
简单类型
|
double/float/half_float
|
64 位双精度浮点/32 位单精度浮点/16 位半进度浮点
|
简单类型
|
boolean
|
“true”/”false”
|
简单类型
|
ip
|
IPV4(192.168.1.110)/IPV6(192.168.0.0/16)
|
JSON 分层嵌套类型
|
object
|
用于保存 JSON 对象
|
JSON 分层嵌套类型
|
nested
|
用于保存 JSON 数组
|
特殊类型
|
geo_point
|
用于保存经纬度坐标
|
特殊类型
|
geo_shape
|
用于保存地图上的多边形坐标
|
10) 获取映射字段
语法:
GET /{index}/_mapping/field/{field}
GET /document/_mapping/field/number
11) 索引库配置管理(settings)
所谓的 settings 就是用来修改索引分片和副本数的。
比如有的重要索引,副本数很少甚至没有副本,那么我们可以通过 setting 来添加副本数。
查看 settings:
GET /document/_settings
可以看到当前的副本数是 1,那么为了提高容错性,我们可以把副本数改成 2:
PUT /document/_settings
{
"number_of_replicas": 2
}
注意:副本可以改,分片不能改!
执行如下语句会报错:
PUT /document/_settings
{
"number_of_shards": 3
}
4. ES 花式查询
我们先插入一些数据,后面查询时以这些数据为例。
在 Kibana 界面上执行如下语句,插入一些数据:
POST /student/_doc/_bulk
{ "index": { "_id": 1 }}
{ "name" : "liubei", "age" : 20 , "sex": "boy", "birth": "1996-01-02" , "about": "i like diaocan he girl" }
{ "index": { "_id": 2 }}
{ "name" : "guanyu", "age" : 21 , "sex": "boy", "birth": "1995-01-02" , "about": "i like diaocan" }
{ "index": { "_id": 3 }}
{ "name" : "zhangfei", "age" : 18 , "sex": "boy", "birth": "1998-01-02" , "about": "i like travel" }
{ "index": { "_id": 4 }}
{ "name" : "diaocan", "age" : 20 , "sex": "girl", "birth": "1996-01-02" , "about": "i like travel and sport" }
{ "index": { "_id": 5 }}
{ "name" : "panjinlian", "age" : 25 , "sex": "girl", "birth": "1991-01-02" , "about": "i like travel and wusong" }
{ "index": { "_id": 6 }}
{ "name" : "caocao", "age" : 30 , "sex": "boy", "birth": "1988-01-02" , "about": "i like xiaoqiao" }
{ "index": { "_id": 7 }}
{ "name" : "zhaoyun", "age" : 31 , "sex": "boy", "birth": "1997-01-02" , "about"