FastAPI流式返回Aurora百万级查询结果过慢的优化咨询
问题描述
我们在Fargate集群中部署了一个FastAPI服务(配置16vCPU、32GB内存),该服务从拥有数百万条记录的Aurora单表查询数据。核心接口代码如下:
@trade_router.get('/trades') def read_item(symbol: str, db: Session = Depends(get_db)): with db as session: trades = session.execute(text("SELECT * FROM trades WHERE symbol = :symbol"), {"symbol": symbol.upper()}) def iter_trades(): for trade in trades: # send as csv yield f"{trade.trade_date}, {trade.symbol}, {trade.price}, {trade.quantity}\n" return StreamingResponse(iter_trades(), media_type="application/json")
当前遇到的性能问题:
- 数据库直接执行相同查询仅需约7秒
- 通过接口返回700k条记录时,全部数据返回耗时长达2分钟
- 客户端(C# .Net桌面应用、浏览器、Postman、curl)调用性能一致,均需约2分钟
我们有以下疑问:
- Python和FastAPI是否为这类场景的合适解决方案?
- 如何优化接口的返回速度?
- 是否需要通过物化视图、按
symbol分区等方式让数据库完成更多处理? - 参考建议提到“不应从FastAPI或任何Web服务器发起700k行的数据库请求”,该如何调整架构?
优化方案
1. 修复响应媒体类型错误
代码中实际返回CSV格式数据,但设置的media_type="application/json"会导致客户端/中间件做无效的格式解析,增加额外开销。应改为:
return StreamingResponse(iter_trades(), media_type="text/csv")
2. 优化数据库结果读取方式
当前代码中,session.execute默认会把结果集全部加载到内存(客户端游标),700k条数据会占用大量内存并增加IO时间。建议使用服务器端游标(Server-Side Cursor),让数据库分批返回数据:
with db as session: # 针对SQLAlchemy,设置stream_results=True启用服务器端游标 trades = session.execute( text("SELECT * FROM trades WHERE symbol = :symbol"), {"symbol": symbol.upper()}, stream_results=True )
如果使用PostgreSQL兼容的Aurora,还可以配合fetchmany(size=1000)批量读取,减少迭代时的数据库交互次数。
3. 减少Python字符串拼接开销
700k次循环中的字符串格式化(f-string)累计开销极大,建议使用标准库csv模块批量生成CSV内容,提升效率:
import csv from io import StringIO @trade_router.get('/trades') def read_item(symbol: str, db: Session = Depends(get_db)): def iter_trades(): output = StringIO() writer = csv.writer(output) # 写入表头 writer.writerow(["trade_date", "symbol", "price", "quantity"]) with db as session: trades = session.execute( text("SELECT trade_date, symbol, price, quantity FROM trades WHERE symbol = :symbol"), {"symbol": symbol.upper()}, stream_results=True ) for trade in trades: writer.writerow(trade) # 输出当前缓冲区内容并清空 output.seek(0) yield output.read() output.seek(0) output.truncate() return StreamingResponse(iter_trades(), media_type="text/csv")
或者直接按批次拼接字符串,减少IO操作次数。
4. 数据库层面优化
- 添加索引:确保
symbol字段存在索引,进一步缩短数据库查询时间(即使当前查询7秒,索引能让过滤更高效):CREATE INDEX idx_trades_symbol ON trades(symbol); - 按
symbol分区:如果查询几乎都是按symbol过滤,分区能让数据库仅扫描目标分区,大幅减少磁盘IO:-- 示例:按symbol列表分区(根据实际symbol类型选择分区方式) ALTER TABLE trades PARTITION BY LIST (symbol); - 物化视图:如果数据更新频率低,可预先生成每个
symbol的物化视图,查询直接读取预计算结果:CREATE MATERIALIZED VIEW mv_trades_symbol_xxx AS SELECT trade_date, symbol, price, quantity FROM trades WHERE symbol = 'XXX'; -- 定期刷新视图 REFRESH MATERIALIZED VIEW mv_trades_symbol_xxx;
5. 调整架构:避免Web服务传输大结果集
参考建议提到的“不应从Web服务器发起700k行请求”,可调整架构:
- 直接从数据库导出到对象存储:让Aurora把查询结果导出到S3,然后接口返回S3的临时下载链接,客户端直接从S3下载(速度远快于Web服务转发)。
- 分页查询:如果业务允许,让客户端分批次请求数据(比如每次10k条),避免单次传输大量数据。
关于Python和FastAPI的适用性
Python和FastAPI完全适用于这类场景,当前的性能瓶颈并非框架本身,而是代码实现细节(媒体类型错误、客户端游标、字符串拼接开销)和数据传输方式导致的。通过上述优化,性能可大幅接近数据库直接查询的水平。
内容的提问来源于stack exchange,提问作者rmcsharry
相关产品推荐
相关产品推荐

