1 Star 0 Fork 0

Kudryavka / es

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

Elasticsearch

项目演示代码:https://github.com/kudryavka1/es-demo

对于Elasticsearch 作为主数据库需要注意:Elasticsearch 当大批量并发写入数据时,写入的数据不能立即查询到,会有1-3秒的延迟。

1.Elasticsearch 介绍

1.1 Elasticsearch 简介

ElasticSearch,简称es,es是一个开源的高拓展的分布式全文检索引擎,它可以近乎实施的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也使用java开发并使用Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

1.2 和Solr的比较

Solr是Apache下的一个顶级开源项目,采用java开发,是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展、并对索引、搜索性能进行了优化。它可以独立运行,是一个独立的企业及搜索应用服务器,它对外提供类似于web-service的API接口。用户可以通过http请求,像搜索引擎服务器提交一定格式的文件,生成索引;也可以通过提出查找请求,并得到返回结果。

两者比较

  • 当单纯的对已有数据进行搜索时,Solr更快
  • 实时建立索引时,Solr会产生io阻塞,查询性能较差,ElasticSearch具有明显的优势
  • 随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化

个人总结: Elasticsearch 是基于Lucene封装的搜索工具(底层还是使用Lucene),Elasticsearch(Lucene)性能强大在于它使用了倒排索引进行保存数据。在数据量较少时,Solr的速度高于Elasticsearch,随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。具体可查阅博客https://blog.csdn.net/jetty_welcome/article/details/104342595

1.3 Elasticsearch 的使用场景

  • 维基百科,类似百度百科,全文检索,高亮,搜索推荐
  • 国外新闻网站,类似搜狐新闻,用户行为日志(点击,浏览,收藏,评论)+社交网络数据,数据分析。。。
  • Stack Overflow国外的程序异常讨论论坛
  • GitHub(开源代码管理),搜索上千亿行代码
  • 电商网站,检索商品
  • 日志数据分析,logstash采集日志,ES进行复杂的数据分析,ELK技术(elasticsearch+logstash+kibana)
  • 商品价格监控网站
  • 商业智能系统
  • 站内搜索

1.4 Elasticsearch 中概念的介绍

  • Elasticsearch 中的名词和传统数据库进行类比:

    image-20210922093544085

  • 倒排索引

    es的搜索快的原因

    image-20210922093623728

2.Elasticsearch 安装部署

Elasticsearch 的安装,需要用到Elasticsearch的本体,和ik分词器。同时可以再部署一个Elasticsearch head 来监控Elasticsearch的运行状态。

注意:Elasticsearch插件的版本 必须和主版本一致!(所有文件版本一致)

image-20210816104506476

2.1 Elasticsearch 7.x 在Linux下安装

可以参考博客https://www.cnblogs.com/tjp40922/p/12194739.html

记得安装完,要开放9200端口!

elasticsearch.yml 的配置(目前ES的配置)

#集群名称 
cluster.name: my-application

#节点名称
node.name: node-1

network.host: 0.0.0.0

http.port: 9200

cluster.initial_master_nodes: ["node-1"]

#开启跨域访问支持,默认为false  
http.cors.enabled: true  

#跨域访问允许的域名地址,(允许所有域名)以上使用正则  
http.cors.allow-origin: /.*/   

xpack.ml.enabled: false

2.2 ik 分词器的使用

ik 分词器可以让Elasticsearch 对中文有良好的识别率并分词
ik分词的两种模式:
  • ik_max_word 会将文本做最细粒度的拆分

    比如会将「中华人民共和国国歌」拆分为:中华人民共和国、中华人民、中华、华人、人民共和国、人民、人、民、共和国、共和、和、国国、国歌,会穷尽各种可能的组合;

  • ik_smart 最粗粒度的拆分

    比如会将「中华人民共和国国歌」拆分为:中华人民共和国、国歌。

显而易见在搜索效果中来说,拆分越细粒度的搜索效果越好

参考 https://zq99299.github.io/note-book/elasticsearch-senior/ik/30-ik-introduce.html

2.3 Elasticsearch head 的部署

下载 https://github.com/mobz/elasticsearch-head

进入项目目录

npm install
npm run start

在浏览器访问http://localhost:9100 进入主页面

image-20210816105445140

3 Elasticsearch 通过restful接口请求方式的使用

3.1 索引相关

PUT /shopping   // 创建一个索引
GET /shopping  //查看一个索引
DELETE /shopping // 删除shopping索引

3.2 文档的创建

POST /shopping/_doc/1    //如果不指定ID,则es随机帮我们生成
{
    "title":"小米11手机",
    "category":"小米11",
    "images":"http://www.gulixueyuan.com/xm.jpg",
    "price":3999.00
}

返回结果如下

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",    //自定义唯一性标识
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

3.3 文档的修改

POST /shopping/_update/1
{
	"doc": {
		"title":"小米手机",
		"category":"小米"
	}
}

返回结果如下

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 3,
    "result": "updated",   // 表示数据被更新
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 3,
    "_primary_term": 1
}

3.4 文档的删除

删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。

DELETE /shopping/_doc/1

返回结果如下

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 4,
    "result": "deleted",  //删除成功
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 4,
    "_primary_term": 1
}

再重新请求,查看是否删除成功

GET /shopping/_doc/1
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "found": false
}

3.5 文档的查询

GET /shopping/_search
{
	"query":{
		"match":{
			"category":"小米"
		}
	}
}

返回结果如下

{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1.3862942,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手机",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手机",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手机",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

3.6 查询索引中文档的数量

GET /_cat/count/index_name?v
返回数据:
epoch      timestamp count
1629278159 09:15:59  10372015

4 Elasticsearch 在Sprinboot 环境中使用

4.1 搭建环境

在pom目录中 引入 es 注意版本要对应

 		<dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

配置Config类

@Configuration
public class ElasticSearchClientConfig {
    private final static String HOST = "121.40.68.176";
    private final static Integer PORT = 9200;
    private final static String SCHEME = "http";
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost(HOST,PORT,SCHEME))
        );
        return client;
    }
}

4.2 调用es的增删改查方法

这里以潍柴项目作为举例

   public String save(EsNews esNews) throws IOException {
        IndexRequest request = new IndexRequest(indexName);
        request.id(esNews.getId());
        request.source(JSONObject.toJSONString(esNews),XContentType.JSON);
        IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        return index.status().toString();
    }
 public String deleted(String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(indexName,id);
        DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        return delete.status().toString();
    }
 public String update(EsNews esNews) throws IOException {
        UpdateRequest request = new UpdateRequest(indexName, esNews.getId());
        request.doc(JSONObject.toJSONString(esNews),XContentType.JSON);
        UpdateResponse update = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        return update.status().toString();
    }
public EsNews getById(String id) throws IOException {
        GetRequest getRequest = new GetRequest(indexName, id);
        GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        EsNews esNews = JSONObject.parseObject(response.getSourceAsString(), EsNews.class);
        return esNews;
    }

4.3 批量保存方法

当ES新建,需要大量的数据从其他库导入时,可以调用以下方法。

该方法以MongoDB举例,MySQL同理

public Boolean saveNewsFromDbToEs() throws IOException {
        for (int i = 0; ; i++) {
            Integer size = 100;
            Query query = new Query();
            query.limit(size);
            query.skip(i * size); // 分页保存,每次保存100条
            List<EsNews> list = mongoTemplate.find(query, EsNews.class);
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.timeout("10s");
            if (list.isEmpty()){
                break;
            }
            for (EsNews news : list) {
                bulkRequest.add(new IndexRequest(indexName)
                                .id(news.getId()).source(JSONObject.toJSONString(news),XContentType.JSON));
            }
            restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); //执行保存
        }
        return true;
    }

4.4 搜索

es的搜索分为两种,**match和term。**match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找,而term会直接对关键词进行查找。一般模糊查找的时候,多用match,而精确查找时可以使用term。

潍柴项目中,需要根据title和context进行模糊查询,所以这里使用match搜索,并使用should将两个条件关联(只要满足其一即可)

public List<Map<String, Object>> search(String title,String content) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        //构建搜索
        MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("title", title);
        MatchQueryBuilder matchQueryBuilder2 = QueryBuilders.matchQuery("contentText", content);
        BoolQueryBuilder must = QueryBuilders.boolQuery()
            .must(matchQueryBuilder1)
            .should(matchQueryBuilder2);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(must)
//                .query(matchQuery)
                .timeout(TimeValue.timeValueSeconds(10))
                .size(20);
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = search.getHits();
        List<Map<String ,Object>> list = new ArrayList<>();
        for (SearchHit hit : hits) {
            if (hit.getScore() >= minScore){
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                sourceAsMap.put("source",hit.getScore());
                list.add(sourceAsMap);
            }
        }
        System.out.println("搜索出了"+hits.getHits().length+"条");
        return list;
    }

4.5 高亮搜索

public List<Map<String, Object>> searchHighLight(String title,String content) throws IOException {
        SearchRequest request = new SearchRequest(indexName);
        MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("title", title);
        MatchQueryBuilder matchQueryBuilder2 = QueryBuilders.matchQuery("contentText", content);
        BoolQueryBuilder must = QueryBuilders.boolQuery()
            .must(matchQueryBuilder1)
            .should(matchQueryBuilder2);
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<span style='color:red'>").postTags("</span>");
        highlightBuilder.field("contentText").preTags("<span style='color:red'>").postTags("</span>");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(must)
//                .query(matchQuery)
                .timeout(TimeValue.timeValueSeconds(10))
                .highlighter(highlightBuilder)
                .size(20);
        request.source(searchSourceBuilder);
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

        //分析结果
        List<Map<String,Object>> list = new ArrayList<>();
        for (SearchHit hit : response.getHits()) {
            // 解析高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField restitle = highlightFields.get("title");
            HighlightField resContent = highlightFields.get("contentText");
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            if (restitle != null){
                // 将原来的字段 换位新的高亮字段
                Text[] fragments = restitle.getFragments();
                String newTitle = "";
                for (Text fragment : fragments) {
                    newTitle+=fragment;
                }
                sourceAsMap.put("title",newTitle); // 替换原先的字段
            }
            if (resContent != null){
                // 将原来的字段 换位新的高亮字段
                Text[] fragments = resContent.getFragments();
                String newContentText = "";
                for (Text fragment : fragments) {
                    newContentText+=fragment;
                }
                sourceAsMap.put("contentText",newContentText); // 替换原先的字段
            }
            list.add(sourceAsMap);
        }

        return list;
    }

4.6 排序

 		// 排序条件
        FieldSortBuilder timeSortBuilder = SortBuilders.fieldSort("sourceTime").order(SortOrder.DESC);
        // 默认score是倒序
        ScoreSortBuilder scoreSortBuilder = new ScoreSortBuilder();

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(must)
                .timeout(TimeValue.timeValueSeconds(10))
                .sort(timeSortBuilder) //先根据时间排序
                .sort(scoreSortBuilder) //再根据得分排序
                .size(10);

4.7 聚合搜索

根据输入的字段进行聚合搜索,相当于sql语句中的group by

    public Map<String,Object> aggsByField(String field) throws IOException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        TermsAggregationBuilder aggregation = AggregationBuilders.terms(field).field(field)
                .subAggregation(AggregationBuilders.sum("sum_id").field("id")).  //求和
                subAggregation(AggregationBuilders.avg("avg_id").field("id")); //求平均值
        sourceBuilder.aggregation(aggregation);
        sourceBuilder.size(0);
        SearchRequest searchRequest = new SearchRequest().indices(indexName).source(sourceBuilder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = response.getAggregations();
        ParsedStringTerms parsedStringTerms = aggregations.get(field);
        Map<String,Object> map = new HashMap<>();
        List<? extends Terms.Bucket> buckets = parsedStringTerms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            //获取数据
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedSum sumId = bucketAggregations.get("sum_id");
            ParsedAvg avgId = bucketAggregations.get("avg_id");
            map.put(bucket.getKey().toString(),bucket.getDocCount());
        }
        return map;
    }

5 Elasticsearch 使用小知识

5.1 保存时间格式

在创建索引时,如果不指定mapping,通过接口直接保存Java对象,遇见yyyy-mm-dd类型的字符串,ES不会将其识别为date类型,而是会识别为text。

如果想让ES存储类型改为date类型,方便日后进行搜索,那么在创建索引时,需要指定mapping属性;

例如: fetchTime 和 sourceTime 都需要指定为date类型

PUT /weichai_dev
{
  "mappings": {
      "properties" : {
   		...
        "content" : {
          "type" : "text",
          "analyzer":"ik_smart"
        },
       ...
        "fetchTime" : {
          "type": "date",
           "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd" 
        },
      ...
        "imageNames" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
       ...
    }
  }
}

5.2 Elasticsearch 两种修改方式的比较

ES想修改文档,可以有两种方式。

  • PUT 覆盖文档

    PUT 会 根据ID,对文档进覆盖,如果PUT的文档包含空字段,那么空字段也会覆盖现有文档。

  • UPDATE 更新文档

    UPDATE也是根据ID进行更新文档,但和PUT的覆盖不同,如果new一个对象,只赋值一个属性,再去更新,那么只会更新一个字段(不更新值为null的字段)。

5.3 Elasticsearch 查询方式的比较

  • term

    term会直接对关键词进行查找,对文档不进行分词查询,精确查找时可以使用term。

  • terms

    terms查询时,需要传值一个数组。相当于SQL中的in语法。对数组中的值进行term查找。

  • match

    match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找,一般模糊查找的时候,多用match。

  • matchAll

    查询所有,相当于select * from table

5.4 为索引增加一个字段并设置默认值

  • 使用 put 方法加字段
    PUT my_index/_mapping
    {
      "properties": {
        "字段":{
          "type": "类型"
        }
      }
    }
  • 设置默认值使用 post
    POST my_index/_update_by_query
    {
      "script": {
        "lang": "painless",
        "source": "if (ctx._source.字段== null) {ctx._source.字段= '0'}"
      }
    }

注意:索引的字段mapping只能添加,不能修改,也不能修改对应的类型。如果需要修改字段,请删索引重新创。

5.5 mapping 中的嵌套对象

在Elasticsearch中,如果不进行设置,往索引中保存文档时,当索引没有文档中的字段,索引会自动把该字段添加到mapping中去。

在保存文档时,我们不应该把具体的value当成mapping,这会造成索引的mapping随着文档的增加而无限制的增加。

目前潍柴项目中,mapping的设置就是反例:

image-20210918164734227

面对这种场景,我们需要设置嵌套对象。

例:我们需要获取一个网站中所有的cookies,而cookies的类型是非常多的,如果不断的保存,会造成索引的mapping不断增加。

这里我们定义一个cookies嵌套对象。类似于我们Java中,给类定义一个List属性。

其中,cookies字段的type为nested。

定义mapping:

PUT /map_test
{
   "mappings": {
      "properties" : {
        "url" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "cookies" :{
          "type": "nested",
          "properties": {
            "name": {
              "type": "keyword"
            },
            "value": {
              "type": "text"
            }
          }
        }
    }
  }
}

向索引中添加数据:

PUT /map_test/_doc/1
{
  "url":"www.baidu.com",
  "cookies": [
    {
      "name": "userId",
      "value": "123456"
    },
    {
      "name": "userName",
      "value": "zhanghao"
    },
    {
      "name": "system",
      "value": "win10"
    }
  ]
}

通过查询,我们可以看到我们刚刚添加的数据

{
  "_index" : "map_test",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "url" : "www.baidu.com",
    "cookies" : [
      {
        "name" : "userId",
        "value" : "123456"
      },
      {
        "name" : "userName",
        "value" : "zhanghao"
      },
      {
        "name" : "system",
        "value" : "win10"
      }
    ]
  }
}

查询mapping,发现mapping中也没有添加多余的字段。

GET /map_test/_mapping
{
  "map_test" : {
    "mappings" : {
      "properties" : {
        "cookies" : {
          "type" : "nested",
          "properties" : {
            "name" : {
              "type" : "keyword"
            },
            "value" : {
              "type" : "text"
            }
          }
        },
        "url" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

这里我们介绍如何创建nested,具体使用、查询nested字段篇幅太大,可以参考博客

https://blog.csdn.net/laoyang360/article/details/82950393

使用nested也有一定的缺点,更新nested字段时会修改整篇文档,同时查询会变得复杂。

如果嵌套字段需要频繁修改,建议不要使用nested,而去使用父子文档。

5.6 避免空值引起的聚合不准

在我们聚合查询时,如果聚合的字段设置为null,则该文档聚合时对外不可见,会造成聚合不准。

例:求平均值

首先我们创建一个mapping

PUT /aggtest
{
   "mappings": {
      "properties" : {
        "score" : {
          "type": "float"
        }
    }
  }
}

插入两条数据,其中一条的score为null

PUT /aggtest/_doc/1
{
  "score": 5
}
PUT /aggtest/_doc/2
{
  "score": null
}

然后我们对score字段聚合求平均数

POST /aggtest/_search
{
  "size": 0,
  "aggs": {
    "avg": {
      "avg": {
        "field": "score"
      }
    }
  }
}

查看返回结果

"hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "avg" : {
      "value" : 5.0
    }
  }

可以看到,我们虽然有2个文档,一个为值为5一个为null,但平均数求出来为5,而total为2,这是不准确的。

要避免这种情况,我们需要在创建mapping时,指定null_value

PUT /aggtest
{
   "mappings": {
      "properties" : {
        "score" : {
          "type": "float",
          "null_value": "1.0"
        }
    }
  }
}

再重新聚合,求平均数,可以看到求出的平均数是准的了。

"hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "avg" : {
      "value" : 3.0
    }
  }

为什么会出现该问题?

null 不能被索引或搜索。 当字段设置为 null(或空数组或 所有值为 null 值的数组)时,将其视为该字段没有值。使用 null_value

参数可以用指定的值替换显式的空值,以便可以对其进行索引和搜索。

6 Elasticsearch 出现的问题以及对应的解决方法

6.1 Elasticsearch 索引变为只读

ES插入大量的数据时报错

TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block

原因:

是因为一次请求中批量插入的数据条数巨多,以及短时间内的请求次数巨多引起ES节点服务器内存超过限制。

解决方法:

PUT _all/_settings
{
  "index.blocks.read_only_allow_delete": null
}

6.2 索引数据量过大, 变成只读

[2021-08-18T11:15:24,911][WARN ][o.e.c.r.a.DiskThresholdMonitor] [node-1] flood stage disk watermark [95%] exceeded on [c0yTJqovTfyY3AFwR-FKHw][node-1][/home/elasticsearch/elasticsearch-7.14.0/data/nodes/0] free: 1.7gb[4.1%], all indices on this node will be marked read-only

原因:

es的磁盘不够了

解决方法:

对磁盘扩容,具体参考文章 https://www.cnblogs.com/straycats/p/11261364.html

7 Elasticsearch 内存设置

ES 默认安装后设置的内存是 1GB,对于任何一个现实业务来说,这个设置都太小了。

如果是通过解压安装的 ES,则在 ES 安装文件中包含一个 jvm.option 文件,添加如下命令来设置 ES 的堆大小,Xms 表示堆的初始大小,Xmx 表示可分配的最大内存,都是 1GB。

确保 Xmx 和 Xms 的大小是相同的,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源,可以减轻伸缩堆大小带来的压力。

假设你有一个 64G 内存的机器,按照正常思维思考,你可能会认为把 64G 内存都给 ES 比较好,但现实是这样吗, 越大越好?虽然内存对 ES 来说是非常重要的,但是答案是否定的!

因为 ES 堆内存的分配需要满足以下两个原则:

  • 不要超过物理内存的 50%:Lucene 的设计目的是把底层 OS 里的数据缓存到内存中。 Lucene 的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系 统也会把这些段文件缓存起来,以便更快的访问。 如果我们设置的堆内存过大,Lucene 可用的内存将会减少,就会严重影响降低 Lucene 的全文本查 询性能。
  • 堆内存的大小最好不要超过 32GB:在 Java 中,所有对象都分配在堆上,然后有一个 Klass Pointer 指针指向它的类元数据。 这个指针在 64 位的操作系统上为 64 位,64 位的操作系统可以使用更多的内存(2^64)。在 32 位 的系统上为 32 位,32 位的操作系统的最大寻址空间为 4GB(2^32)。 但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的 指针在主内存和缓存器(例如 LLC, L1等)之间移动数据的时候,会占用更多的带宽。 最终我们都会采用 31 G 设置 -Xms 31g -Xmx 31g

假设你有个机器有 128 GB 的内存,你可以创建两个节点,每个节点内存分配不超过 32 GB。 也就是说不超过 64 GB 内存给 ES 的堆内存,剩下的超过 64 GB 的内存给 Lucene

空文件

简介

暂无描述 展开 收起
Java 等 2 种语言
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/kudryavka/es.git
git@gitee.com:kudryavka/es.git
kudryavka
es
es
master

搜索帮助