什么是Elasticsearch
你知道的,为了搜索
首先,它是一个全文搜索和分析引擎,有以下三个特点。
- 高度可扩展的
- 开源的
- 基于Lucene的
ELK的版本问题
现在所说的ELK其实就是Elastic Stack(官方称呼),包含ElasticSearch, logstash(服务器端的数据处理管道), kibana(ElasticSearch可视化)和beats(日志收集工具)。
这个网站可以查询所有有关ELK的历史版本https://www.elastic.co/cn/downloads/past-releases,当使用elk成员配合使用时,起码要保证大版本的统一,不能说使用的ElasticSearch是7.x版本的,而kibana却是6.x版本的,会产生版本不兼容问题。
环境搭建
安装 Elasticsearch
在安装地址中选择你想要安装的版本,解压之后即可使用。
如果是集群形式的,可在 …\config\elasticsearch.yml中配置一些你的集群信息:
cluster.name: my-learn # 集群名称 # path.data: /path/to/data # ES数据存储路径 # path.logs: /path/to/logs # ES日志存储路径 node.name: node-1 # 当前节点的名称 # network.host: 0.0.0.0 # 配置当前结点绑定的IP地址 http.port: 9200 # 设置对外服务的HTTP端口,默认为9200
运行Elasticsearch
Linux
./bin/elasticsearch
Windows
...\bin\elasticsearch.bat
当启动日志中出现starting …,标志后可以访问 http://localhost:9200/?pretty 。
{ "name" : "LAPTOP-E0AJGK48", "cluster_name" : "my-application", "cluster_uuid" : "y5dYZ3tdQCqOuWeeSkkOhA", "version" : { "number" : "7.17.0", "build_flavor" : "default", "build_type" : "zip", "build_hash" : "bee86328705acaa9a6daede7140defd4d9ec56bd", "build_date" : "2022-01-28T08:36:04.875279988Z", "build_snapshot" : false, "lucene_version" : "8.11.1", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search" }
当出现如上信息后则代表启动成功。
注意:在Elasticsearch v5.0版本之后需要JVAV8的环境。
安装Web界面(可视化处理)
为了方便我们使用,有两个可视化界面可供选择,elasticsearch-head和kibana。
安装elasticsearch-head插件
安装es-head插件需要有的依赖
- node
- 检查是否有node,在控制台输入node -v,如果有版本号弹出就是有了。
- 没有的可以去下载,下载地址。
- 再次测试是否安装成功。
- grunt
- 在控制台中执行 npm install -g grunt -cli 等待安装成功即可。
- 控制台输入:grunt -version 命令检查验证安装是否成功。
下载es-head
1.github下载:https://github.com/mobz/elasticsearch-head
2.百度云下载:https://pan.baidu.com/s/119GZF4E1NeEgctRxRp88Ng 提取码:oz9q
下载好后进行解压。
进入文件夹修改 Gruntfile.js
找到connect中添加hostname: ‘*’, 如下所示。
connect: { server: { options: { hostname: '*', port: 9100, base: '.', keepalive: true } } }
接着在该文件下打开控制台,输入 npm install,等待安装完成。
接着输入 npm run start 启动es-head插件。
验证:http://localhost:9100/,如果发现无连接可能是
- es 并未启动,启动即可。
- es启动
- 解决方法,在es文件中 …\config\elasticsearch.yml中添加以下配置:
http.cors.enabled: true # 配置跨域资源共享 http.cors.allow-origin: "*"
安装kibana
在下载地址中选择和你下载的es同一个版本的kibana。
解压后在…/config/kibana.yml文件中找到 elasticsearch.hosts: [“http://localhost:9200”],释放。
linux运行
./bin/kibana
windows
bin/kibana.bat # 或是双击kibana.bat文件
验证,访问http://localhost:5601/app/dev_tools#/console出现如下界面,就代表成功了:
基本概念
了解基本概念有助于我们的进一步学习。
集群(Cluster)
集群(cluster)是一组具有相同cluster.name
的节点集合,他们协同工作,共享数据并提供故障转移和扩展功能,当然一个节点也可以组成一个集群。
集群由唯一名称标识,默认情况下为“elasticsearch”。
但建议修改集群名称,使用默认名称可能会导致节点加入错误集群造成不必要的麻烦。
集群名称修改在 elasticsearch.yam 文件中,由 cluster.name 字段控制。
cluster.name: my-learn # 集群名称
要确保不同的环境中使用不同的集群名称,否则最终会导致节点加入错误的集群。
集群的健康值
有三种状态,可以在es-head中查看。
也可以在kibana的控制台中使用 GET /_cluster/health
命令来查看状态。
{ "cluster_name" : "my-learn", "status" : "green", "timed_out" : false, "number_of_nodes" : 1, "number_of_data_nodes" : 1, "active_primary_shards" : 8, "active_shards" : 8, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }
绿色 - 一切都很好(集群功能齐全)。
黄色 - 所有数据均可用,但尚未分配一些副本(集群功能齐全)。
红色 - 某些数据由于某种原因不可用(集群部分功能)。
注意:当群集为红色时,它将继续提供来自可用分片的搜索请求,但您可能需要尽快修复它,因为存在未分配的分片。
节点(Node)
一个正在运行的ES实例就是一个节点,节点存储数据并参与集群的索引和搜索功能。
和集群一样,节点也是由名称进行标识的,默认的名称是随机分配的,这对管理项目以及查看日志非常的不利,所以尽量确定节点的名称。
建议给每个节点设置一个有意义的、清楚的、描述性的名字,同样也是在 elasticsearch.yml 中配置
node.name: elasticsearch_learn
可以将节点配置为按集群名称加入特定集群。默认情况下,每个节点都设置为加入一个名为 elasticsearch 的集群,这意味着如果您在网络上启动了许多节点并且假设它们可以相互发现 - 它们将自动形成并加入一个名为 elasticsearch 的集群。
索引(Index)
索引是具有某些类似特征的文档集合。例如,您可以拥有店铺数据的索引,商品的一个索引以及订单数据的一个索引。
类似于关系型数据库中的表。
索引由名称标识(必须全部小写),此名称用于在对其中的文档执行索引,搜索,更新和删除操作时引用索引。
类型(Type)
在v6.x及以上版本中已弃用。
是索引的逻辑类别/分区,允许您在同一索引中存储不同类型的文档,例如,一种类型用于用户,另一种类型用于博客帖子。
文档(Document)
文档是可以建立索引的基本信息单元。例如,您可以为单个客户提供文档,为单个产品提供一个文档,为单个订单提供一个文档。该文档以JSON(JavaScript Object Notation)格式表示。
类似于关系型数据库中的一行记录。
文档的元数据
es每一个文档,除了保存我们写入进行的文档原始数据外,也有文档自己的元数据,这些元数据,用于标识文档的相关信息。
下面是一个普通的es文档:
{ "_index" : "test_logs2", "_type" : "_doc", "_id" : "1", "_version" : 1, "_seq_no" : 0, "_primary_term" : 1, "found" : true, "_source" : { "uid" : 1, "username" : "test" } }
从上面的文档中,我们可以看文档的元数据字段如下:
_index:文档所在索引名称
_source:原始json数据
_type:文档所属类型,es7.0以后只有为 _doc
_version:文档版本,如果对文档进行修改,则该字段会增加
_score:相关性打分
id:文档唯一id
分片(Shards)
索引存储量可能超过单个节点的硬件限制的数据量。例如,占用1TB磁盘空间的十亿个文档的单个索引可能不适合单个节点的磁盘,或者可能太慢而无法单独从单个节点提供搜索请求。
为了解决这个问题,Elasticsearch提供将索引细分为多个分布在不同的片区,这种功能被称之为分片,也是数据的最小单元块。
类似于关系型数据库的表分区概念。
设置分片的目的及原因主要是:
- 它允许您水平拆分/缩放内容量
- 它允许您跨分片(可能在多个节点上)分布和并行化操作,从而提高性能/吞吐量
分片的分布方式以及如何将其文档聚合回搜索请求的机制完全由 Elasticsearch 管理,对用户而言是透明的。也就是说用户在使用时是感觉不到分片的。
在可能随时发生故障的网络/云环境中,分片非常有用,建议使用故障转移机制,以防分片/节点以某种方式脱机或因任何原因消失。为此,Elasticsearch 允许您将索引的分片的一个或多个制作成所谓的副本分片或简称副本。
副本(Replicasedit)
副本,是对分片的复制。目的是为了当分片/节点发生故障时提供高可用性,它允许您扩展搜索量/吞吐量,因为可以在所有副本上并行执行搜索。
类似于关系型数据库中,担心一个表中数据量过大,新建了一个表。
总而言之,每个索引可以拆分为多个分片。索引也可以复制为零次(表示没有副本)或更多次。复制之后,每个索引将具有主分片(从原始分片复制而来的)和复制分片(主分片的副本)。
可以在创建索引时为每个索引定义分片和副本的数量。创建索引后,您也可以随时动态更改副本数。您可以使用_shrink 和 _splitAPI 更改现有索引的分片数,但这不是一项轻松的任务,所以预先计划正确数量的分片是最佳方法。
相同分片的副本不会放在同一节点。
副本是乘法,越多越浪费,但也越保险。分片是除法,分片越多,单分片数据就越少也越分散。
elasticsearch和关系型数据库的对比
关系型数据库 -> Tables(表) -> Rows(行) -> Columns(列)。
Elasticsearch -> Indeces(索引) -> Documents(文档) -> Fields(属性)。
由于elasticsearch中索引的特殊性,我们有必要将它做一个区分。
「索引」含义的区分
- 索引(名词) 如上文所述,一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是 indices 或 indexes。
- 索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。这很像SQL中的INSERT关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档。
- 倒排索引 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。
交互
elasticsearch交互方式主要取决于你是否使用Java。
Java API
如果你正在使用 Java,在代码中你可以使用 Elasticsearch 内置的两个客户端:
- 节点客户端(Node client)
节点客户端作为一个非数据节点加入到本地集群中。换句话说,它本身不保存任何数据,但是它知道数据在集群中的哪个节点中,并且可以把请求转发到正确的节点。 - 传输客户端(Transport client)
轻量级的传输客户端可以将请求发送到远程集群。它本身不加入集群,但是它可以将请求转发到集群中的一个节点上。
两个 Java 客户端都是通过 9300 端口并使用 Elasticsearch 的原生 传输 协议和集群交互。集群中的节点通过端口 9300 彼此通信。如果这个端口没有打开,节点将无法形成一个集群。
更多的信息可以在Elasticserch客户端找到。
RESTful API with JSON over HTTP
除java外的语言可以使用RESTful API 通过端口 9200 和 Elasticsearch 进行通信,你可以用你最喜爱的 web 客户端访问 Elasticsearch 。事实上,正如你所看到的,你甚至可以使用 curl 命令来和 Elasticsearch 交互。
curlcurl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
curl的缩写格式
一个完整的curl请求为
curl -XGET 'localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }'
curl的缩写格式就是省略请求中所有相同的部分,例如主机名、端口号以及 curl 命令本身。下面是他的缩写格式。
GET /_count { "query": { "match_all": {} } }
CRUD
可以在kibana控制台进行命令的执行操作。
插入文档信息
插入单条文档信息
PUT /learn/user/1 { "first_name" : "三", "last_name" : "张", "age" : 25, "about" : "法外狂徒", "interests": [ "偷盗", "抢劫","嘿嘿" ] }
有关路径 /learn/user/1 包含了三部分信息:
- learn 索引名称
- user 类型名称
- 1 ID
当出现如下信息时代表插入成功
{ "_index" : "learn", "_type" : "user", "_id" : "1", "_version" : 2, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 3, "_primary_term" : 1 }
在这里我们指定了文档的id,如果不指定id而是使用如下命令,那么es将会自动给其分配一个id。这个id是URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串,产生冲突的几率几乎为0。
POST /learn/user/ { "first_name" : "三", "last_name" : "张", "age" : 25, "about" : "法外狂徒", "interests": [ "偷盗", "抢劫","嘿嘿" ] }
我又插入两条信息
PUT /learn/user/2 { "first_name" : "xk", "last_name" : "蔡", "age" : 99, "about" : "基尼太美", "interests": [ "唱", "跳","rap","篮球" ] } PUT /learn/user/3 { "first_name" : "倍", "last_name" : "安", "age" : 10, "about" : "散弹枪狂热者", "interests": [ "盗窃","篮球" ] }
我们下面将讲到更改文档,如果使用PUT插入文档的话很容易造成文档的覆盖,这显然不是我们想要看到的。我们想要的是当文档不存在时再新增文档。有如下方法
# 新增文档但不更改,1 PUT /learn/user/5/_create { "first_name" : "先森", "last_name" : "双口", "age" : 10, "about" : "散弹批发商", "interests": [ "嘿嘿","游戏" ] } # 新增文档但不更改,2 PUT /learn/user/6?op_type=create { "first_name" : "先森", "last_name" : "双口", "age" : 10, "about" : "散弹批发商", "interests": [ "嘿嘿","游戏" ] }
如果文档已近存在返回409状态码,如果成功添加返回201状态码。
更改文档信息
使用PUT请求
当一个文档已经存在是,再次使用PUT插入文档的方法就可以做到文档的更改。
PUT /learn/user/3 { "first_name" : "小日本鬼子", "last_name" : "安", "age" : 10, "about" : "散弹枪狂热者", "interests": [ "盗窃","篮球" ] }
更改完成后version会改变。
在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 尽管你不能再对旧版本的文档进行访问,但它并不会立即消失。当继续索引更多的数据,Elasticsearch 会在后台清理这些已删除文档。
文档的部分更改
我们对文档进行更改时使用的是 update
API。
文档是不可变的:他们不能被修改,只能被替换,所以我们对文档进行更改时其实时拿了个新的文档将老的文档给替换掉了
update
请求最简单的一种形式是接收文档的一部分作为 doc
的参数, 它只是与现有的文档进行合并。对象被合并到一起,覆盖现有的字段,增加新的字段。
我们将user中的安倍的first_name
改为鬼子就可以进行如下操作。
POST /learn/user/3/_update { "doc" : { "first_name" : "鬼子" } }
结果
{ "_index" : "learn", "_type" : "user", "_id" : "3", "_version" : 4, "_seq_no" : 21, "_primary_term" : 2, "found" : true, "_source" : { "first_name" : "鬼子", "last_name" : "安", "age" : 10, "about" : "散弹枪狂热者", "interests" : [ "盗窃", "篮球" ] } }
我们也可以使用脚本对部分文档进行更新。
脚本可以在 update
API中用来改变 _source
的字段内容, 它在更新脚本中称为 ctx._source
。
下面使用脚本对安鬼子的年龄进行修改。
POST /learn/user/3/_update { "script" : "ctx._source.age=0" }
结果
{ "_index" : "learn", "_type" : "user", "_id" : "3", "_version" : 5, "_seq_no" : 22, "_primary_term" : 2, "found" : true, "_source" : { "first_name" : "鬼子", "last_name" : "安", "age" : 0, "about" : "散弹枪狂热者", "interests" : [ "盗窃", "篮球" ] } }