一、核心概念
1、cluster
代表一个集群,集群中有多个节点,其中一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来书的,es的一个重要概念是去中心化,字面理解是无中心节点,这是对于外部来说,从外部来看es集群,在逻辑上是一个整体,你与任何一个节点通信与整个es集群通信是等价的
但es内部有主节点,重要负责管理集群状态,包括管理分片的状态和副本的状态,以及节点的发现和删除
只需要在同一个网段内启动多个es节点,就可组成一个集群
默认情况下es会自动发现同一网段内的节点,并自动组成集群
集群状态查看
http://192.168.1.191:9200/_cluster/health?pretty
2、shards
代表索引分片,es可以把一个完整的索引分成多个分片(默认5个),这样的好处在于可以把一个大的索引拆分成多个,分布到不同的节点上,构成分布式搜索,分片的数量只能在索引创建前指定,并且索引创建后不能更改
可以在创建索引库的时候指定
curl -XPUT 'localhost:9200/test1/' -d'{"settings":{"number_of_shards":3}}'
3、replicas
代表索引副本,es可以给索引设置副本,副本的作用是提高系统的容错性,当某个节点分片损坏或丢失时,可以从副本中恢复,可提高es的查询效率,es会自动对搜索请求进行负载均衡
可在创建索引库的时候指定(注区别shards,replicas可修改数量)
curl -XPUT 'localhost:9200/test2/' -d'{"settings":{"number_of_replicas":2}}'
默认有一个副本,总共两个,默认状态下number_of_replicas=1
4、recovery
代表数据恢复或重新分布,es在有节点加入或退出时,根据机器的负载对索引分片进行重写新分配,挂掉的节点重新启动时也会进行数据恢复
5、gateway
代表es索引的持久化存储方式,es默认的是先把索引存储到内存中,当内存满的时候,持久化到磁盘,当这个es集群重启时,就会从gateway中读取索引数据。
es支持多种类型的gateway,有本地的文件系统(默认),分布式文件系统,hadoop的HDFS和amazon的S3云存储服务
如果要将数据落地到hdfs上,需要先安装插件,elasticsearch/elasticsearch-hadoop
6、discovery.zen
es的自动发现节点机制,es是一个基于p2p的系统,先通过广播寻找存在的节点,再通过多播协议进行节点之间的通信,同时,也支持点对点的交互
不同网段节点组成es集群方式:
//禁用自动发现机制
discovery.zen.ping.multicast.enabled: false
//设置新节点被启动时能够发现的主节点列表
discovery.zen.ping.unicast.hosts: [“192.168.1.191", " 192.168.1.192"]
7、transport
代表es内部节点或集群与客户端的交互方式,默认内部使用tcp协议进行交互,同时支持http协议(json格式),thrift,servlet、memcached、zeroMQ等传输协议(通过插件方式集成)
二、ElasticSearch中的settings和mappings
settings 索引库默认配置
查看默认配置方法
curl -XGET http://localhost:9200/sh/_settings?pretty
修改索引库默认配置,如分片数量,副本数
curl -XPUT http://192.168.30.102:9200/sh/ -d ''{
"settings":{
"number_of_shards":3,
"number_of_replicas":3
}
}’
Mappings索引的字段名字,数据类型定义
类似于关系型数据库建表时要定义字段名及其数据类型,(类似solr中的schme),es中的mapping要比数据库中灵活很多,可以动态添加字段。并且一般不需要mapping都可以,因为es会自动根据数据格式定义它的类型,如果需要对某些字段添加特殊属性(如:定义使用其他分词器、是否分词、是否存储等),必须手动添加mapping
查询mapping信息
curl -XGET http://localhost:9200/ik/_mapping?pretty
修改字段相关属性
例子:
curl -XPOST http://localhost:9200/ik/ikType/_mapping -d'{
"properties": {
"content": {
"type": "string",
"index":"analyzed",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}'
三、elasticsearch的API
1、相关连接准备工作
TransportClient client;
@Before
public void client() throws UnknownHostException {
Map<String, String> map=new HashMap<>();
map.put("cluster.name", "zs");//es集群名称zs
Settings.Builder sBuilder=Settings.builder().put(map);
client=TransportClient.builder().settings(sBuilder).build();
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.30.101"), 9300));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.30.102"),9300));
}
2、创建索引库
@Test
public void createIndexBase() {
IndicesExistsResponse response=client.admin().indices().prepareExists("test1").execute().actionGet();
if(response.isExists()) {
System.out.println("索引库以存在=====");
client.admin().indices().prepareDelete("test1").execute();
}
Map<String, String> map=new HashMap<>();
map.put("number_of_shards", "5");
map.put("number_of_replicas", "1"); client.admin().indices().prepareCreate("test1").setSettings(map).execute();
}
3、添加索引库中相关内容
@Test
public void get() {
GetResponse response=client.prepareGet("test1","employee","1").execute().actionGet();
System.out.println(response.getVersion());
Map<String, Object> source=response.getSource();
for(String key:source.keySet()) {
System.out.println(key+"==="+source.get(key));
}
}
4、搜索
@Test
public void search() {
//指定从test,javatest索引库来查询
SearchRequestBuilder builder=client.prepareSearch("test","shsxt");
builder.setTypes("employee","blog","ikType");
builder.setFrom(0);
builder.setSize(8);
String key="bin";
builder.setQuery(QueryBuilders.multiMatchQuery(key, "name","first_name","content"));
//builder.addSort("age",SortOrder.DESC);
SearchResponse searchResponse = builder.get();
SearchHits hits = searchResponse.getHits();
System.out.println("共:"+hits.getTotalHits());
SearchHit[] hits2 = hits.getHits();
for(SearchHit hit:hits2) {
System.out.println("分数:"+hit.getScore());
Map<String, Object> source = hit.getSource();
System.out.println(hit.getIndex());
System.out.println(hit.getId());
for(String s:source.keySet()) {
System.out.println(s+"=="+source.get(s));
}
}
}
5、获取索引库中内容
@Test
public void get() {
GetResponse response=client.prepareGet("test1","employee","1").execute().actionGet();
System.out.println(response.getVersion());
Map<String, Object> source=response.getSource();
for(String key:source.keySet()) {
System.out.println(key+"==="+source.get(key));
}
}
6、更新
@Test
public void update() {
UpdateRequest request=new UpdateRequest();
request.index("test1");
request.type("employee");
request.id("3");
Map<String, String> map=new HashMap<String, String>();
map.put("age", "25");
map.put("name", "erke");
request.doc(map);
client.update(request);
System.out.println("OK++");
}
7、有则更新,无则添加
@Test
public void upsert() throws Exception {
IndexRequest indexRequest =new IndexRequest("test1","employee","1");
indexRequest.source(XContentFactory.jsonBuilder().startObject()
.field("name","pyl")
.field("age","23")
.field("haha","6666").endObject()
);
UpdateRequest updateRequest=new UpdateRequest("test1","employee","1").doc(
XContentFactory.jsonBuilder()
.startObject()
.field("name", "beijing")
.field("describe", "beijing is good")
.endObject()
);
updateRequest.upsert(indexRequest);
client.update(updateRequest);
}
8、删除
@Test
public void delete() throws InterruptedException, ExecutionException {
DeleteResponse response=client.prepareDelete("test1","employee","3").execute().actionGet();
System.out.println(response.isFound());
}
9、批量操作
@Test
public void bulk() throws IOException {
BulkRequestBuilder builder=client.prepareBulk();
IndexRequest request=new IndexRequest("test1","employee","5").source(
XContentFactory.jsonBuilder().startObject().field("name","Tom")
.field("age","18")
.field("like","book")
);
builder.add(request);
builder.add(client.prepareDelete("test1","employee","2"));
BulkResponse bulkResponse = builder.get();
System.out.println(bulkResponse.hasFailures());
}
四、ElasticSearch查询
四种搜索类型
1、query and fetch
速度最快,但会返回N倍数量的数据
向索引的所有分片(shard)都发出查询请求,各分片返回的时候把元素文档(document)和计算后的排名信息一起返回,这种搜索方法最快,只需查询一次,但是返回的数量可能是用户要求的size的N倍
如:用户要求分数前10的数据,N个分片均返回10个,然后客户端再排序,得到的数量过多
2、query then fetch
默认搜索方式
第一步,先想所有shard发出请求,各分片只返回排序和排名相关的信息(不包括document文档),然后进行第二步,去相关的shard取document,
3、DFS query and fetch
可以更精准的控制搜索打分和排名
比第一种方法多了一个初始化散发(initial scatter)步骤,可以更精准的控制搜索打分和排名
4、DFS query then fetch
初始化散发其实就是在进行真正的查询之前,先把各个分片的词频率和文档频率收集一下,然后进行词搜索的时候,各分片依据全局的词频率和文档频率进行搜索和排名。显然如果使用DFS_QUERY_THEN_FETCH这种查询方式,效率是最低的,因为一个搜索,可能要请求3次分片。但使用DFS方法,搜索精度应该是最高的。
总结:性能上QUERY_AND_FETCH最快,DFS_QUERT_THEN_FETCH精准度最高。从搜索的精准度来说,DFS的比非DFS的精准度更高
查询:query – builder.setQuery(QueryBuilders.matchQuery("name", "test"))
分页:from/size – builder.setFrom(0).setSize(1)
排序:sort – builder.addSort("age", SortOrder.DESC)
过滤:filter-builder.setPostFilter(QueryBuilders.rangeQuery("age").from(1).to(19))
高亮:highlight
统计:facet(已废弃)使用aggregations 替代
根据字段进行分组统计
根据字段分组,统计其他字段的值
size设置为0,会获取所有数据,否则,只会返回10条
五、ElasticSearch分页
与SQL使用limit来控制单页数量类似,Elasticsearch使用from size两个参数
size:每次返回多少结果,默认是10
from:从那条结果开始,默认是0
假设每页显示5条结果,那么1-3页查询请求分别是
第一页:_search?size=5
第二页:_search?size=5&from=5
第三页:_search?size=5&from=10
注意:一次不要请求太多页码过大的结果,这会给服务器造成很大压力,因为他们在返回前生成自己的排序结果,然后进行集中处理,以确保最终结果的正确性
time_out:查询结果是否超时
curl -XGET hrrp://localhost:9200/_search?timeout=10ms
es会在10ms内返回查询结果,注意,timeout并不会终止查询,只会在指定的短时间内返回当时已经查到的数据,然后关闭连接,在后台,其他的查询可能会依旧继续,尽管查询结果已经被返回了
六、分片查询
默认是randomize across shards:随机从分片中取数据
_local:指查询操作会优先在本地节点有的分片中查询,没有的话再在其他节点上查询
-Primary:只在主分片中查询
_primary_first:会优先在主分片中查询,如果主分片找不到,会在副本中查询
_only_node:指在指定id的节点里面查询,如果该节点只有查询索引的部分分片中查找,所以查询结果可能不完整,如__only_node:123在节点id为123的节点中查询
_prefer_node:node id 优先在指定的节点上执行查询
_shards:0,1,2,3 :查询指定分片的数据
七、脑裂问题
同一个集群中的不同节点。对于集群的状态有不一样的理解,discovery.zen.minimum_master_nodes用于控制选举的行为发生的最小集群节点数量。一般只有2个以上节点的集群中,主节点才有意义
正常情况下,集群中的所有节点,应该对集群中的master是一致的,若出现不一致的状态信息,说明不同节点对master节点的选择出现了异常,也就是所谓的脑裂问题,脑裂会使集群失去正常状态,导致集群不能正常工作。
脑裂产生原因:
1、由于是网络通信,网络问题可能会导致某些节点认为master死掉,而另选master,这种情况可能性较小
2、节点负载:由于master节点与data节点混在一起,即安装在同一节点上,当工作节点的负载较大时,导致实例es停止响应,而若这台节点恰好充当着master节点的角色,那么一部分节点会认为这个master节点失效,故重新选举节点,这是就会出现脑裂。同时由于data节点上es进程占用的内存较大,较大规模的内存回收操作也会造成es进程失去响应
脑裂问题的解决
修改相关配置
• 主节点
node.master: true //设为true表名此节点可以为master的候选节点,可以配置两台,防止master挂掉后,无master候选使用
node.data: false
• 从节点
node.master: false
node.data: true
• 所有节点
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping.unicast.hosts: [“slave1”, “master” , “slave2"]
八、优化
1、调整最大打开文件的数量
ulimit -a 默认数量为1024
可以将此数值调大,提高性能,高版本已经强制要求调整最大打开文件数量
ulimit -n 32000
2、修改配置文件调整es的JVM内存的大小
修改bin/elasticsearch.in.sh中ES_MIN_MEM和ES_MAX_MEM的大小,建议一样大,避免频繁的分配内存,根据服务器内存大小,一般分配60%左右
3、设置mlockall
Linux系统会将一些占着内存但不在使用的写进交换区(磁盘空间,非内存),等使用的时候再读出来,读取写入的过程会存在性能消耗,可以配置锁定swapped提高性能
修改文件
vim conf/elasticsearch.yml
bootstrap.mlockall: true
4、分片数量
一般分片数量多的话会提高分片索引效率,但过多会过少都会导致检索比较慢,分片数多会导致检索时打开的文件较多,也会导致多态服务器之间通信,而过少会导致单个分片索引过大,检索速度比较慢。因此,单个分片最多存储20G左右的索引数据,所以分片数量=数据总量/20G
5、副本数
副本多可以提升搜索能力,但过多副本会导致文件数量过多,对服务器造成额外压力,一般同步副本设置为2-3个
6、segment
各个分片由一个个segment组成,随着segment越多,查询性能会越来越差,我们可以将segment进行合并
索引量不是很大的话可以将segment设置为1
-命令行
curl -XPOST 'http://localhost:9200/shsxt/_optimize?max_num_segments=1'
– java代码:client.admin().indices().prepareOptimize(“shsxt").setMaxNumSegments(1).get();
7、删除文档
在lucene中删除一个文档,数据不会立马重磁盘上删除,而是在lucene索引中产生一个.del的文件,而在检索过程中这部分数据 也会参加检索,之后会判断将它去除掉,这样会降低检索效率,因此可执行清除文档
– curl -XPOST 'http://localhost:9200/elasticsearch/_optimize?only_expunge_deletes=true'
– client.admin().indices().prepareOptimize("elasticsearch ").setOnlyExpungeDeletes(true).get();
8、副本设置
如果在项目开始时有大量数据需要导入时,可以先将副本数设置为0,这样可以提高导入效率,若大于0,则数据导入的过程中会同时进行副本备份,对es增加压力。待索引完成后将副本数该回,这样可以提高效率
9、all域
elasticsearch默认为每个被索引的文档都定义了一个特殊的域“all域”,它自动包含索引文档中一个或多个域中的内容,在进行搜索时,不指明要搜索的文档的域,elasticsearch会自动搜索all域,提高速度,但也会增加系统在索引阶段对CPU和存储空间资源的开销
可以使用"_all" : {"enabled":false} 开关禁用它
九、相关注意
java代码操作es集群时,要保证本地和集群上的es版本一致
保证集群中每个节点的JDK版本和es配置一致
elasticsearch分片规则
建立索引时,根据id进行hash,得到的hash值与该索引库的分片数量取余,取余的值即为存入的分片ID
具体源码为:根据OperationRouting类generateShardId方法进行分片