Elasticsearch
1、Elasticsearch概述
1.1 Elasticsearch介绍
1.1.1 Elasticsearch概念
Elasticsearch是一个基于Lucene的实时分布式搜索和分析引擎。
Elasticsearch是基于RESTFUL接口:
普通的请求 get?a=1
REST请求 get/a/1
Lucene是单台机器的底层的API。
Solr是基于Lucene封装的产品,在一边搜索一边建立索引时会产生很大的IO阻塞。
1.1.2 Elasticsearch对比Solr
1. 接口:
Solr类似于webservice接口
REST风格的访问接口
2. 分布式存储
SolrCloud Solr4.x才支持
Elasticsearch是为分布式而生的
3. 支持的格式
Solr: xml, json
Elaticsearch: json
Elasticsearch和Solr的百度指数
1.2 REST介绍
1.2.1 REST概念
REST = Representational State Transfer, 是一种软件架构风格,而不是标准,只是提供一组设计原则和约束条件。
1.2.2 REST操作
GET:获取对象的当前状态
PUT:改变对象的状态
POST:创建对象
DELETE:删除对象
HEAD:获取头信息
1.3 Elasticsearch内置的REST接口
1.4 Elasticsearch和RDB对比
1.5 Elasticsearch安装配置
1.Java版本要求:最低1.7
下载地址:https://www.elastic.co/downloads/
问题讨论可以登录https://discuss.elastic.co/c论坛查找
2.配置vim config/elasticsearch.yml
network.host: 192.168.0.1 (默认127.0.0.1)
cluster.name: yaoCluster
node.name: node1
加上防脑裂配置
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping_timeout: 120s
client.transport.ping_timeout: 60s
discovery.zen.ping.unicast.hosts:["192.168.39.4","192.168.39.5","192.168.39.6"]
在局域网中,集群名字一样,节点名字不一样,它会自动组群。
3.启动
elasticsearch -d(后台运行)
4.安装后访问
(单机)http://192.168.39.4:9200
(集群)http://192.168.39.4:9200/_cluster/health?pretty
2、Elasticsearch插件和使用
2.1 安装elasticsearch-servicewrapper插件
1.下载elasticsearch-servicewrapper
git clone https://github.com/elasticsearch/elasticsearch-servicewrapper,然后将目录下的service目录拷贝至ES_HOME/bin目录下。
2.简单配置jvm的内存
修改ES_HOME/bin/service/elasticsearch.conf,
set.default.ES_HEAP_SIZE=1024,该值可根据机器的配置自定义。
3.安装启动服务
执行命令:ES_HOME/bin/service/elasticsearch install
这里需要添加一下执行权限
chmod u+x elasticsearch
4.启动/停止/重启服务
ES_HOME/bin/service/elasticsearch start/stop/restart
2.2 安装Kibana插件
解压安装,修改配置文件vi config/kibana.yml的elasticsearch.url属性即可。
elasticsearch.url: http://192.168.39.4:9200
2.3 安装Marvel插件
Step 1: Install Marvel into Elasticsearch:
bin/plugin install license
bin/plugin install marvel-agent
Step 2: Install Marvel into Kibana:
bin/kibana plugin --install elasticsearch/marvel/latest
Step 3: Start Elasticsearch and Kibana:
bin/elasticsearch
bin/kibana
Step 4: Navigate to http://localhost:5601/app/marvel
2.4 Elasticsearch使用
curl命令:
curl是利用URL语法在命令行方式下工作的开源文件传输工具。
curl -x
-x指定http请求的方法,HEAD GET POST PUT DELETE
-d指定传输的数据
curl建立索引库:
curl -XPUT 'http://localhost:9200/index_name/'
PUT/POST都行
PUT是幂等方法(幂等是指不管进行多少次操作,结果都一样),用于更新
POST用于新增
curl创建索引:
curl -XPOST http://localhost:9200/pet/cat/1 -d '{
"first_name" : “little",
"last_name" : “Mona",
"age" : 0.5,
"about" : “It love eat",
"interests": [ “eat", “sleep" ]
}'
创建索引注意事项:
索引库名称必须要全部小写,不能以下划线开头,也不能包含逗号。
如果没有明确指定索引数据的ID,那么es会自动生成一个随机的ID,需要使用POST参数
curl –XPOST http://localhost:9200/pet/dog/1 -d '{"first_name" :"John"}'
如果想要确定创建的都是全新的内容
1:使用自增ID
2:在url后面添加参数
curl -XPUT http://localhost:9200/pet/dog/2?op_type=create -d
'{"name":“robin","age":1}'
curl -XPUT http://localhost:9200/pet/dog/2/_create -d
'{"name":“tony","age":1}'
GET查询:
根据id查询
curl -XGET http://localhost:9200/pet/dog/1?pretty
在任意的查询字符串中添加pretty参数,es可以得到易于识别的json结果。
curl后添加-i 参数,这样就能得到反馈头文件。
curl -i 'http://192.168.1.170:9200/pet/dog/1?pretty'
检索文档中的一部分,如果只需要显示指定字段,
curl -XGET http://localhost:9200/pet/dog/1?_source=name,age
如果只需要source的数据
curl –XGET http://localhost:9200/pet/dog/1/_source
查询所有
curl -XGET http://localhost:9200/pet/dog/_search
可以再返回的hits 中发现录入的文档。搜索会默认返回最前的10个数值。
根据条件进行查询
curl -XGET http://localhost:9200/pet/dog/_search?q=last_name:Jone
DSL查询:
DSL = Domain Specific Language
curl -XGET http://localhost:9200/pet/dog/_search -d
'{"query":
{"match":
{"last_name":“Jone"}
}
}'
MGET查询:
使用mget API获取多个文档
curl -XGET http://localhost:9200/_mget?pretty -d
'{"docs":[{"_index":“pet","_type":“dog","_id":2,"_source":"name"},{"_index":"website","_type":"blog","_id":2}]}'
如果需要的文档在同一个_index或者同一个_type中,可以在URL中指定一个默认的/_index或者/_index/_type
curl -XGET http://localhost:9200/pet/dog/_mget?pretty -d
'{"docs":[{"_id":1},{"_type":"blog","_id":2}]}'
如果所有的文档拥有相同的_index 以及_type,直接在请求中添加ids的数组即可
curl -XGET http://localhost:9200/pet/dog/_mget?pretty -d
'{"ids":["1","2"]}'
注意:如果请求的某一个文档不存在,不会影响其他文档的获取结果。HTTP返回状态码依然是200,这是因为mget这个请求本身已经成功完成。要确定独立的文档是否被成功找到,需要检查found标识。
Head的使用:
如果只想检查一下文档是否存在,可以使用HEAD来替代GET方法,这样就只会返回HTTP头文件
curl -i -XHEAD http://localhost:9200/pet/dog/1
Elasticsearch更新:
ES可以使用PUT或者POST对文档进行更新,如果指定ID的文档已经存在,则执行更新操作。
注意:执行更新操作的时候:
1.ES首先将旧的文档标记为删除状态
2.然后添加新文档
3.旧文档不会立即消失,但无法访问
4.ES会在继续添加更多数据的时候在后台清理已经标记为删除状态的文档
Elasticsearch局部更新
局部更新,可以添加新字段或者更新已有字段(必须使用POST)
curl –XPOST http://localhost:9200/pet/dog/1/_update -d
'{"doc":{“name":“Haha",“color":“Yellow"}}'
Elasticsearch删除
curl -XDELETE http://localhost:9200/pet/dog/4/
如果文档存在,es会返回200 ok的状态码,found属性值为true,_version属性的值+1
如果文档不存在,found属性值为false,但是_version属性的值依然会+1,这个就是内部管理的一部分,它保证了在多个节点间的不同操作的顺序都被正确标记了。
注意:删除一个文档也不会立即生效,它只是被标记成已删除。Elasticsearch将会在之后添加更多索引的时候才会在后台进行删除内容的清理。
通过查询API删除指定索引库下指定类型下的数据
curl -XDELETE 'http://localhost:9200/pet/dog/_query?q=name:robin’
curl -XDELETE 'http://localhost:9200/pet/dog/_query' -d ‘{
"query" : {
"term" : {“name" : “robin" }
}
}'
通过查询API删除指定索引库下多种类型的数据
curl -XDELETE
'http://localhost:9200/pet/dog,cat/_query?q=name:Mona‘
删除所有索引库中的匹配的数据
curl -XDELETE 'http://localhost:9200/_all/_query?q=tag:SB'
Elasticsearch版本控制
普通关系型数据库使用的是(悲观并发控制(PCC))
当在读取一个数据前先锁定这一行,然后确保只有读取到数据的这个线程可以修改这一行数据。
ES使用的是(乐观并发控制(OCC))
ES不会阻止某一数据的访问,然而,如果基础数据在读取和写入的间隔中发生了变化,更新就会失败,这时候就由程序来决定如何处理这个冲突。它可以重新读取新数据来进行更新,又或者将这一情况直接反馈给用户。
ES版本控制(使用ES内部版本号)
1: 首先得到需要修改的文档,获取版本(_version)号
curl -XGEThttp://localhost:9200/elasticsearch/emp/1
2: 在执行更新操作的时候把版本号传过去
curl -XPUT http://localhost:9200/elasticsearch/emp/1?version=1-d
'{"name":“zs","age":25}'(覆盖)
curl -XPOST
http://localhost:9200/elasticsearch/emp/1/_update?version=1-d
‘{“doc”:{“name”:”Haha",”color":”Yellow"}}'(部分更新)
3:如果传递的版本号和待更新的文档的版本号不一致,则会更新失败。
ES版本控制(使用外部版本号)
如果数据库已经存在了版本号,或者是可以代表版本的时间戳。这时就可以在es的查询url后面添加version_type=external来使用这些号码。
注意:版本号码必须要是大于0小于9223372036854775807(Java中long的最大正值)的整数。
es在处理外部版本号的时候,它不再检查_version是否与请求中指定的数值是否相等,而是检查当前的_version是否比指定的数值小,如果小,则请求成功。
curl -XPUT
‘http://localhost:9200/pet/dog/20?version=10&version_type=external’ -d‘{“name”: “robin"}'
注意:此处url前后的引号不能省略,否则执行的时候会报错。
Elasticsearch的分页
与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
注意:不要一次请求过多或者页码过大的结果,这么会对服务器造成很大的压力。因为它们会在返回前排序。一个请求会经过多个分片。每个分片都会生成自己的排序结果。然后再进行集中整理,以确保最终结果的正确性。
timed_out
curl -XGET http://localhost:9200/_search?timeout=10ms
ES会在10ms之内返回查询内容
注意:timeout并不会终止查询,它只是会在指定的时间内返回当时已经查询到的数据,然后关闭连接。在后台,其他的查询可能会依旧继续,尽管查询结果已经被返回了。
3、Elasticsearch概念和理论
3.1 cluster
集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。ES的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看ES集群,在逻辑上是个整体,与任何一个节点的通信和与整个ES集群通信是等价的。
主节点的职责是负责管理集群状态,包括管理分片的状态和副本的状态,以及节点的发现和删除。
只需要在同一个网段之内启动多个ES节点,就可以自动组成一个集群。
默认情况下ES会自动发现同一网段内的节点,自动组成集群。
集群状态查看,http://192.168.39.4:9200/_cluster/health?pretty
3.2 shards
索引分片,ES可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。
可以在创建索引库的时候指定
curl -XPUT 'localhost:9200/test1/'-d'{"settings":{"number_of_shards":3}}'
默认是一个索引库有5个分片,即number_of_shards: 5
3.3 replicas
索引副本,ES可以给索引设置副本,副本的作用:
一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高ES的查询效率,ES会自动对搜索请求进行负载均衡。
可以在创建索引库的时候指定
curl -XPUT 'localhost:9200/test2/'
-d'{"settings":{"number_of_replicas":2}}'
默认是一个分片有1个副本,即number_of_replicas: 1
3.4 recovery
数据恢复或叫数据重新分布,ES在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。
3.5 gateway
ES索引的持久化存储方式,ES默认是先把索引存放到内存中,当内存满了时再持久化到硬盘。当这个ES集群关闭再重新启动时就会从gateway中读取索引数据。ES支持多种类型的gateway,有本地文件系统(默认),分布式文件系统,Hadoop的HDFS和amazon的s3云存储服务。
如果需要将数存储到hadoop的hdfs需要先安装插件
elasticsearch/elasticsearch-hadoop,然后再elasticsearch.yml配置
gateway:
hdfs:
uri: hdfs://localhost:9000
3.6 discovery.zen
ES的自动发现节点机制,ES是一个基于p2p的系统,它先通过广播寻找存在的节点,再通过多播(组播)协议来进行节点之间的通信,同时也支持点对点的交互。
如果是不同网段的节点如何组成ES集群
禁用自动发现机制
discovery.zen.ping.multicast.enabled: false
设置新节点被启动时能够发现的主节点列表
discovery.zen.ping.unicast.hosts:["192.168.39.4","192.168.39.5","192.168.39.6"]
3.7 Transport
ES内部节点或集群与客户端的交互方式,默认内部是使用tcp协议进行交互,同时它支持http协议(json格式)、thrift、servlet、memcached、zeroMQ等的传输协议(通过插件方式集成)。
3.8 settings
settings修改索引库默认配置
例如:分片数量,副本数量
查看:curl -XGEThttp://localhost:9200/index/_settings?pretty
curl -XPUT 'localhost:9200/index/'
-d'{"settings":{"number_of_shards":3,"number_of_replicas":2}}'
3.9 mappings
对索引库中索引的字段名称及其数据类型进行定义,类似于关系数据库中表建立时要定义字段名及其数据类型那样,它可以动态添加字段。一般不需要指定mapping,因为ES会自动根据数据格式定义它的类型,如果需要对某些字段添加特殊属性(如:定义使用其它分词器、是否分词、是否存储等),就必须手动添加mapping。
查询索引库的mapping信息
curl -XGET http://localhost:9200/index/emp/_mapping?pretty
mappings修改字段相关属性
例如:字段类型,使用哪种分词工具
操作不存在的索引
curl -XPUT
http://192.168.79.131:9200/shb02-d'{"mappings":{"emp":{"properties":{"name":{"type":"string","indexAnalyzer":"ik","searchAnalyzer":"ik"}}}}}'
操作已存在的索引
curl –XPOST
http://192.168.79.131:9200/crxy/shb02/_mapping-d'{"properties":{"name":{"type":"string","indexAnalyzer":"ik","searchAnalyzer":"ik"}}}'
4、Elasticsearch集成中文分词器
4.1下载编译
从地址https://github.com/medcl/elasticsearch-analysis-ik下载
elasticsearch中文分词器
这里默认的是master的 但是master的项目需要用gradle编译,这里选择1.8.0版本。而且从下面的介绍可以知道1.8.0正好对应elasticsearch的2.2.0版本
下载后的压缩包解压后进去发现是pom工程
分别执行如下命令:
mvn clean
mvn compile
mvn package
4.2 安装插件
前面编译了插件以后会在target/releases目录下出现一个zip包。
在安装好的elasticsearch中在plugins目录下新建ik目录,将此zip包拷贝到ik目录下。
将权限修改为elasticsearch启动用户的权限,通过unzip命令解压缩,例如在plugins/ik目录下执行unzip elasticsearch-analysis-ik-1.8.0.zip
解压后查看 得到解压后的结果。
4.3 安装完成
每台机器都这样操作,重新启动elasticsearch集群。
5、Elasticsearch的查询分片及其优化方案
5.1 Elasticsearch分片查询
默认是randomize across shards即随机选取,表示随机的从分片中取数据。
_local:指查询操作会优先在本地节点有的分片中查询,没有的话再在其它节点查询。
_primary:指查询只在主分片中查询。
_primary_first:指查询会先在主分片中查询,如果主分片找不到(挂了),就会在副本中查询。
_only_node:指在指定id的节点里面进行查询,如果该节点只有查询索引的部分分片,就只在这部分分片中查找,所以查询结果可能不完整。如_only_node:123在节点id为123的节点中查询。
_prefer_node:nodeid 优先在指定的节点上执行查询。
_shards:0 ,1,2,3,4:查询指定分片的数据。
自定义:_only_nodes:根据多个节点进行查询。
Es查询的时候默认是随机从一些分片中查询数据,可以通过配置让es从某些分片中查询数据
1:_local
指查询操作会优先在本地节点有的分片中查询,没有的话再在其它节点查询。
2:_primary:指查询只在主分片中查询
3:_primary_first:指查询会先在主分片中查询,如果主分片找不到(挂了),就会在副本中查询。
4:_only_node:指在指定id的节点里面进行查询,如果该节点只有要查询索引的部分分片,就只在这部分分片中查找,所以查询结果可能不完整。如_only_node:123在节点id为123的节点中查询。
5:_prefer_node:nodeid 优先在指定的节点上执行查询
6:Custom (string) value:随机指定一个值就可以。
7:_shards:0,1,2,3,4:查询指定分片的数据
8:自定义:_only_nodes:nodeid1,nodeid2,nodeid3根据多个节点进行查询
Es默认没有提供这种查询方式,所以就只能修改源码了。
5.2 Elasticsearch脑裂问题
所谓脑裂问题(类似于精神分裂),就是同一个集群中的不同节点,对于集群的状态有了不一样的理解。
http://blog.csdn.net/cnweike/article/details/39083089
discovery.zen.minimum_master_nodes
用于控制选举行为发生的最小集群节点数量。推荐设为大于1的数值,因为只有在2个以上节点的集群中,主节点才是有意义的。
正常情况下,集群中的所有的节点,应该对集群中master的选择是一致的,这样获得的状态信息也应该是一致的,不一致的状态信息,说明不同的节点对master节点的选择出现了异常——脑裂问题。这样的脑裂状态直接让节点失去了集群的正确状态,导致集群不能正常工作。
5.1.1 导致脑裂原因
1、网络:由于是内网通信,网络通信问题造成某些节点认为master死掉,而另选master的可能性较小。
2、节点负载:由于master节点与data节点都是混合在一起的,所以当工作节点的负载较大时,导致对应的ES实例停止响应,而这台服务器如果正充当着master节点的身份,那么一部分节点就会认为这个master节点失效了,故重新选举新的节点,这时就出现了脑裂;同时由于data节点上ES进程占用的内存较大,较大规模的内存回收操作也能造成ES进程失去响应。
5.1.2 解决脑裂方法
主节点
node.master:true
node.data:false
从节点
node.master:false
node.data:true
全部节点
discovery.zen.ping.multicast.enabled:false
discovery.zen.ping.unicast.hosts:[“master”,”slave1”,”slave2"]
5.3 Elasticsearch优化
1. 调大系统的"最大打开文件数",建议32K甚至是64K
ulimit -a (查看)
ulimit -n 32000(设置)
2. 修改配置文件调整ES的JVM内存大小
(1)修改bin/elasticsearch.in.sh中ES_MIN_MEM和ES_MAX_MEM的大小,建议设置一样大,避免频繁的分配内存,根据服务器内存大小,一般分配60%左右(默认256M)
(2)如果使用searchwrapper插件启动ES的话则修改
bin/service/elasticsearch.conf(默认1024M)
3. 设置mlockall来锁定进程的物理内存地址
避免交换(swapped)来提高性能
修改文件conf/elasticsearch.yml
boostrap.mlockall: true
4. 分片多的话,可以提升建立索引的能力,5-20个比较合适。
如果分片数过少或过多,都会导致检索比较慢。分片数过多会导致检索时打开比较多的文件,另外也会导致多台服务器之间通讯。而分片数过少会导至单个分片索引过大,所以检索速度慢。建议单个分片最多存储20G左右的索引数据,所以,分片数量=数据总量/20G
5. 副本多的话,可以提升搜索的能力,但是如果设置很多副本的话也会对服务器造成额外的压力,因为需要同步数据。所以建议设置2-3个即可。
6. 要定时对索引进行优化,不然segment越多,查询的性能就越差
索引量不是很大的话情况下可以将segment设置为1
curl -XPOST
'http://localhost:9200/index/_optimize?max_num_segments=1'
7. 删除文档:在Lucene中删除文档,数据不会马上在硬盘上除去,而是在lucene索引中产生一个.del的文件,而在检索过程中这部分数据也会参与检索,lucene在检索过程会判断是否删除了,如果删除了在过滤掉。这样也会降低检索效率。所以可以执行清除删除文档
curl -XPOST
'http://localhost:9200/elasticsearch/_optimize?only_expunge_deletes=true'
8. 如果在项目开始的时候需要批量入库大量数据的话,建议将副本数设置为0
因为es在索引数据的时候,如果有副本存在,数据也会马上同步到副本中,这样会对es增加压力。待索引完成后将副本按需要改回来。这样可以提高索引效率。
9. 去掉mapping中_all域,Index中默认会有_all的域,(相当于solr配置文件中的拷贝字段text),这个会给查询带来方便,但是会增加索引时间和索引尺寸:
"_all":{"enabled":"false"}
10. log输出的水平默认为trace,即查询超过500ms即为慢查询,就要打印日志,造成cpu和mem,io负载很高。把log输出水平改为info,可以减轻服务器的压力。
修改ES_HOME/conf/logging.yml文件
或者修改ES_HOME/conf/elasticsearch.yml