本文目录导读:

- 方案一:基于文件的统一存储(最简单,适合单机)
- 方案二:基于数据库的统一存储(适合复杂结构、需要查询)
- 方案三:基于消息队列 + 消费者(适合高并发、分布式)
- 方案四:基于外部 API / 微服务(最灵活,适合跨语言)
- 选择建议表
- 最佳实践(通用配置建议)
针对“脚本如何统一存储写入数据”这个问题,核心需求通常是为了避免并发冲突、简化日志/结果收集以及便于外部系统消费。
根据你的脚本类型(Shell、Python、批处理)和应用场景,这里有几种主流的统一存储方案:
基于文件的统一存储(最简单,适合单机)
核心思路:所有脚本都向同一个文件(或同一类前缀的文件)追加写入,通过文件锁来避免并发写入混乱。
推荐工具:flock (Linux) 或 文件句柄复用。
-
Shell 脚本示例:
#!/bin/bash OUTPUT_FILE="/var/log/scripts_unified_result/output.csv" # 使用 flock 实现原子写入 ( flock -x 200 # 获取排他锁 echo "$(date '+%Y-%m-%d %H:%M:%S'),$JOB_NAME,$RESULT" >> "$OUTPUT_FILE" ) 200>"$OUTPUT_FILE.lock" -
Python 脚本示例:
import filelock import json def write_unified(data: dict): lock_path = '/tmp/unified_store.lock' store_path = '/tmp/unified_store.jsonl' # 使用 JSON Lines 格式 lock = filelock.FileLock(lock_path) with lock: with open(store_path, 'a') as f: f.write(json.dumps(data) + '\n')
适用场景:日志汇总、简单任务结果收集、单机多进程脚本。
基于数据库的统一存储(适合复杂结构、需要查询)
核心思路:所有脚本连接同一个数据库(MySQL、PostgreSQL、SQLite),通过数据库的事务(ACID)机制保证数据一致性。
-
推荐实践:使用队列表或流水表。
-
Python 示例 (使用 SQLite + 连接池):
import sqlite3 from contextlib import closing # 初始化 conn = sqlite3.connect('/data/unified.db', check_same_thread=False) def insert_data(table: str, data: dict): with closing(conn.cursor()) as cur: # 使用参数化查询防止注入 cur.execute(f"INSERT INTO {table} (column1, column2) VALUES (?, ?)", (data['key1'], data['key2'])) conn.commit()
适用场景:需要后续分析、查询、报表生成;数据字段结构复杂且经常变化。
基于消息队列 + 消费者(适合高并发、分布式)
核心思路:脚本只负责把数据推到消息队列(Redis List、Kafka、RabbitMQ),由后台一个消费者进程统一处理写入数据库或文件。
优点:解耦、缓冲、削峰填谷。
典型架构:
[脚本A] -> Redis List -> [统一写入消费者] -> 数据库/文件 [脚本B] -> Redis List -> [统一写入消费者] -> 数据库/文件 [脚本C] -> Redis List -> [统一写入消费者] -> 数据库/文件
-
Python 示例 (Producer 端):
import redis r = redis.Redis(host='localhost', port=6379) # 脚本只需 push,不关心写入 r.lpush('data_pipeline', json.dumps({"script": "A", "data": "value"})) -
Consumer 端:
while True: msg = r.brpop('data_pipeline') # 阻塞直到有数据 data = json.loads(msg[1]) # 在这里统一写入数据库或文件(支持重试、去重) save_to_db(data)
适用场景:大规模分布式脚本系统、微服务架构、需要流量控制。
基于外部 API / 微服务(最灵活,适合跨语言)
核心思路:运行一个“数据写入微服务”(API Server),所有脚本通过 HTTP/gRPC 调用此 API 写入数据。
优点:语言无关、权限集中管理、可扩展。
-
API Server 示例 (Flask):
from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/log', methods=['POST']) def unified_log(): data = request.json source = data.get('source_script') value = data.get('value') # 统一写入核心逻辑 with open('log.txt', 'a') as f: f.write(f"{source}:{value}\n") return jsonify({"status": "ok"}), 201
调用方式:
- Shell:
curl -X POST -H "Content-Type: application/json" -d '{"source":"script1","value":"done"}' http://localhost:5000/log - Python:
requests.post('http://localhost:5000/log', json=...)
适用场景:脚本语言混杂(Bash、Python、PowerShell、Node.js)、需要统一鉴权/限流。
选择建议表
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单机、简单数据、偶尔运行 | 方案一(文件 + flock) | 零依赖,部署简单 |
| 单机、结构复杂、需要查询 | 方案二(SQLite) | 支持 SQL,无需安装数据库服务 |
| 多机、脚本量级大 | 方案三(Redis/MQ) | 解耦,防止数据库被打死 |
| 多语言、多团队 | 方案四(API) | 接口统一,便于管理和审计 |
| 需要实时分析、流处理 | 方案三(Kafka) | 高吞吐、持久化、可回溯消费 |
最佳实践(通用配置建议)
- 统一时间格式:建议统一使用 ISO 8601 格式(
2024-05-20T10:30:00Z),避免时区混乱。 - 增加版本号:在写入的数据前面前置一个字段(如
"ver": "1.0"),方便后续格式变更。 - 错误处理:每个写入操作都应该有
try...except或|| true,避免因为存储本身问题导致脚本失败。 - 数据分片/轮转:如果是文件存储,可以按天生成文件(
output_20240520.log),避免单文件过大。
如果你能提供具体的脚本语言(如 Bash、PowerShell、Python)和部署环境(单机、Kubernetes、Crontab 集群),我可以给出更精确的代码示例。