You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shell脚本为JSON文件添加Elasticsearch索引耗时过长求优化

批量JSON转Elasticsearch Bulk格式的性能优化方案

问题背景

我们有大量JSON文件(单文件含20万条记录,总计5000个/80GB),需要转成Elasticsearch Bulk格式后导入。当前脚本逐行处理单文件耗时约30分钟,而导入仅需20秒,核心瓶颈在JSON转Bulk格式的处理环节,需优化该逻辑。

当前处理逻辑:逐行读取JSON,为每行生成一条索引元数据行,格式如下:

{"index":{"_id":$guid,"_index" : "demoindex", "_type" : "usage"}}
$row

核心瓶颈分析

  1. 进程开销过大:逐行循环中每次调用awk提取GUID,20万条记录会启动20万次awk进程,性能损耗极大。
  2. 磁盘IO频繁:每次处理一行都执行两次echo >>追加写入临时文件,磁盘小IO次数过多。

优化方案

方案一:用AWK一次性处理全文件(核心优化)

用AWK代替Shell逐行循环,全程仅启动一次AWK进程,批量生成Bulk格式并写入文件,效率提升数十倍。

替换原脚本中的while read循环部分:

# 替换原while read循环
echo "Parsing $feed"
awk -F '"' '{
    printf "{\"index\": {\"_index\": \"demoindex\", \"_id\": \"%s\", \"_type\": \"usage\"}}\n%s\n", $4, $0
}' "$feed" > "$FEED_TMP_DIR/$feed"

方案二:跳过临时文件,流式导入(进一步节省IO)

如果不需要保留转换后的Bulk文件,可直接通过管道将AWK输出传给curl,跳过磁盘写入步骤:

echo "Parsing and importing $feed"
awk -F '"' '{
    printf "{\"index\": {\"_index\": \"demoindex\", \"_id\": \"%s\", \"_type\": \"usage\"}}\n%s\n", $4, $0
}' "$feed" | curl -s -H "Content-Type: application/x-ndjson" -XPOST 'localhost:9200/demoindex/usage/_bulk?' --data-binary @- >> $FEEDER_LOG

方案三:并行处理多文件(进阶优化)

若服务器CPU、内存及Elasticsearch集群负载允许,可并行处理多个文件,进一步缩短总耗时。示例用xargs实现4个文件并行处理(可根据服务器性能调整-P后的数值):

cd $FEED_DIR
# 替换原for循环
ls -1 *.json | xargs -P 4 -I {} bash -c '
    feed="{}"
    echo "Processing $feed"
    awk -F '"'"'"'"'"'"'"'"'{
        printf "{\"index\": {\"_index\": \"demoindex\", \"_id\": \"%s\", \"_type\": \"usage\"}}\n%s\n", $4, $0
    }'"'"'"'"'"'"'"'"' "$feed" | curl -s -H "Content-Type: application/x-ndjson" -XPOST "localhost:9200/demoindex/usage/_bulk?" --data-binary @- >> '"$FEEDER_LOG"'
    echo "Completed $feed"
'

其他辅助优化

  1. 调整Elasticsearch索引参数:导入前关闭刷新间隔,减少索引开销,导入完成后恢复:
# 导入前设置
curl -XPUT -H 'Content-Type: application/json' 'localhost:9200/demoindex/_settings' -d '{
    "number_of_replicas": 0,
    "refresh_interval": "-1"
}'

# 所有文件导入完成后恢复
curl -XPUT -H 'Content-Type: application/json' 'localhost:9200/demoindex/_settings' -d '{
    "number_of_replicas": 1,  # 改为你原有的副本数
    "refresh_interval": "30s" # 改为你原有的刷新间隔
}'
  1. 安全遍历文件:替换for feed in $(ls -1 *.json)为for feed in *.json,避免文件名含特殊字符时出错。

优化后完整脚本示例

#!/bin/bash

FEED_DIR="/apps/elasticsearch/demo"
FEED_TMP_DIR="/apps/elasticsearch/demo/temp"
FEED_ARCHIVE="/apps/elasticsearch/demo/archive"
FEEDER_LOG="/apps/log/feeder/demofeeder.log"

SCRPT_HOME="/apps/bin/elasticsearch"

LOCKFILE=$SCRPT_HOME/system/demofeeder.lock

# 检查是否已有进程在运行
if test -f "$LOCKFILE"; then
    echo "demofeeder.sh is still running."
    echo "$LOCKFILE exists."
    echo "Exiting !!"
    exit 1;
fi

echo "Creating lock file: $LOCKFILE" > $FEEDER_LOG
touch $LOCKFILE

# 调整Elasticsearch参数以加速导入
echo -e "\n Setting index settings before indexing!\n" >> $FEEDER_LOG
curl -XPUT -H 'Content-Type: application/json' 'localhost:9200/demoindex/_settings' -d '{
    "number_of_replicas": 0,
    "refresh_interval": "-1"
}'

echo "Starting demoindex Feeding Data at "$(date -u)"." >> $FEEDER_LOG
echo "Starting demoindex Feeding Data at "$(date -u)"."

echo "Clearing Temp directory" >> $FEEDER_LOG
rm -f $FEED_TMP_DIR/*.json

cd $FEED_DIR
for feed in *.json
do
  echo "Parsing $feed"
  # 用AWK一次性处理全文件
  awk -F '"' '{
      printf "{\"index\": {\"_index\": \"demoindex\", \"_id\": \"%s\", \"_type\": \"usage\"}}\n%s\n", $4, $0
  }' "$feed" > "$FEED_TMP_DIR/$feed"

  echo "Loading parsed files into elasticsearch: $FEED_TMP_DIR/$feed"
  curl -s -H "Content-Type: application/x-ndjson" -XPOST 'localhost:9200/demoindex/usage/_bulk?' --data-binary "@$FEED_TMP_DIR/$feed" >> $FEEDER_LOG

  echo "Deleting parsed json: "$FEED_TMP_DIR/$feed""
  rm "$FEED_TMP_DIR/$feed"

  echo "Moving $feed to Archive Folder"
  mv $FEED_DIR/$feed $FEED_ARCHIVE/$feed
done

# 恢复Elasticsearch原参数
echo -e "\n Restoring index settings!\n" >> $FEEDER_LOG
curl -XPUT -H 'Content-Type: application/json' 'localhost:9200/demoindex/_settings' -d '{
    "number_of_replicas": 1,
    "refresh_interval": "30s"
}'

echo "Data Feeding demoindex Completed at "$(date -u)"." >> $FEEDER_LOG
echo "Removing lock file: $LOCKFILE" >> $FEEDER_LOG
rm -f $LOCKFILE
echo "Data Feeding demoindex Completed at "$(date -u)"."

内容的提问来源于stack exchange,提问作者user1398291

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 15:30:03