es学习笔记

一、es简介?

1 es介绍

简介:es是基于Apache lucene的开源分布式(全文)搜索引擎,提供简单的restful API来隐藏Lucene的复杂性。
es数据库类比:

Relational DB 数据库(database) 表(tables) 行(rows) 字段(columns)
Elasticsearch 索引(incices) types documents fields

ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向扩展至数以百计的服务器存储以及处理PB级的数据。
可以在极短的时间内存储、搜索和分析大量的数据。通常作为具有复杂搜索场景情况下的核心发动机。
ES就是为高可用和可扩展而生的。一方面可以通过升级硬件来完成系统扩展,称为垂直或向上扩展(Vertical Scale/Scaling Up)。
另一方面,增加更多的服务器来完成系统扩展,称为水平扩展或者向外扩展(Horizontal Scale/Scaling Out)。尽管ES能够利用更强劲的硬件,但是垂直扩展毕竟还是有它的极限。真正的可扩展性来自于水平扩展,通过向集群中添加更多的节点来分担负载,增加可靠性。ES天生就是分布式的,它知道如何管理多个节点来完成扩展和实现高可用性。意味应用不需要做任何的改动。
Gateway,代表ES索引的持久化存储方式。在Gateway中,ES默认先把索引存储在内存中,然后当内存满的时候,再持久化到Gateway里。当ES集群关闭或重启的时候,它就会从Gateway里去读取索引数据。比如LocalFileSystem和HDFS、AS3等。
DistributedLucene Directory,它是Lucene里的一些列索引文件组成的目录。它负责管理这些索引文件。包括数据的读取、写入,以及索引的添加和合并等。
River,代表是数据源。是以插件的形式存在于ES中。 
Mapping,映射的意思,非常类似于静态语言中的数据类型。比如我们声明一个int类型的变量,那以后这个变量只能存储int类型的数据。比如我们声明一个double类型的mapping字段,则只能存储double类型的数据。
Mapping不仅是告诉ES,哪个字段是哪种类型。还能告诉ES如何来索引数据,以及数据是否被索引到等。
Search Moudle,搜索模块,支持搜索的一些常用操作
Index Moudle,索引模块,支持索引的一些常用操作
Disvcovery,主要是负责集群的master节点发现。比如某个节点突然离开或进来的情况,进行一个分片重新分片等。这里有个发现机制。
发现机制默认的实现方式是单播和多播的形式,即Zen,同时也支持点对点的实现。另外一种是以插件的形式,即EC2。
Scripting,即脚本语言。   
Transport,代表ES内部节点,代表跟集群的客户端交互。包括 Thrift、Memcached、Http等协议
RESTful Style API,通过RESTful方式来实现API编程。
3rd plugins,代表第三方插件。
Java(Netty),是开发框架。
JMX,是监控。

2 es节点

一个节点是一个ES的实例,在服务器上启动ES之后,就拥有了一个节点,如果在另一个服务器上启动ES,这就是另一个节点。甚至可以在一台服务器上启动多个ES进程,在一台服务器上拥有多个节点。多个节点可以加入同一个集群。
当ElasticSearch的节点启动后,它会利用多播(multicast)(或者单播,如果用户更改了配置)寻找集群中的其它节点,并与之建立连接。
节点主要有3种类型,第一种类型是client_node,主要是起到请求分发的作用,类似路由。第二种类型是master_node,是主的节点,所有的新增,删除,数据分片都是由主节点操作(elasticsearch底层是没有更新数据操作的,上层对外提供的更新实际上是删除了再新增),当然也能承担搜索操作。第三种类型是date_node,该类型的节点只能做搜索操作,具体会分配到哪个date_node,就是由client_node决定,而data_node的数据都是从master_node同步过来的

3 es分片

一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。
为了解决这个问题,ES提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
分片之所以重要,主要有两方面的原因:
1、允许你水平分割/扩展你的内容容量
允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由ES管理的,对于作为用户的你来说,这些都是透明的。
2、在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了。这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,ES允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,主要有两方面的原因:
(1)在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。
(2)扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行
总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制数量,但是不能改变分片的数量。
默认情况下,ES中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。一个索引的多个分片可以存放在集群中的一台主机上,也可以存放在多台主机上,这取决于你的集群机器数量。主分片和复制分片的具体位置是由ES内在的策略所决定的。

4 es中的索引原理

(1)传统的关系型数据库
二叉树查找效率是logN,同时插入新的节点不必移动全部节点,所以用树型结构存储索引,能同时兼顾插入和查询的性能。因此在这个基础上,再结合磁盘的读取特性(顺序读/随机读),传统关系型数据库采用了B-Tree/B+Tree这样的数据结构做索引
(2)ES
采用倒排索引
Term(单词):一段文本经过分析器分析以后就会输出一串单词,这一个一个的就叫做Term
Term Dictionary(单词字典):顾名思义,它里面维护的是Term,可以理解为Term的集合
Term Index(单词索引):为了更快的找到某个单词,我们为单词建立索引
Posting List(倒排列表):倒排列表记录了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。(PS:实际的倒排列表中并不只是存了文档ID这么简单,还有一些其它的信息,比如:词频(Term出现的次数)、偏移量(offset)等。
(PS:如果类比现代汉语词典的话,那么Term就相当于词语,Term Dictionary相当于汉语词典本身,Term Index相当于词典的目录索引)
我们知道,每个文档都有一个ID,如果插入的时候没有指定的话,Elasticsearch会自动生成一个
Elasticsearch分别为每个字段都建立了一个倒排索引。比如,“张三”、“北京市”、22 这些都是Term,而[1,3]就是Posting List。Posting list就是一个数组,存储了所有符合某个Term的文档ID。
只要知道文档ID,就能快速找到文档。为了通过我们给定的关键词快速找到这个Term就要建索引了,为Terms建立索引,最好的就是B-Tree索引(MySQL就是B树索引最好的例子)。
我们查找Term的过程跟在MyISAM中记录ID的过程大致是一样的
MyISAM中,索引和数据是分开,通过索引可以找到记录的地址,进而可以找到这条记录
在倒排索引中,通过Term索引可以找到Term在Term Dictionary中的位置,进而找到Posting List,有了倒排列表就可以根据ID找到文档了
(PS:可以这样理解,类比MyISAM的话,Term Index相当于索引文件,Term Dictionary相当于数据文件)
(PS:其实,前面我们分了三步,我们可以把Term Index和Term Dictionary看成一步,就是找Term。因此,可以这样理解倒排索引:通过单词找到对应的倒排列表,根据倒排列表中的倒排项进而可以找到文档记录)

二、 linux启动es

修改config/elasticsearch.yml 中network.host: 127.0.0.1 中的ip为指定ip或0.0.0.0
(ip与network.host: 127.0.0.1 中的ip中间有空格!!!)
创建新用户:
useradd es
为es用户添加添加文件夹及授予权限:在root权限下
chown -R es:es /elasticsearch-6.8.0
chmod -R 777 /elasticsearch-6.8.0
注:在文件夹下: chmod -X * -R
切换到es用户下:
su es
切换到存放es的目录下:
./elasticsearch -d
screen ./elasticsearch

测试es启动是否成功:
ps aux | grep elasticsearch
curl -XGET ip:port
curl -u elastic:passwoed -X GET ip:port
curl -u elastic -XPUT ‘http://ip:port/_xpack/security/user/elastic/_password?pretty’ -H ‘Content-Type: application/json’ -d’
{
“password” : “123456”
}

注:
修改config/elasticsearch.yml 中network.host: 127.0.0.1 中的ip
修改ip后如出现以下情况导致无法启动:
ERROR: [2] bootstrap checks failed
[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
[2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
[1]: max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536]
解决办法:
编辑 /etc/security/limits.conf,追加以下内容;

  • soft nofile 65536
  • hard nofile 65536
    此文件修改后需要重新登录用户,才会生效

ERROR: [2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决办法:
编辑 /etc/sysctl.conf,追加以下内容:
vm.max_map_count=655360
保存后,执行:sysctl -p 重新启动

三、kibana方式操作ES

1 查询

以下test为索引名, http://127.0.0.1:5601/

# 集群相关
# 查询集群健康状态
GET _cluster/health
# 查询所有节点
GET _cat/nodes
# 查询索引及分片的分布
GET _cat/shards
# 查询所有插件
GET _cat/plugins

# 索引相关查询
查询所有索引及容量
GET _cat/indices
查询索引映射结构
GET _index/_mapping
查询所有索引映射结构  
GET _all
查询所有的相同前缀索引
GET base_*/_search
查询所有索引模板
GET _template
# 查看索引mapping结构
GET lawzllaw/_mapping
# 查看索引设置
GET lawzllaw/_settings
# 查看索引内数量
GET lawzllaw/_count


# 查看数据索引方式  #  "field": "title.keyword",
GET test/_analyze
{
  "field": "title", 
  "text": "公共租赁住房管理办法"
}
# 查询所有数据 可加入"from":0, "size":1限制数量
GET test/_search
{
  "query": {
    "match_all": {}
  }
}
# elasticsearch 查询(match和term)
# match 会进行分词,返回所有被分词后相关文档
GET test/_search
{
  "query": {
    "match": {
      "title": {
        "query": "公共节能条例"
      }
    }
  }
}

# match_phrase也会进行分词,但是返回精确匹配 结果必须包含所有分词
# slop参数:查询词条能够相隔多远时仍然将文档视为匹配即有有几个词可以不匹配
GET test/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "公共节能条例",
        "slop" : 1
      }
    }
  }
}


# multi_match 两个字段进行匹配
GET test/_search
{
  "query": {
    "multi_match": {
        "query" : "宝马",
        "fields" : ["title", "content"]
    }
  }
}
# 评分:完全匹配的文档占的评分比较高,使用best_fields
        越多字段匹配的文档评分越高,就要使用most_fields
        词条的分词词汇是分配到不同字段中的,那么就使用cross_fields
GET test/_search
{
  "query": {
    "multi_match": {
      "query": "我的宝马发动机多少",
      "type": "best_fields",
      "fields": [
        "tag",
        "content"
      ],
      "tie_breaker": 0.3
    }
  }
}


# term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
# 使用term要确定的是这个字段是否“被分析”(analyzed),默认的字符串是被分析的。
# 被分析可使用title.keyword
GET test/_search
{
  "query": {
    "term": {
      "title": "汽车保养"
    }
  }
}
# bool联合查询: must,should,must_not(多条件查询)
"""
must: 文档必须完全匹配条件
should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should
must_not: 文档必须不匹配条件
"""

GET test/_search
{
  "query": {
    "bool": {
      "must": {
        "term": {
          "content": "宝马"
        }
      },
      "must_not": {
        "term": {
          "title": "宝马"
        }
      }
    }
  }
}
GET test/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "租赁"
          }
        },
        {
          "match": {
            "timeliness": "1"
          }
        }
      ]
    }
  }
}

# should (or查询):
GET test/_search
{
    "query": {
        "bool": {
            "must": {
                "bool" : { 
                    "should": [
                        { "match_phrase": { "unitname": "北京市" }},
                        { "match_phrase": { "unitname": "上海市" }} ] 
                }
            }

        }
    }
}

# 范围查询并进行排序,用range:gte和lte指定范围,再用sort:asc和desc排序
GET base_company_event*/_search
{
  "query": {
    "range": {
      "p_stock2201_f033n": {
        "gte": 1000,
        "lte": 2300
      }
    }
  },
  "sort": [
    {
      "p_stock2201_f033n": {
        "order": "asc"
      }
    }
  ]
}
# 聚合查询1)固定用到size和aggs,field后面输入属性,from和to后边输入要分段的范围
GET test/_search
{
  "size": 20,
  "aggs": {
    "p_stock2201_f033n": {
      "range": {
        "field": "p_stock2201_f033n",
        "ranges": [
          {
            "from": 1000,
            "to": 2000
          },{
            "from": 2000,
            "to": 3000
          },{
            "from": 3000,
            "to": 4000
          }
        ]
      }
    }
  }


# 聚合查询 group
GET test/_search
{
  "size": 0,
  "aggs": {
    "group_by_place": {
      "terms": {
        "field": "timeliness.keyword",
        "size": 10
      }
    }
  }
}

# 聚合查询 group
GET test/_search
{
  "size": 0,
  "aggs": {
    "group_by_place": {
      "terms": {
        "field": "timeliness.keyword",
        "size": 10
      }
    }
  }
}

2 修改

POST test/doc/599966     # POST index/doc/id
{
    "timeliness":2
}

#  对索引根据条件修改字段值
POST test/_update_by_query
{
  "query": {
    "match": {
      "effectlevel": "司法性文件"
    }
  },
  "script": {
    "source": "ctx._source['effectlevel'] = '23'"

  }
}

3 删除

# 删除索引
DELETE /test

# 对索引进行条件删除
POST /test/_delete_by_query
{
  "query": {
    "match": {
      "publishtime": "2021-04-05"      
    }
  }
}

# es清空索引数据
POST test/_delete_by_query
{
  "query": {"match_all": {}}
}

4 新增

# 创建索引:
PUT /people
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "man": {
      "dynamic": "strict",
      "properties": {
        "name": {
          "type": "text"
        },
        "age": {
          "type": "integer"
        },
        "birthday": {
          "type": "date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        },
        "address":{
          "dynamic": "true",
          "type": "object"
        }
      }
    }
  }
}

四、Python操作es

# 引入es
from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulk

body = {}
# 连接es
ES_INDEX = "lawzllaw"
es = Elasticsearch()

# 创建index
result = es.indices.create(index="lawzllaw", ignore=400)
# 删除 Index
result = es.indices.delete(index='news', ignore=[400, 404])

# 插入数据
result = es.create(index='lawzllaw', doc_type='', id=1, body=body)
result = es.index(index='lawzllaw', doc_type='', body=body)
# 更新数据
result = es.update(index='lawzllaw', doc_type='', body=body, id=1)
result = es.index(index='lawzllaw', doc_type='', body=body, id=1)
result es.update_by_query(index=ES_INDEX, body=body, doc_type="doc")
# 删除数据
result = es.delete(index='lawzllaw', doc_type='', id=1)
result = es.delete_by_query(index='lawzllaw', body=body, doc_type='')
# 查询:两种get and search
result = es.get(index="lawzllaw", doc_type="test-type", id=01)
result = es.search(index='lawzllaw', body=body)

# 批量写入、删除、更新
doc = [
    {'index': {'_index': 'lawzllaw', '_type': 'typeName', '_id': 'idValue'}}
    {'name': 'jack', 'sex': 'male', 'age': 10 }
    {'delete': {'_index': 'lawzllaw', '_type': 'typeName', '_id': 'idValue'}}
    {"create": {'_index' : 'lawzllaw', "_type" : 'typeName', '_id': 'idValue'}}
    {'name': 'lucy', 'sex': 'female', 'age': 20 }
    {'update': {'_index': 'lawzllaw', '_type': 'typeName', '_id': 'idValue'}}
    {'doc': {'age': '100'}}
 ]

es.bulk(index='lawzllaw', doc_type='typeName', body=doc)

#批量更新也可以采用如下的方式进行json拼装,最后写入
 for line in list:
    action = {
        "_index": self.lawzllaw,
        "_type": self.index_type,
        "_id": i, #_id 也可以默认生成,不赋值
        "_source": {
            "date": line['date'],
            "source": line['source'].decode('utf8'),
            "link": line['link'],
            "keyword": line['keyword'].decode('utf8'),
            "title": line['title'].decode('utf8')}
    }
    i += 1
    ACTIONS.append(action)
success, _ = bulk(self.es, ACTIONS, index=self.lawzllaw, raise_on_error=True)

五、curl操作es

六、 ES 漏洞介绍

Elasticsearch未授权访问漏洞

1设置x-pack用户登录授权,配置ES

$ cat elasticsearch.yml
cluster.name: eryajf-search
node.name: es-node1
path.data: /data/elasticsearch7/data
path.logs: /data/elasticsearch7/log
network.host: 0.0.0.0
http.port: 9200
xpack.security.enabled: true # 这条配置表示开启xpack认证机制
xpack.security.transport.ssl.enabled: true
cluster.initial_master_nodes: ["es-node1"]
$ cat elasticsearch.yml
cluster.name: eryajf-search
node.name: es-node1
path.data: /data/elasticsearch7/data
path.logs: /data/elasticsearch7/log
network.host: 0.0.0.0
http.port: 9200
xpack.security.enabled: true # 这条配置表示开启xpack认证机制
xpack.security.transport.ssl.enabled: true
cluster.initial_master_nodes: ["es-node1"]

参数说明:
xpack.security.enabled:表示开启xpack认证机制。
xpack.security.transport.ssl.enabled:这条如果不配,es将起不来,会报如下错误:
Transport SSL must be enabled if security is enabled on a [basic] license. Please set [xpack.security.transport.ssl.enabled] to [true] or disable security by setting [xpack.security.enabled] to [false]

2 设置用户名密码:
启动成功后,我们执行下面的命令,进行密码设置。
./bin/x-pack/setup-passwords interactive
接着,根据提示,为三个内置账号设置密码。
也可以有系统自动生成密码。自动生成密码,执行下面的命令即可。
./bin/x-pack/setup-passwords auto

三个内置账号的角色权限解释如下:
elastic 账号:拥有 superuser 角色,是内置的超级用户。
kibana 账号:拥有 kibana_system 角色,用户 kibana 用来连接 elasticsearch 并与之通信。Kibana 服务器以该用户身份提交请求以访问集群监视 API 和 .kibana 索引。不能访问 index。
logstash_system 账号:拥有 logstash_system 角色。用户 Logstash 在 Elasticsearch 中存储监控信息时使用。
另外也可以在 kibana 中通过 DSL 语句设置密码。
POST _xpack/security/user/kibana/_password
{
“password”: “elastic”
}

3 密码修改后,我们还需要修改 elasticsearch 的配置文件。
http.cors.enabled: true
http.cors.allow-origin: ‘*’
http.cors.allow-headers: Authorization,X-Requested-With,Content-Length,Content-Type

编辑完 elasticsearch/config/elasticsearch.yml 文件后,保存重启 es 即可。
head 访问方式,http://localhost:9100/?base_uri=http://localhost:9200&auth_user=elastic&auth_password=password。

注:
修改max_result_window
7.10.0 版本之前版本:
curl -H “Content-Type: application/json” -XPUT ip:port/test/_settings -d ‘{ “index.max_result_window” :“200000”}’
7.10.0 版本:需要两步才能实现:
1 curl -H “Content-Type: application/json” -XPUT ip:port/test/_settings -d ‘{ “index.max_result_window” :“200000”}’
2 curl -H -XGET ip:port/lawzllaw/_search -d ‘{ “size”: 0, “track_total_hits”: true, “query”: { “match_all”: {}}’

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>