入门
简记
Elasticsearch 是一个使用java编写的面向文档的建立在全文搜索引擎库 Apache Lucene™ 基础之上开源的搜索引擎;
Lucene 仅仅只是一个库(当下最先进、高性能、全功能的搜索引擎库--无论是开源还是私有);
然而,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确的形容:
- 一个分布式的实时文档存储,每个字段 可以被索引与搜索
- 一个分布式实时分析搜索引擎
- 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据
Elasticsearch 将所有的功能打包成一个单独的服务,这样你可以通过程序与它提供的简单的 RESTful API 进行通信, 可以使用自己喜欢的编程语言充当 web 客户端,甚至可以使用命令行(去充当这个客户端)。
Elasticsearch 不仅存储文档,而且 _索引 每个文档的内容使之可以被检索。在 Elasticsearch 中,你 对文档进行索引、检索、排序和过滤--而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。
Elasticsearch 使用 JavaScript Object Notation 或者 JSON 作为文档的序列化格式。JSON 序列化被大多数编程语言所支持,并且已经成为 NoSQL 领域的标准格式。 它简单、简洁、易于阅读。
注意 :
源码地址:github.com/elastic/elasticsearch
集群内的原理、分布式文档存储、执行分布式检索、分布内部原理;
近似匹配、部分匹配;
suggestions、geolocation、percolation、fuzzy 与 partial matching 等特性
分布式特性:
Elasticsearch 天生就是分布式的,并且在设计时屏蔽了分布式的复杂性。
列举一些后台自动执行的操作:
- 分配文档到不同的容器 或 分片 中,文档可以储存在一个或多个节点中
- 按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡
- 复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失
- 将集群中任一节点的请求路由到存有相关数据的节点
- 集群扩容时无缝整合新节点,重新分配分片以便从离群节点恢复
安装并运行
linux下elasticsearch 安装、配置及示例
https://blog.csdn.net/sinat_28224453/article/details/51134978
windows 下安装elasticsearch
https://blog.csdn.net/sinat_28224453/article/details/51516061
https://blog.csdn.net/u014236259/article/details/53696133
下载:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.0.zip
内置客户端
节点客户端(Node client)
节点客户端作为一个非数据节点加入到本地集群中。换句话说,它本身不保存任何数据,但是它知道数据在集群中的哪个节点中,并且可以把请求转发到正确的节点。
传输客户端(Transport client)
轻量级的传输客户端可以将请求发送到远程集群。它本身不加入集群,但是它可以将请求转发到集群中的一个节点上。
两个 Java 客户端都是通过 9300 端口并使用 Elasticsearch 的原生 传输 协议和集群交互。集群中的节点通过端口 9300 彼此通信。如果这个端口没有打开,节点将无法形成一个集群。
Java 客户端作为节点必须和 Elasticsearch 有相同的 主要 版本;否则,它们之间将无法互相理解。
雇员目录案例分析
需求:
- 支持包含多值标签、数值、以及全文本的数据
- 检索任一雇员的完整信息
- 允许结构化搜索,比如查询 30 岁以上的员工
- 允许简单的全文搜索以及较复杂的短语搜索
- 支持在匹配文档内容中高亮显示搜索片段
- 支持基于数据创建和管理分析仪表盘
索引雇员文档
存储数据到 Elasticsearch 的行为叫做 索引。
一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
- 每个雇员索引一个文档,包含该雇员的所有信息。
- 每个文档都将是
employee
类型 。 - 该类型位于 索引
megacorp
内。 - 该索引保存在我们的 Elasticsearch 集群中。
# 索引名称 类型名称 特定雇员的ID PUT /megacorp/employee/1 { "first_name" : "John", "last_name" : "Smith", "age" : 25, "about" : "I love to go rock climbing", "interests": [ "sports", "music" ] }
curl命令执行
索引
curl -XPUT http://localhost:9200/megacorp/employee/?pretty -d "{\"first_name\":\"John\",\"last_name\":\"Smith\",\"age\" :25,\"about\" :\"I love to go rock climbing\",\"interests\": [\"sports\", \"music\" ]}"
检索文档
?pretty 结构化文档输出
curl -XGET http://localhost:9200/megacorp/employee/1?pretty
删除
curl -XDELETE http://localhost:9200/megacorp/employee/1?pretty
轻量搜索
简单地执行 一个 HTTP GET
请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:
GET /megacorp/employee/1
GET /megacorp/employee/_search
GET /megacorp/employee/_search?q=last_name:Smith 搜索完赋值给q
查询表达式搜索
等同于上
GET /megacorp/employee/_search { "query" : { "match" : { "last_name" : "Smith" } } }
过滤器 filter
年龄大于 30 的
GET /megacorp/employee/_search { "query" : { "bool": { "must": { "match" : { "last_name" : "smith" } }, "filter": { "range" : { "age" : { "gt" : 30 } } } } } }
全文搜索
GET /megacorp/employee/_search { "query" : { "match" : { "about" : "rock climbing" } } }
结果区别于传统的关系型数据库:(传统关系型数据库中的一条记录要么匹配要么不匹配)
{ ... "hits": { "total": 2, "max_score": 0.16273327, "hits": [ { ... "_score": 0.16273327, "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } }, { ... "_score": 0.016878016, "_source": { "first_name": "Jane", "last_name": "Smith", "age": 32, "about": "I like to collect rock albums", "interests": [ "music" ] } } ] } }
相关性得分
Elasticsearch 默认按照相关性得分排序,即每个文档跟查询的匹配程度。第一个最高得分的结果很明显:John Smith 的 about
属性清楚地写着 “rock climbing” 。
但为什么 Jane Smith 也作为结果返回了呢?原因是她的 about
属性里提到了 “rock” 。因为只有 “rock” 而没有 “climbing” ,所以她的相关性得分低于 John 的。
短语搜索(精准)
上边的match
修改为 match_phrase
的查询:
高亮搜索
每个搜索结果中 高亮 部分文本片段,以便让用户知道为何该文档符合查询条件。
GET /megacorp/employee/_search { "query" : { "match_phrase" : { "about" : "rock climbing" } }, "highlight": { "fields" : { "about" : {} } } }
{ ... "hits": { "total": 1, "max_score": 0.23013961, "hits": [ { ... "_score": 0.23013961, "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] }, "highlight": { "about": [ "I love to go <em>rock</em> <em>climbing</em>" ] } } ] } }
分析/聚合aggregations
检索雇员所有的兴趣interests
GET /megacorp/employee/_search { "aggs": { "all_interests": { "terms": { "field": "interests" } } } }
结果
{ ... "hits": { ... }, "aggregations": { "all_interests": { "buckets": [ { "key": "music", "doc_count": 2 }, { "key": "forestry", "doc_count": 1 }, { "key": "sports", "doc_count": 1 } ] } } }
指定人员smith的兴趣爱好,组合查询结果只包含匹配查询的文档。
GET /megacorp/employee/_search { "query": { "match": { "last_name": "smith" } }, "aggs": { "all_interests": { "terms": { "field": "interests" } } } }
聚合还支持分级汇总 。比如,查询特定兴趣爱好员工的平均年龄:
GET /megacorp/employee/_search { "aggs" : { "all_interests" : { "terms" : { "field" : "interests" }, "aggs" : { "avg_age" : { "avg" : { "field" : "age" } } } } } }
聚合结果:每个兴趣都有了一个附加的 avg_age
属性代表平均年龄。
... "all_interests": { "buckets": [ { "key": "music", "doc_count": 2, "avg_age": { "value": 28.5 } }, { "key": "forestry", "doc_count": 1, "avg_age": { "value": 35 } }, { "key": "sports", "doc_count": 1, "avg_age": { "value": 25 } } ] }
集群拓展
后续完善
文档处理
Elasticsearch中文档:指最顶层或者根对象, 这个根对象被序列化成 JSON 并存储到 Elasticsearch 中,指定了唯一 ID
文档元数据:
_index
索引,文档在哪存放;共同的特性被分组到一起的文档集合。例如:商品products,交易sales
实际上,在 Elasticsearch 中,我们的数据是被存储和索引在 分片 中,而一个索引仅仅是逻辑上的命名空间, 这个命名空间由一个或者多个分片组合在一起。
_type
文档表示的对象类别:所有的产品放在同一个索引中,但是不用的产品类别:"electronics" 、 "kitchen" 。
types 的文档可能有不同的字段,但最好能够非常相似。命名可以是大写或者小写,但是不能以下划线或者句号开头,不应该包含逗号, 并且长度限制为256个字符. 我们使用 blog
作为类型名举例。
_id
文档唯一标识
ID 是一个字符串, 当它和 _index
以及 _type
组合就可以唯一确定 Elasticsearch 中的一个文档。 当你创建一个新的文档,要么提供自己的 _id
,要么让 Elasticsearch 帮你生成。
索引文档
参照上边的雇员,PUT /megacorp/employee/1 修改为PUT /megacorp/employee即自动生成一个。
自动生成的 ID 是 URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串。 这些 GUID 字符串由可修改的 FlakeID 模式生成,这种模式允许多个节点并行生成唯一 ID ,且互相之间的冲突概率几乎为零。
查询
取回一个:GET /website/blog/123?pretty pretty增加可读性
返回文档一部分:GET /website/blog/123?_source=title,text
只想得到 _source
字段,不需要任何元数据,你能使用 _source
端点:GET /website/blog/123/_source
文档是否存在:
更新文档
- 从旧文档构建 JSON
- 更改该 JSON
- 删除旧文档
- 索引一个新文档
创建新文档
POST /website/blog/
{ ... }
PUT /website/blog/123?op_type=create
{ ... }
等同于
PUT /website/blog/123/_create
{ ... }
删除:DELETE /website/blog/123
乐观并发控制
Elasticsearch 是分布式的。当文档创建、更新或删除时, 新版本的文档必须复制到集群中的其他节点。Elasticsearch 也是异步和并发的,这意味着这些复制请求被并行发送,并且到达目的地时也许 顺序是乱的 。 Elasticsearch 需要一种方法确保文档的旧版本不会覆盖新的版本。
每个文档都有一个 _version
(版本)号,当文档被修改时版本号递增。 Elasticsearch 使用这个 _version
号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。
我们可以利用 _version
号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version
号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。
示例:
1.新建一个文章,其 _version
版本号是 1
PUT /website/blog/1/_create { "title": "My first blog entry", "text": "Just trying this out..." }
2.先查询,再编辑保存,指定 version
为我们的修改会被应用的版本:
PUT /website/blog/1?version=1 { "title": "My first blog entry", "text": "Starting to get the hang of this..." }
3.成功会 _version
+1 ,失败会报错
通过外部系统使用版本控制
场景:
其它数据库作为主要的数据存储,使用 Elasticsearch 做数据检索, 这意味着主数据库的所有更改发生时都需要被复制到 Elasticsearch ,如果多个进程负责这一数据同步,你可能遇到类似于之前描述的并发问题。