Elasticsearch-使用bulk会掉数据?

在这里插入图片描述

先介绍一个参数

“cluster.routing.allocation.disk.watermark.flood_stage”:“95%”

这是Elasticsearch的默认参数值
官方文档(7\8版本该参数功能相同)
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-cluster.html
在这里插入图片描述
简单的说:该参数控制磁盘写入的阈值,当超过该阈值索引会变成只读状态。
例如,磁盘100G,该参数“95%”,那么当磁盘使用量超过95G时,elasticsearch上的索引将只读,从而保证不要出现磁盘写满的情况。
这个参数看似平平无奇,实则配合上es bulk写入会有惊人的效果。

实验

一、向test_2024索引中插入一条数据(golang)
	es, err := elasticsearch.NewClient(cfg)
	if err != nil {
		log.Fatalf("Error creating the client: %s", err)
	}

	// 准备要插入的数据
	data := map[string]interface{}{
		"name": "b",
	}

	// 将数据编码为 JSON
	buf, err := json.Marshal(data)
	if err != nil {
		log.Fatalf("Error encoding data: %s", err)
	}

	// 准备索引请求
	indexName := "test_2024"
	docID := "1" // 文档的唯一标识符
	r := strings.NewReader(string(buf))

	// 发送索引请求
	res, err := es.Index(indexName, r, es.Index.WithDocumentID(docID))
	if err != nil {
		log.Fatalf("Error indexing document: %s", err)
	}
	defer res.Body.Close()

	// 检查响应状态
	if res.IsError() {
		log.Fatalf("Error: %s", res.Status())
	}

	// 打印响应信息
	fmt.Println("Document indexed successfully.")

插入成功,并可正常查到

在这里插入图片描述

二、模拟磁盘使用量超过阈值

通过调整参数"cluster.routing.allocation.disk.watermark.flood_stage":“0.00000001”
实现数据量超过了阈值的情况。
然后继续向test_2024中插入一条数据(golang)

es, err := elasticsearch.NewClient(cfg)
	if err != nil {
		log.Fatalf("Error creating the client: %s", err)
	}

	// 准备要插入的数据
	data := map[string]interface{}{
		"name": "c",
	}

	// 将数据编码为 JSON
	buf, err := json.Marshal(data)
	if err != nil {
		log.Fatalf("Error encoding data: %s", err)
	}

	// 准备索引请求
	indexName := "test_2024"
	docID := "2" // 文档的唯一标识符
	r := strings.NewReader(string(buf))

	// 发送索引请求
	res, err := es.Index(indexName, r, es.Index.WithDocumentID(docID))
	if err != nil {
		log.Fatalf("Error indexing document: %s", err)
	}
	defer res.Body.Close()

	// 检查响应状态
	if res.IsError() {
		log.Fatalf("Error: %s", res.Status())
	}

	// 打印响应信息
	fmt.Println("Document indexed successfully.")

结果也符合预期,插入报错

在这里插入图片描述

虽然报错不清晰,但是好歹通过代码返回也能知道这条数据是没有插入成功的。
这里可以通过curl的方式插入一条数据测试,报错内容更加清晰

在这里插入图片描述
(磁盘使用量超过洪水水位线,索引已只读)

三、此时如果我们使用代码bulk的方式插入数据(golang)
	documents := []map[string]interface{}{
		{"name": "c"},
		{"name": "d"},
	}

	// 准备批量请求体
	var buf bytes.Buffer
	for _, doc := range documents {
		meta := map[string]interface{}{
			"index": map[string]interface{}{
				"_index": "test_2024", // 你的索引名称
			},
		}
		if err := json.NewEncoder(&buf).Encode(meta); err != nil {
			log.Fatalf("Error encoding meta data: %s", err)
		}
		if err := json.NewEncoder(&buf).Encode(doc); err != nil {
			log.Fatalf("Error encoding document: %s", err)
		}
	}

	// 发送批量请求
	req, err := http.NewRequest("POST", "http://xxx.xxx.xxx.xxx:9200/_bulk", &buf)
	if err != nil {
		log.Fatalf("Error creating bulk request: %s", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := http.DefaultClient
	res, err := client.Do(req)
	if err != nil {
		log.Fatalf("Error making bulk request: %s", err)
	}
	slurp, err := io.ReadAll(res.Body)
	if err != nil {
		log.Println(string(slurp))
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		log.Fatalf("Error response: %s", res.Status)
	} else {
		log.Printf("Successfully indexed %d documents", len(documents))
	}

结果惊人,居然提示插入成功

在这里插入图片描述

看下数据

还是只有一条,说明实际并没有插入成功

四、打断点对bulk进行调试

在这里插入图片描述

看到正常报错,该报错与用curl(单条或bulk)插入数据报错相同

在这里插入图片描述

结论

当业务需要定时向ES中插入10000条数据,那么肯定采用bulk的方式。
但是如果此时ES磁盘达到阈值,那么数据就无法正常写入,但是代码又不会报错(如实验三,bulk的sdk只会判断整个请求是否正常发送与返回,并没有针对bulk里面的每条数据是否插入成功来判断)

最终就造成了代码提示写入成功,但实际掉数据的情况。

彩蛋

如果通过代码层去解析bulk中每条数据,判断是否写入成功会大大影响写入效率,也就失去了bulk的意义。
最好还是对ES所在服务器的磁盘进行监测,同时监测值至少低于 cluster.routing.allocation.disk.watermark.flood_stage值10个百分点。

在这里插入图片描述

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 Elasticsearch 的 `_bulk` API 发送批量操作请求后,它将返回一个响应,其中包含每个操作的结果。响应的格式为 JSON,你可以解析它来获取有关每个操作的详细信息。 以下是一个示例 `_bulk` API 的响应结果: ```json { "took": 15, "errors": false, "items": [ { "index": { "_index": "myindex", "_type": "_doc", "_id": "1", "status": 200, "error": null } }, { "update": { "_index": "myindex", "_type": "_doc", "_id": "2", "status": 409, "error": { "type": "version_conflict_engine_exception", "reason": "Version conflict, document already exists (current version [1])" } } }, { "delete": { "_index": "myindex", "_type": "_doc", "_id": "3", "status": 200, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 } } } ] } ``` 在这个示例中,响应包含了三个操作的结果:一个索引操作、一个更新操作和一个删除操作。 - `took` 字段表示执行这个批量请求所花费的时间(以毫秒为单位)。 - `errors` 字段指示是否在批量请求中发生了错误。如果所有操作都成功,则为 `false`;如果至少有一个操作失败,则为 `true`。 - `items` 字段是一个数组,包含每个操作的结果。每个操作结果都是一个对象,其中包含了操作的类型(如 `index`、`update`、`delete` 等)和相应的元数据信息(如索引名称、文档 ID、状态码等)。 - 对于成功的操作,`status` 字段表示 HTTP 状态码,通常为 200。`error` 字段为 `null`。 - 对于失败的操作,`status` 字段可能表示错误的 HTTP 状态码,例如 409 表示版本冲突。`error` 字段包含了错误的详细信息,如错误类型和原因。 - 对于某些操作(如删除操作),还可能包含其他字段,如 `result` 表示操作的结果(如 "deleted" 表示删除成功),以及 `_shards` 字段表示操作在分片上的执行情况。 你可以根据需要解析这个响应,并处理每个操作的结果,以了解每个操作的成功与否,以及出错操作的具体错误信息。 希望这个解释对你有帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值