📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

为什么这个错误发生 #

retry_on_primary_exception 表示在主分片上执行的操作失败,需要在主分片上重试。这个异常通常在主分片状态发生变化时发生。

这个错误可能由以下原因引起:

  1. 主分片已迁移:操作执行时主分片已迁移到其他节点
  2. 主分片不再是主:原主分片降级为副本,新的主分片已选出
  3. 主分片恢复中:主分片正在恢复过程中,暂时无法处理写操作
  4. 并发版本冲突:存在并发操作导致主分片变更
  5. 分片重新分配:分片被重新分配到其他节点
  6. 节点故障转移:原主分片所在的节点故障,主分片已转移到其他节点

如何修复这个错误 #

1. 检查分片状态 #

# 查看分片分配状态
GET /_cat/shards?v&h=index,shard,prirep,state,node

# 查看特定分片
GET /_cluster/allocation/explain
{
  "index": "<index>",
  "shard": 0,
  "primary": true
}

2. 重试操作 #

客户端通常会自动重试,也可以手动重试:

# 对于索引操作
POST /<index>/_doc/<id>?retry_on_conflict=3
{
  "field": "value"
}

# 对于更新操作
POST /<index>/_update/<id>?retry_on_conflict=3
{
  "doc": {
    "field": "value"
  }
}

3. 使用路由 #

使用路由确保操作发送到正确的分片:

# 使用路由参数
POST /<index>/_doc/<id>?routing=<user_id>
{
  "field": "value"
}

4. 检查集群健康状态 #

# 等待集群稳定
GET /_cluster/health?wait_for_status=yellow&timeout=50s

# 查看未分配的分片
GET /_cat/shards?v | grep UNASSIGNED

5. 等待分片迁移完成 #

# 查看正在恢复的分片
GET /_cat/recovery?v

# 等待迁移完成
GET /_cluster/health?wait_for_no_relocating_shards=true&timeout=50s

6. 使用乐观并发控制 #

# 使用序列号进行条件更新
POST /<index>/_update/<id>
{
  "doc": {
    "field": "value"
  },
  "if_seq_no": 5,
  "if_primary_term": 1
}

7. 检查节点日志 #

# 查看主分片相关日志
grep -i "primary" /path/to/easysearch/logs/easysearch.log | tail -50

# 查看分片迁移日志
grep -i "relocation" /path/to/easysearch/logs/easysearch.log | tail -50

8. 调整副本设置 #

如果主分片频繁变更,可能是副本配置问题:

# 检查副本数量
GET /<index>/_settings?flat_settings=true

# 临时减少副本以稳定集群
PUT /<index>/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

# 等待稳定后再恢复副本
PUT /<index>/_settings
{
  "index": {
    "number_of_replicas": 1
  }
}

9. 检查分片分配规则 #

# 查看分配规则
GET /_cluster/settings?filter_path=**.routing.allocation

# 确保没有阻止分配的规则

10. 使用客户端重试机制 #

# Python 客户端配置重试
from elasticsearch import Elasticsearch, ElasticsearchException
from elasticsearch.helpers import bulk
import time

es = Elasticsearch(
    ["http://localhost:9200"],
    max_retries=3,
    retry_on_timeout=True,
    retry_on_status=[503, 504]  # Service Unavailable, Gateway Timeout
)

# 或使用指数退避
from elastic_transport import Retry

retry = Retry(
    limit=5,
    backoff_factor=1,
    status_forcelist=[503, 504]
)

预防措施 #

  • 配置合理的副本数以提高可用性
  • 避免在分片迁移时执行大量写操作
  • 实现客户端自动重试机制
  • 监控分片健康状态和迁移进度
  • 使用负载均衡器分发请求
  • 配置合理的超时时间
  • 避免在集群不稳定时执行关键操作
  • 使用乐观并发控制处理并发写操作