es学习笔记记录整理,不定期更新
ELK,分别是Elasticsearch、Logstash、Kibana组成 ,之后又有新成员Beats的加入,所以就形成了Elastic Stack。
Elasticsearch
Elasticsearch 基于java,是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。
Logstash
Logstash 基于java,是一个开源的用于收集,分析和存储日志的工具
Kibana
Kibana 基于nodejs,也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的Web 界面,可以汇总、分析和搜索重要数据日志
Beats
Beats是elastic公司开源的一款采集系统监控数据的代理agent,是在被监控服务器上以客户端形式运行的数据收集器的统称,可以直接把数据发送给Elasticsearch或者通过Logstash发送给Elasticsearch,然后进行后续的数据分析活动 。
Beats由如下组成:
Elastic Stack的核心,全文搜索引擎。官网:https://www.elastic.co/cn/products/elasticsearch
目前主流版本:7.2.0
#创建elsearch用户以及主目录,Elasticsearch不支持root用户运行
useradd -d /home/es -m elasticsearch
# 设置密码 12345678即可
passwd elasticsearch
#下载
cd /home/es
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.2.0-linux-x86_64.tar.gz
#解压安装包
tar -zxvf elasticsearch-7.2.0-linux-x86_64.tar.gz
#修改配置文件
vim config/elasticsearch.yml
network.host: 0.0.0.0 #设置ip地址,任意网络均可访问
#修改jvm启动参数
vim conf/jvm.options
-Xms128m #根据自己机器情况修改
-Xmx128m
# ===== root用户下的操作 ======
# 修改文件描述符 设定同一时间打开的文件数的最大值为65535
vim /etc/profile
ulimit -n 65536
# 生效
source /etc/profile
# 修改limits.conf
vim /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
# 验证
ulimit -a
# 修改 最大映射数量 MMP
vim /etc/sysctl.conf
vm.max_map_count=262144
# 生效
sysctl -p
# ====启动服务====
su - elasticsearch
cd /home/es/elasticsearch-7.2.0-linux-x86_64/bin
./elasticsearch 或 ./elasticsearch -d #后台启动
elasticsearch.yml可参考
cluster.name: cms
network.host: 0.0.0.0
# custom config
node.name: "node-1"
discovery.seed_hosts: ["127.0.0.1", "[::1]"]
cluster.initial_master_nodes: ["node-1"]
# 开启跨域访问支持,默认为false
http.cors.enabled: true
# 跨域访问允许的域名地址,(允许所有域名)以上使用正则
http.cors.allow-origin: /.*/
ES官方并没有为ES提供界面管理工具,仅仅是提供了后台的服务。elasticsearch-head是一个为ES开发的一个页
面客户端工具,其源码托管于GitHub,地址为:https://github.com/mobz/elasticsearch-head
建议通过docker或Chrome插件安装
docker安装
#拉取镜像
docker pull mobz/elasticsearch-head:5
#创建容器
docker create --name elasticsearch-head -p 9100:9100 mobz/elasticsearch-head:5
#启动容器
docker start elasticsearch-head
docker安装可能出现的问题:
问题: 使用 Elasticsearch Head 查看“数据浏览”时,右侧不出数据,使用浏览器F12查看后,发现 406 Not Acceptable 错误
解决:
进入 es-head 安装目录;
cd _site/
编辑 vendor.js 共有两处:
将 6886行 contentType: “application/x-www-form-urlencoded” 修改为 contentType: “application/json;charset=UTF-8”
将 7574行 var inspectData = s.contentType === “application/x-www-form-urlencoded” && 修改为 var inspectData = s.contentType === “application/json;charset=UTF-8” &&
刷新浏览器验证
索引
文档
存储在Elasticsearch中的主要实体叫文档(document)。用关系型数据库来类比的话,一个文档相当于数据库表中的一行记录
Elasticsearch和MongoDB中的文档类似,都可以有不同的结构,但Elasticsearch的文档中,相同字段必须有相同类型
文档由多个字段组成,每个字段可能多次出现在一个文档里,这样的字段叫多值字段(multivalued)
每个字段的类型,可以是文本、数值、日期等。字段类型也可以是复杂类型,一个字段包含其他子文档或者数组
映射
文档类型
在Elasticsearch中,提供了功能丰富的RESTful API的操作,包括基本的CRUD、创建索引、删除索引等操作
7.x以后的更新:
PUT {index}/{type}/{id}
需要修改成PUT {index}/_doc/{id}
PUT {index}/{type}/_mapping
则变成 PUT {index}/_mapping
_type
都将被移除在Lucene中,创建索引是需要定义字段名称以及字段的类型的,在Elasticsearch中提供了非结构化的索引,就是不
需要创建索引结构,即可写入数据到索引中,实际上在Elasticsearch底层会进行结构化操作,此操作对用户是透明
创建空索引 :
PUT /haoke
{
"settings": {
"index": {
"number_of_shards": "2", #分片数
"number_of_replicas": "0" #副本数
}
}
}
#删除索引
DELETE /haoke
{
"acknowledged": true
}
url规则:POST /{索引}/{类型}/{id}
7.x以后为/{索引}/_doc/{id}
POST /haoke/user/1001
#数据
{
"id":1001,
"name":"张三",
"age":20,
"sex":"男"
}
#响应
{
"_index": "haoke",
"_type": "user",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
说明:非结构化的索引,不需要事先创建,直接插入数据默认创建索引
不指定id插入数据:
POST /haoke/user/
{
"id":1002,
"name":"张三",
"age":20,
"sex":"男"
}
在Elasticsearch中,文档数据是不可以修改的,但是可以通过覆盖的方式进行更新
PUT /haoke/user/1001
{
"id":1001,
"name":"张三",
"age":21,
"sex":"女"
}
更新后,版本_version
字段会加1
局部更新:
#注意:这里多了_update标识
POST /haoke/user/1001/_update
{
"doc":{
"age":23
}
}
发起DELETE请求
DELETE /haoke/user/1001
删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。Elasticsearch将会在你之后添加更多索引的时候才会在后台进行删除内容的清理
GET /haoke/user/BbPe_WcB9cFOnF3uebvr
GET /haoke/user/_search
默认返回10条数据
7.x以后
GET /haoke/_search
#查询年龄等于20的用户
GET /haoke/user/_search?q=age:20
DSL查询(Query DSL),它允许你构建更加复杂、强大的查询
DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现
POST /haoke/user/_search
#请求体
{
"query" : {
"match" : { #match只是查询的一种
"age" : 20
}
}
}
POST /haoke/user/_search
#请求数据
{
"query": {
"bool": {
"filter": {
"range": {
"age": {
"gt": 30
}
}
},
"must": {
"match": {
"sex": "男"
}
}
}
}
}
POST /haoke/user/_search
#请求数据
{
"query": {
"match": {
"name": "张三 李四"
}
}
}
POST /haoke/user/_search
{
"query": {
"match": {
"name": "张三 李四"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
类似SQL中的group by操作
POST /haoke/user/_search
{
"aggs": {
"all_interests": {
"terms": {
"field": "age"
}
}
}
}
年龄30的有2条数据,20的有一条,40的一条
文档以JSON格式进行存储,可以是复杂的结构
{
"_index": "haoke",
"_type": "user",
"_id": "1005",
"_version": 1,
"_score": 1,
"_source": {
"id": 1005,
"name": "孙七",
"age": 37,
"sex": "女",
"card": {
"card_number": "123456789"
}
}
}
其中,card是一个复杂对象,嵌套的Card对象
元数据 metadata
文档不只有数据,还包含元数据metadata–关于文档的信息。
三个必须的元数据节点:
_index
文档存储的地方_type
文档代表的对象的类,7.x以后type只有_doc
_id
文档的唯一标识_index
索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方
物理空间上,数据被存储和索引在分片shards中,索引是把一个或多个分片分组在一起的逻辑空间。当然,java应用不用考虑分片,认为存储在索引中。
_type
使用相同类型(type)的文档表示相同的“事物”,因为他们的数据结构也是相同的,类似于java的类class
每个类型(type)都有自己的映射(mapping)或者结构定义,就像传统数据库表中的列一样。所有类型下的文档被存储在同一个索引下,但是类型的映射(mapping)会告诉Elasticsearch不同的文档如何被索引
_id
id和_index
组合,在es中唯一标识一个文档,可自己定义id,也可es自动生成32位id。
可以在查询url后面添加pretty参数,美化json格式
xxx/user/1005?pretty
响应的数据中,如果不需要全部的字段,可以指定某些需要的字段进行返回
GET /haoke/user/1005?_source=id,name
#响应
{
"_index": "haoke",
"_type": "user",
"_id": "1005",
"_version": 1,
"found": true,
"_source": {
"name": "孙七",
"id": 1005
}
}
如果不需要返回元数据
GET /haoke/user/1005/_source
或
GET /haoke/user/1005/_source?_source=id,name
只需要判断文档是否存在,而不是查询文档内容,那么可以这样
HEAD /haoke/user/1005
只返回status是200还是404,表示查询的那一刻文档不存在
POST /haoke/user/_mget
{
"ids" : [ "1001", "1003" ]
}
如果,某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据
支持批量的插入、修改、删除操作,都是通过_bulk的api完成的
格式:
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
...
批量插入
{"create":{"_index":"haoke","_type":"user","_id":2001}}
{"id":2001,"name":"name1","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2002}}
{"id":2002,"name":"name2","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2003}}
{"id":2003,"name":"name3","age": 20,"sex": "男"}
最后一行需要有个回车
批量删除
{"delete":{"_index":"haoke","_type":"user","_id":2001}}
{"delete":{"_index":"haoke","_type":"user","_id":2002}}
{"delete":{"_index":"haoke","_type":"user","_id":2003}}
一次请求多少个性能最高?
最佳点sweetspot需要测试,开始数量可以在1000-5000个文档之间,一个好的批次最好在5-15MB大小。
和SQL使用 LIMIT
关键字返回只有一页的结果一样,Elasticsearch接受from
和 size
参数
size: 结果数,默认10
from: 跳过开始的结果数,默认0
如果你想每页显示5个结果,页码从1到3:
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
需要当心分页太深或一次请求过多的情况,因为结果返回之前会排序。
有些时候我们是需要进行明确字段类型的,否则,自动判断的类型和实际需求是不相符的
从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代
PUT /school
{
"settings": {
"index": {
"number_of_shards": "2",
"number_of_replicas": "0"
}
},
"mappings": {
"person": { # 7.x以后需要把person这个type删除
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"mail": {
"type": "keyword"
},
"hobby": {
"type": "text"
}
}
}
}
}
查看映射
GET /school/_mapping
term
主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed
的字符串(未经分析的文本数据类型)
POST /school/person/_search
{
"query" : {
"term" : {
"age" : 20
}
}
}
terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配
{
"terms": {
"tag": [ "search", "full_text", "nosql" ]
}
}
eg.
POST /itcast/person/_search
{
"query" : {
"terms" : {
"age" : [20,21]
}
}
}
按照指定范围查找一批数据:
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
查找文档中是否包含指定字段或没有某个字段
POST /haoke/user/_search
{
"query": {
"exists": { #必须包含
"field": "card"
}
}
}
会在真正查询之前用分析器先分析 match 一下查询字符
{
"match": {
"tweet": "Leehom"
}
}
合并多个条件查询结果,包含操作符
must
多个查询条件的完全匹配 类似&
must not
多个查询条件的相反匹配,类似!
should
至少有一个 类似or
{
"bool": {
"must": { "term": { "folder": "inbox" }},
"must_not": { "term": { "tag": "spam" }},
"should": [
{ "term": { "starred": true }},
{ "term": { "unread": true }}
]
}
}
如,查询年龄20的用户
POST /itcast/person/_search
{
"query": {
"bool": {
"filter": {
"term": {
"age": 20
}
}
}
}
}
查询和过滤的对比
_score
,并且 按照相关性对匹分词就是指将一个文本转化成一系列单词的过程,也叫文本分析,在Elasticsearch中称之为Analysis,如我是中国人 --> 我/是/中国人
指定分词器进行分词
POST /_analyze
{
"analyzer":"standard",
"text":"hello world"
}
指定索引分词
POST /itcast/_analyze
{
"analyzer": "standard",
"field": "hobby",
"text": "听音乐"
}
推荐IK分词器,算法是正向迭代最细粒度切分算法。
IK分词器 Elasticsearch插件地址:https://github.com/medcl/elasticsearch-analysis-ik
cd plugins;
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.2.0/elasticsearch-analysis-ik-7.2.0.zip;
mkdir ik;cd ik;
unzip ../elasticsearch-analysis-ik-6.5.4.zip
# 重启
./bin/elasticsearch
测试
POST /_analyze
{
"analyzer": "ik_max_word",
"text": "我是中国人"
}
全文搜索两个最重要的方面是:
POST /itcast/person/_search
{
"query":{
"match":{
"hobby":"音乐"
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
内部执行过程:
将match的hobby改为 "hobby":"音乐 篮球"
如果想搜索的是既包含“音乐”又包含“篮球”的用户,可指定词之间的逻辑关系
"match":{
"hobby":{
"query":"音乐 篮球",
"operator":"and"
}
}
如果要符合一定的相似度就可以查询到数据,在Elasticsearch中也支持这样的查询,通过minimum_should_match来指定匹配度,如:70%
"match":{
"hobby":{
"query":"游泳 羽毛球",
"minimum_should_match":"80%"
}
}
具体相似度需要结合业务需求反复测试。
在搜索时,也可以使用过滤器中讲过的bool组合查询,示例
"query":{
"bool":{
"must":{
"match":{
"hobby":"篮球"
}
},
"must_not":{
"match":{
"hobby":"音乐"
}
},
"should":[
{
"match": {
"hobby":"游泳"
}
}
]
}
}
must_not
不影响评分,作用只是将不相关的文档排除。
有些时候,我们可能需要对某些词增加权重来影响该条数据的得分
eg. 搜索关键字为“游泳篮球”,如果结果中包含了“音乐”权重为10,包含了“跑步”权重为2
"query": {
"bool": {
"must": {
"match": {
"hobby": {
"query": "游泳篮球",
"operator": "and"
}
}
},
"should": [
{
"match": {
"hobby": {
"query": "音乐",
"boost": 10
}
}
},
{
"match": {
"hobby": {
"query": "跑步",
"boost": 2
}
}
}
]
}
}
ELasticsearch的集群是由多个节点组成的,通过cluster.name设置集群名称,并且用于区分其它的集群,每个节点通过node.name指定节点的名称
节点类型:
master节点
data节点
客户端节点
部落节点
# 3个虚拟机,分别部署安装es
mkdir /home/es-cluster
cp -R /home/es /home/es-cluster
elasticsearch.yml的配置修改如下面的文件,node02设置为master节点,node03设置为普通数据节点。
具体的elasticsearch.yml文件
cluster.name: es-itcast-cluster
node.name: node01
node.master: true
node.data: true
network.host: 192.168.200.207
http.port: 9200
discovery.zen.ping.unicast.hosts: ["192.168.200.207","192.168.200.208","192.168.200.209"]
discovery.zen.minimum_master_nodes: 2
http.cors.enabled: true
http.cors.allow-origin: "*"
cluster.initial_master_nodes: ["192.168.200.207","192.168.200.208"]
查看集群状态:_cluster/health
返回状态为green表示正常。
停掉node01,elasticsearch-header中cluster状态变黄,过一会会自动变绿。
将node01再启动,恢复后会重新加入cluster,重新分配节点信息。
说明:如果在配置文件中discovery.zen.minimum_master_nodes设置的不是N/2+1时,会出现脑裂问题,因此建议3个主机的,设置2个master;5个主机的,设置3个master
当我们想一个集群保存文档时,文档该存储到哪个节点呢? 是随机吗? 是轮询吗?
实际有个计算公式,决定存储节点:
shard = hash(routing) % number_of_primary_shards
routing值默认是_id
,hash后,除以主分片数得到余数,余数的范围是0到主分片数之间,该值就是该文档所在的分片。
新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的副本分片上
步骤:
_id
计算文档属于分片0,转发请求到node03,分片0的主分片位于该节点步骤:
_id
确定文档属于分片0,对应的副本分片在node01和node02都有,转发请求到node02.对于读请求,为平衡负载,会为每个请求选择不同的分片。
可能一个被索引的文档存在于主分片,还没来得及同步到副本分片,副本分片会报告未找到,主分片成功返回文档。索引请求成功返回给client后,文档在primary和replica分片都可用。
文档分散在各个节点,分布式情况下,如何搜索?分两个阶段:搜索query+取回fetch
搜索query
查询阶段分三步:
from+size
的空优先队列from+size
的本地优先队列取回fetch
分发阶段的步骤:
建议DSL查询,采用高阶客户端,后续续写这类代码
#!/bin/sh
ps -ef | grep es | grep java | grep `whoami` | grep -v grep | awk '{print $2}' | while read pid
do
kill -9 ${pid} 2>&1 >/dev/null
echo "process id:${pid} shutdown successful!"
done
indices.memory.index_buffer_size: 30%
(默认10%)首先是jdk调优
写优化
index.refresh_interval:30s
读优化
系统