1. ElasticSearch 高级
2. 批量操作
Bulk 批量操作是将文档的增删改查一系列操作 通过一次请求 全部完成 减少网络传输次数
2.1. 脚本操作
GET person/_search
# 批量操作
POST _bulk
{"delete":{"_index":"person","_id":"3"}}
{"create":{"_index":"person","_id":"8"}}
{"name":"hhh","age":88,"address":"qqqq"}
{"update":{"_index":"person","_id":"2"}}
{"doc":{"name":"qwedqd"}}
2.2. API
//批量操作 bulk
@Test
public void testBulk() throws IOException {
//创建bulkrequest对象 整合所有操作
BulkRequest bulkRequest = new BulkRequest();
//添加操作
//删除1号操作
DeleteRequest deleteRequest = new DeleteRequest("person","1");
bulkRequest.add(deleteRequest);
//添加6号操作
Map map = new HashMap();
map.put("name","测试");
IndexRequest indexRequest = new IndexRequest("person").id("6").source(map);
bulkRequest.add(indexRequest);
//修改3号操作
Map map2 = new HashMap();
map2.put("name","测试3号");
UpdateRequest updateReqeust = new UpdateRequest("person","3").doc(map2);
bulkRequest.add(updateReqeust);
//执行批量操作
BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
RestStatus status = response.status();
System.out.println(status);
}
3. 导入数据
将数据库中表数据导入到ElasticSearch中
- 创建索引 并添加mapping
- 创建pojo类 映射mybatis
- 查询数据库
- 使用Bulk 批量导入
3.1. 使用fastjson 转换对象不转换该成员变量
在成员变量加上注解 使用@JSONField(serialize =false) 此成员变量不参与json转换
4. matchALL查询
matchALL查询 查询所有文档
2.1. 脚本操作
# 默认情况下,es一次只展示10条数据 通过from控制页码 size控制每页展示条数
GET person/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 100
}
4.2. Api操作
//matchall查询所有 分页操作
@Test
public void testMatchALL() throws IOException {
//2通过索引查询
SearchRequest searchRequest = new SearchRequest("person");
//4创建查询条件构建器
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//5查询条件
QueryBuilder query = QueryBuilders.matchAllQuery(); //查询所有文档
//6指定查询条件
sourceBuilder.query(query);
//分页查询
sourceBuilder.from(0); //第几页
sourceBuilder.size(100); //每页显示数
//3添加查询条件构建器
searchRequest.source(sourceBuilder);
//1查询 获取查询结果
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//7获取命中对象 hits
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
5. term查询
5.1. 脚本查询
# term查询 词条查询 一般用于查分类
GET person/_search
{
"query": {
"term": {
"name": {
"value": "hhh"
}
}
}
}
5.2. api查询
//termQuery 词条查询
@Test
public void testTermQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
QueryBuilder query= QueryBuilders.termQuery("name","hhh");
sourceBulider.query(query);
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
6. match查询
- 会对查询条件进行分词
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集(OR) 交集为(AND)
2.1. 脚本操作
# match 查询
GET person/_search
{
"query": {
"match": {
"name": {
"query": "hhh",
"operator": "or"
}
}
}
}
6.2. API操作
//match 词条查询
@Test
public void testMatchQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
MatchQueryBuilder query= QueryBuilders.matchQuery("name","hhh");
query.operator(Operator.AND); //并集
sourceBulider.query(query);
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
7. 模糊查询
- wildcard查询 会对查询条件进行分词 可以使用通配符 ?(任意单个字符) 和 * (0或多个字符)
- regexp 查询 正则查询
- prefix 前缀查询
5.1. 脚本查询
# wildcard 查询 查询条件分词 模糊查询
GET person/_search
{
"query": {
"wildcard": {
"name": {
"value": "h"
}
}
}
}
# 正则查询 查询条件分词 模糊查询
GET person/_search
{
"query": {
"regexp": {
"name": "\\q+(.)*"
}
}
}
# 前缀查询
GET person/_search
{
"query": {
"prefix": {
"name": {
"value": "qwe"
}
}
}
}
7.2. api操作
//wildcard 模糊查询
@Test
public void testWildcardQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
WildcardQueryBuilder query = QueryBuilders.wildcardQuery("name", "h*");
sourceBulider.query(query);
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
//regexp 正则查询
@Test
public void testrRegexpQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
RegexpQueryBuilder query = QueryBuilders.regexpQuery("name", "\\h*");
sourceBulider.query(query);
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
//prefix 前缀查询
@Test
public void testrPrefixQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
PrefixQueryBuilder query = QueryBuilders.prefixQuery("name", "qwe");
sourceBulider.query(query);
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
8. 范围查询
range范围查询 查询指定字段在指定范围内包含值
5.1. 脚本查询
#范围查询
GET perso/_search
{
"query": {
"range": {
"age": {
"gte": 10,
"lte": 30
}
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
5.2. api查询
//range 范围查询
@Test
public void testRangeQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
RangeQueryBuilder query = QueryBuilders.rangeQuery("age");//指定字段
query.gte("10"); //小于等于
query.lte("30"); //大于等于
sourceBulider.query(query);
sourceBulider.sort("age", SortOrder.ASC); //排序
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
9. queryString查询
- 会对查询条件进行分词
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集
- 可以指定多个查询字段
5.1. 脚本查询
# queryString 查询
GET person/_search
{
"query": {
"query_string": {
"fields": ["name","address"],
"query": "华为 OR 手机"
}
}
}
# SimpleQueryStringQuery是QueryStringQuery的简化版,其本身不支持 AND OR NOT 布尔运算关键字,这些关键字会被当做普通词语进行处理。
# 可以通过 default_operator 指定查询字符串默认使用的运算方式,默认为 OR
GET person/_search
{
"query": {
"simple_query_string": {
"fields": ["name","address"],
"query": "华为 OR 手机"
}
}
}
9.2. Api查询
//queryString
@Test
public void testQueryStringQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBulider=new SearchSourceBuilder();
QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为").field("name").field("address").defaultOperator(Operator.OR);
sourceBulider.query(query);
searchRequest.source(sourceBulider);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
10. 布尔查询
boolQuery 对多个查询条件连接 组合查询
- must (and) 条件必须成立
- must_not (not) 条件必须不成立
- should(or) 条件可以成立
- filter 条件必须成功 性能比must高 不会计算得分 (当查询结果符合查询条件越多则得分越多)
5.1. 脚本查询
# bool查询
GET person/_search
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "张三"
}
}}
]
}
}
}
#filter
GET person/_search
{
"query": {
"bool": {
"filter": [
{"term": {
"name": {
"value": "张三"
}
}}
]
}
}
}
# 组合多条件查询
GET person/_search
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "张三"
}
}}
],
"filter": [
{
"term": {
"address": "5G"
}
}
]
}
}
}
10.2. API查询
//boolQuery
@Test
public void testBoolQueryQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
//构建boolQuery
BoolQueryBuilder query = QueryBuilders.boolQuery();
//构建各个查询条件
TermQueryBuilder termQuery = QueryBuilders.termQuery("name", "张三");//查询名字为张三的
query.must(termQuery);
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("address", "5G"); //查询地址包含5g的
query.must(matchQuery);
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
}
11. 聚合查询
- 指标聚合 相当于mysql的聚合函数 max min avg sum等
- 桶聚合 相当于mysql的group by . 不要对text类型的数据进行分组 会失败
5.1. 脚本查询
# 聚合查询
# 指标聚合 聚合函数
GET person/_search
{
"query": {
"match": {
"name": "张三"
}
},
"aggs": {
"NAME": {
"max": {
"field": "age"
}
}
}
}
# 桶聚合 分组 通过 aggs
GET person/_search
{
"query": {
"match": {
"name": "张三"
}
},
"aggs": {
"zdymc": {
"terms": {
"field": "age",
"size": 10
}
}
}
}
9.2. Api查询
//聚合查询 桶聚合 分组
@Test
public void testAggsQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
MatchQueryBuilder query = QueryBuilders.matchQuery("name", "张三");
sourceBuilder.query(query);
/**
* terms 查询后结果名称
* field 条件字段
* size 每页展示的条数
*/
TermsAggregationBuilder aggs = AggregationBuilders.terms("自定义名称").field("age").size(10);
sourceBuilder.aggregation(aggs);
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
System.out.println(sourceAsString);
}
//获取聚合结果
Aggregations aggregations = search.getAggregations();
Map<String, Aggregation> aggregationMap = aggregations.asMap(); //将结果转为map
Terms zdymc = (Terms) aggregationMap.get("自定义名称");
List<? extends Terms.Bucket> buckets = zdymc.getBuckets();
ArrayList list = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
Object key = bucket.getKey();
list.add(key);
}
for (Object o : list) {
System.out.println(o);
}
}
12. 高亮查询
- 高亮字段
- 前缀
- 后缀 如果不设置前后缀 默认为em标签
2.1. 脚本操作
# 高亮查询
GET person/_search
{
"query": {
"match": {
"address": "手机"
}
},
"highlight": {
"fields": {
"address": {
"pre_tags": "<font color='red'>",
"post_tags": "</font>"
}
}
}
}
4.2. Api操作
//highlight 高亮查询
@Test
public void testHighlightQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
MatchQueryBuilder query = QueryBuilders.matchQuery("address", "手机");
sourceBuilder.query(query);
HighlightBuilder highlightBuilder = new HighlightBuilder();//高亮对象
highlightBuilder.field("address"); //字段
highlightBuilder.preTags("<font color='red'>"); //前缀
highlightBuilder.postTags("</font>"); //后缀
sourceBuilder.highlighter(highlightBuilder);
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = search.getHits();
//获取总条数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数" + value);
//获取hits数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString(); //获取json字符串格式的数据
//todo 将对象转为json
Map<String, HighlightField> highlightFields = hit.getHighlightFields(); //获取高亮中的对象元素
HighlightField address = highlightFields.get("address");
Text[] fragments = address.fragments(); //获取元素中的高亮数组结果
//替换json中的成员变量数据
//todo
System.out.println(sourceAsString);
System.out.println(Arrays.toString(fragments));
}
}
13. 重建索引&索引别名
ES的索引一旦创建,只允许添加字段 不允许改变字段 因为改变字段 需要重建倒排索引 影响内部缓存结构
此时需要重建一个新的索引 并将原有索引的数据导入到新索引中
# 重建索引
# 新建索引 索引名称必须全部小写
PUT stdent_index_v1
{
"mappings": {
"properties": {
"birthday":{
"type": "date"
}
}
}
}
GET stdent_index_v1
PUT stdent_index_v1/_doc/1
{
"birthday":"1999-01-01"
}
GET stdent_index_v1/_search
# 现在stdent_index_v1需要存储birthday为一个字符串
# 1.创建新的索引v2
PUT stdent_index_v2
{
"mappings": {
"properties": {
"birthday":{
"type": "text"
}
}
}
}
#2.将旧索引的数据拷贝到新索引 使用_reindex
POST _reindex
{
"source": {
"index": "stdent_index_v1"
},
"dest": {
"index": "stdent_index_v2"
}
}
GET stdent_index_v2/_search
PUT stdent_index_v2/_doc/2
{
"birthday":"199年124日"
}
# 索引别名 因为旧索引已经不使用 而我们代码中写的是旧索引名 无法正常运行 则需要别名
# 1.删除旧索引
DELETE stdent_index_v1
# 2. 给新索引起别名为旧索引名
POST stdent_index_v2/_alias/stdent_index_v1
14. ES集群
ES天然支持分布式,并且分布式自动配置
- 集群(cluster) 一组拥有共同的cluster name 节点
- 节点(node) 集合中的一个ES实例
- 索引(index) es存储数据的地方
- 分片(shard) 索引可以被拆分为不同的部分进行存储 称为分片 在集群环境下 一个索引的不同可以拆分到不同节点中
- 主分片(Primary shard) 相当于副本分片的定义
- 副本分片 每个主分片可以有一个或多个副本 数据与主分片一样
14.1. 搭建
- 准备3个集群 此处作伪集群 使用端口号区分
cp -r elasticsearch-7.15.0 elasticsearch-7.15.0-1
cp -r elasticsearch-7.15.0 elasticsearch-7.15.0-2
cp -r elasticsearch-7.15.0 elasticsearch-7.15.0-3
- 创建日志和data目录 并授权给iekr用户 ```sh cd /opt mkdir logs mkdir data
授权
chown -R iekr:iekr ./logs chown -R iekr:iekr ./data
chown -R iekr:iekr ./elasticsearch-7.15.0-1 chown -R iekr:iekr ./elasticsearch-7.15.0-2 chown -R iekr:iekr ./elasticsearch-7.15.0-3
3.
修改三个集群的配置文件
```sh
vim /opt/elasticsearch-7.15.0-1/config/elasticsearch.yml
# 集群名称 各个集群必须一致
cluster.name: itcast-es
# 节点名称 不能一致
node.name: iekr-1
#是否有资格主节点
node.master: true
#是否存储数据
node.data: true
#最大集群数
node.max_local_storage_nodes: 3
#ip地址
network.host: 0.0.0.0
# 端口
http.port: 9201
#内部节点之间沟通端口
transport.tcp.port: 9700
#节点发现 es7.x才有
discovery.seed_hosts: ["localhost:9700","localhost:9800","localhost:9900"]
#初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: ["iekr-1","iekr-2","iekr-3"]
#数据和存储路径
path.data: /opt/data
path.logs: /opt/logs
vim /opt/elasticsearch-7.15.0-2/config/elasticsearch.yml
# 集群名称 各个集群必须一致
cluster.name: itcast-es
# 节点名称 不能一致
node.name: iekr-2
#是否有资格主节点
node.master: true
#是否存储数据
node.data: true
#最大集群数
node.max_local_storage_nodes: 3
#ip地址
network.host: 0.0.0.0
# 端口
http.port: 9202
#内部节点之间沟通端口
transport.tcp.port: 9800
#节点发现 es7.x才有
discovery.seed_hosts: ["localhost:9700","localhost:9800","localhost:9900"]
#初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: ["iekr-1","iekr-2","iekr-3"]
#数据和存储路径
path.data: /opt/data
path.logs: /opt/logs
vim /opt/elasticsearch-7.15.0-3/config/elasticsearch.yml
# 集群名称 各个集群必须一致
cluster.name: itcast-es
# 节点名称 不能一致
node.name: iekr-3
#是否有资格主节点
node.master: true
#是否存储数据
node.data: true
#最大集群数
node.max_local_storage_nodes: 3
#ip地址
network.host: 0.0.0.0
# 端口
http.port: 9203
#内部节点之间沟通端口
transport.tcp.port: 9900
#节点发现 es7.x才有
discovery.seed_hosts: ["localhost:9700","localhost:9800","localhost:9900"]
#初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: ["iekr-1","iekr-2","iekr-3"]
#数据和存储路径
path.data: /opt/data
path.logs: /opt/logs
- ES默认占用1G 我们通过配置文件修改
vim /opt/elasticsearch-7.15.0-1/config/jvm.options
-Xms256m
-Xmx256m
- 分别启动
systemctl stop firewalld
su iekr
cd /opt/elasticsearch-7.15.0-1/bin/
./elasticsearch
14.2. 使用Kibana配置和管理集群
- 复制kibana
cd /opt
cp -r kibana-7.15.0-linux-x86_64 kibana-7.15.0-linux-x86_64-cluster
- 修改kibana集群配置
vim /opt/kibana-7.15.0-linux-x86_64-cluster/config/kibana.yml
#修改以下内容
elasticsearch.hosts: ["http://localhost:9201","http://localhost:9202","http://localhost:9203"]
- 启动
cd /opt/kibana-7.15.0-linux-x86_64-cluster/bin/
./kibana --allow-root
- 访问 http://192.168.130.124:5601/app/monitoring 查询集群节点信息
14.3. JavaApi访问集群
- application.yml
elasticsearch:
host: 192.168.130.124
port: 9201
host2: 192.168.130.124
port2: 9202
host3: 192.168.130.124
port3: 9203
- config类 并注册ioc容器 ```java package com.itheima.elasticsearchdemo.config;
import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @ConfigurationProperties(prefix = “elasticsearch”) public class ElasticSearchConfig {
private String host;
private int port;
private String host2;
private int port2;
private String host3;
private int port3;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Bean
public RestHighLevelClient client(){
return new RestHighLevelClient(RestClient.builder(
new HttpHost(host,port,"http"),
new HttpHost(host2,port2,"http"),
new HttpHost(host3,port3,"http")
));
}
}
<a name="03684b4b"></a>
## 14.4. 集群原理
<a name="58346d8b"></a>
### 14.4.1. 分片配置
-
在创建索引时 如果不指定分配配置 默认主分片1 副本分片1
- ![](https://cdn.jsdelivr.net/gh/Iekrwh/images/md-images/image-20211010162725564.png#alt=image-20211010162725564)
-
在创建索引时 可以通过settings设置分片
-
```apl
PUT stdent_index_v3
{
"mappings": {
"properties": {
"birthday":{
"type": "date"
}
}
},
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
分片与自平衡: es默认会交错存储分片 如果其中一个节点失效不影响访问 并且会自动将失效的分片归并到目前仍在线的节点上 节点重新上线归还分片
ES每个查询在每个分片中是单线程执行 但是可以并行处理多个分片
分片数量一旦确定好了 不能修改 但是可以通过重建索引和索引别名来迁移
索引分片推荐配置方案
- 每个分片推荐大小10-30GB
- 分片数量推荐 = 节点数 * 1 ~ 3 倍
14.4.2. 路由原理
- 文档存入对应的分片 es计算分片编号的过程 称为路由
- 路由算法 shard_index = hash(id) % number_of_shards
14.5. 脑裂
- 一个正常es集群中只有一个主节点 主节点负责管理整个集群 如创建或删除索引 跟踪哪些节点是集群的一部分 并决定哪些分片分配给相关的节点
- 集群的所有节点都会选择同一个节点作为主节点
- 脑裂问题的出现是因为从节点在选择主节点上出现分歧导致一个集群出现多个主节点从而集群分离,使得集群处于异常状态
14.5.1. 脑裂原因
网络原因: 网络延迟 一般出现在外网集群
节点负责 主节点的角色即为master又为data 当数据访问量较大时 可能导致Master节点停止响应(假死状态)
#是否有资格主节点
node.master: true
#是否存储数据
node.data: true
JVM内存回收
- 当Master节点设置的JVM内存较小时 引发JVM的大规模内存回收 造成ES进程失去响应
14.5.2. 避免脑裂
- 网络原因: discovery.zen.ping.timeout 超时时间配置大一些 默认为3S
- 节点负责 角色分离 当主节点就不要当数据存储 当数据存储的就不要当主节点
- 修改 jvm.options 的最大内存和最小内存 为服务器的内存一半
15. 集群扩容
- 修改所有集群中的 配置文件 添加新的集群
- 全部启动