aiofiles.open写文件更慢是因为默认无缓冲,每次write都触发系统调用,且底层依赖线程池而非真正的异步I/O;提升吞吐关键在减少I/O次数、合理限流并发,并根据场景选择同步buffering或asyncio.to_thread。
因为 aiofiles.open 默认不缓冲,每次 write() 都触发一次系统调用(即使内容很小),在高并发场景下会放大事件循环调度开销。真正提升吞吐的关键不是“用异步函数”,而是减少 I/O 次数 + 合理控制协程并发量。
open() + buffering=8192 通常比 aiofiles 更快aiofiles,但必须配合 asyncio.Semaphore 限流,否则可能耗尽系统文件描述符或触发 OSError: Too many open files
aiofiles 底层仍依赖线程池(loop.run_in_executor)模拟异步,不是真正的异步 I/O —— 这点常被忽略核心是用 asyncio.Semaphore 控制同时打开的文件数,避免资源打满。示例中限制为 10 个并发写操作:
import asyncio import aiofiles import ossem = asyncio.Semaphore(10) # 控制最大并发写入数
async def write_file(filename: str, content: str): async with sem: # 等待许可 try: async with aiofiles.open(filename, "w") as f: await f.write(content) except OSError as e: if e.errno == 24: # Too many open files print(f"警告:{filename} 写入失败 - {e}") raise
启动 100 个写任务
tasks = [writefile(f"out{i}.txt", f"data_{i}") for i in range(100)] await asyncio.gather(*tasks, return_exceptions=True)
async with sem,尤其在循环中启动大量任务时return_exceptions=True 防止一个失败导致整个 gather 中断aiofiles.open(..., "w") 不支持 buffering 参数,无法像同步那样调优缓冲区要。如果单次 write() 传入几 MB 的字符串,aiofiles 会把它整个加载进内存再交由线程池处理,容易引发内存峰值。建议按 64KB~1MB 分块写入:
async def write_large_content(filename: str, content: str, chunk_size: int = 1024*64):
async with aiofiles.open(filename, "w") as f:
for i in range(0, len(content), chunk_size):
chunk = content[i:i + chunk_size]
await f.write(chunk)
async for line in ... 流式写
mmap 或 shutil.copyfileobj,异步在此场景无优势有,但取决于场景。纯文本批量写入时,asyncio.to_thread + 同步 open 往往更稳、更易调试:
import asyncioasync def sync_write_in_thread(filename: str, content: str): def _write(): with open(filename, "w", buffering=8192) as f: f.write(content) await asyncio.to_thread(_write)
to_thread(Python 3.9+)比 aiofiles 更轻量,且能利用 buffering 和 newline 等底层优化aiofiles,不必强行替换;但新项目遇到写性能瓶颈,先测 to_thread 版本uvloop + libuv 绑定方案,而非纯 Python 层的 aiofiles
实际压测中,多数 Web 日志或批处理写入场景,瓶颈不在「是否异步」,而在磁盘随机写延迟和 OS 文件缓存策略。别过早优化 aiofiles 调用本身,先确认是不是真的卡在写文件上。