Scrapy多进程写Elasticsearch遇TOO_MANY_REQUESTS只读块异常排查咨询
问题根因
你遇到的429报错不是写入并发太高触发的ES限流,报错里的cluster_block_exception是ES的磁盘保护机制触发的:ES检测到节点磁盘剩余空间低于默认阈值后,自动把所有索引标记为只读(仅允许删除,禁止写入),所有写入请求都会被拦截。
为什么df看磁盘没超却触发告警
- 你的ES跑在Docker容器里(日志里的
elasticsearch_1是Docker容器名),ES读取的是自身数据目录所在文件系统的可用空间,不是你在macOS下df看到的全分区通用值 - ES默认磁盘洪泛水位阈值是:磁盘使用率超95%,或剩余可用空间小于5GB。告警里显示ES节点实际可用空间只有3.8GB,低于5GB阈值,所以触发了只读保护。
- 你执行的
df输出里/dev/disk1s1(macOS系统数据分区)使用率已经到98%,剩余可用实际只有约5.7GB,扣掉Docker镜像、系统缓存占用的部分后,ES能用到的可用空间进一步降到3.8GB,符合告警触发条件。
解决步骤
1. 先临时解除索引只读状态
执行以下ES API命令,把所有被标记为只读的索引的限制去掉:
PUT /_all/_settings { "index.blocks.read_only_allow_delete": null }
注意:这只是临时操作,磁盘空间问题没解决的话,ES每30秒做一次磁盘检测,过不了多久会再次自动把索引设为只读。
2. 先解决磁盘空间不足的核心问题
优先清磁盘,不然调再多阈值都是临时方案:
- 清理Docker无用资源:执行
docker system prune -a删掉没用的镜像、停止的容器、悬空缓存,释放宿主机磁盘空间 - 清理ES冗余数据:删掉不需要的旧索引、过期的测试文档,直接释放ES数据目录的占用
- 给ES单独挂大容量数据盘:别让ES和系统盘、Docker系统镜像共用分区,避免其他程序占了空间挤占ES的可用额度
3. 小容量测试环境可调整磁盘水位阈值
如果是测试环境,ES部署的磁盘本身空间就不大(比如总共只有10GB左右),可以手动调低洪泛水位的剩余空间阈值,比如改成剩余2GB以下才触发告警:
临时调整(重启ES后失效)
直接执行集群配置修改API:
PUT _cluster/settings { "transient": { "cluster.routing.allocation.disk.watermark.flood_stage": "2gb", "cluster.routing.allocation.disk.watermark.high": "85%", "cluster.routing.allocation.disk.watermark.low": "80%" } }
永久调整
修改ES配置文件elasticsearch.yml,加以下内容,重启ES生效:
cluster.routing.allocation.disk.watermark.low: 80% cluster.routing.allocation.disk.watermark.high: 85% cluster.routing.allocation.disk.watermark.flood_stage: 2gb
别把阈值设得太低,不然磁盘真写满会导致ES节点崩溃、数据损坏。
4. 优化并发写入配置,支撑多进程爬虫的写入需求
等磁盘问题解决、索引只读限制解除后,再调配置提高写入承载力,避免后续真的触发并发限流:
ES端调整
- 适当增大写入队列容量:修改
elasticsearch.ymlthread_pool.write.queue_size: 1000 - 强制用批量写入:别每爬一条就调一次ES的单条
index接口,攒够100~500条文档再走_bulk接口批量提交,能减少90%以上的请求次数 - 调低索引刷新频率:把索引的
refresh_interval从默认的1s改成5s,减少索引刷新的IO开销PUT /你的索引名/_settings { "index": { "refresh_interval": "5s" } }
爬虫端调整
- 控制写入并发:别让所有爬虫进程同时往ES发请求,加个中间缓冲队列(比如用Redis),所有爬虫进程把要写的文档先扔到队列里,再用少量独立进程批量消费队列写ES,避免瞬间并发打满ES的写入线程池
- 开启客户端重试:ES客户端遇到429报错时做指数退避重试,别直接报错终止爬虫任务
内容的提问来源于stack exchange,提问作者omars
相关产品推荐
相关产品推荐

