多线程使用Boto3 S3 client写入文件时出现异常乱码问题
问题成因
- 线程不安全的并发写入:Python内置的文件对象
write方法未实现线程安全保障,你使用16个线程同时调用同一个文件对象的写入方法时,多个线程的写入字节会出现穿插拼接,原本的完整JSON结构被打断,就会产生乱码。该代码此前正常运行是因为过往元数据体积小、单次写入耗时极短,并发冲突概率极低,近期要么是元数据平均体积变大,要么是任务量上升,冲突概率提升后就暴露了该问题。 - 次要成因:如果存储桶近期新增了包含非UTF-8编码的自定义元数据,
json.dumps未做编码兼容时也可能产生乱码;如果写入的文件放在WSL2挂载的Windows NTFS分区(/mnt/c/这类路径),跨系统文件写入的缓冲同步异常也可能偶发乱码,不过该情况概率远低于线程安全问题。
你遇到的乱码正是多个线程写入内容穿插拼接后的典型表现:
unitrust1%0#Uuca extended validation rootq!
�j�r�4|WNV>y�);l���mM�5߮��$�yE����ވ]%|�dg
修复方案
推荐优先使用「线程仅处理S3请求、主线程统一写入」的无锁方案,性能和稳定性更高,修改后代码如下:
import boto3 import json import concurrent.futures s3_client = boto3.client("s3") MAX_WORKERS = 16 # 函数仅负责拉取元数据,不做写入操作 def get_metadata(payload): return s3_client.get_object(Bucket=payload[0], Key=payload[1])['Metadata'] if __name__ == '__main__': file_name = "你的文件路径" # 打开文件时明确指定UTF-8编码,避免WSL2默认编码兼容问题 with open(file_name, 'w+', encoding='utf-8', newline='') as file: with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: # 主线程迭代拉取结果,串行写入,完全避免并发冲突 for metadata in executor.map(get_metadata, payloads): file.write(json.dumps(metadata, ensure_ascii=False)+'\n')
如果不想修改原有逻辑,也可以通过加线程锁保证写入原子性,修改方案如下:
import boto3 import json import concurrent.futures import threading s3_client = boto3.client("s3") MAX_WORKERS = 16 # 全局写入锁 write_lock = threading.Lock() def write_metadata(payload): metadata = s3_client.get_object(Bucket=payload[0], Key=payload[1])['Metadata'] # 写入前加锁,保证单次写入操作不会被其他线程打断 with write_lock: file.write(json.dumps(metadata, ensure_ascii=False)+'\n') if __name__ == '__main__': file_name = "你的文件路径" with open(file_name, 'w+', encoding='utf-8', newline='') as file: with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: executor.map(write_metadata, payloads)
额外优化建议:如果文件需要同步到Windows分区,优先写入到WSL2原生Ext4路径(如/home/下),写完后再拷贝到挂载的Windows分区,避免跨文件系统的缓冲异常。
内容的提问来源于stack exchange,提问作者Igor Tavares
相关产品推荐
相关产品推荐

