
aws lambda 函数若在 handler 外部复用数据库连接,会导致连接被容器重用并可能携带过期查询缓存或事务隔离状态,造成新写入数据无法被后续读取立即感知;正确做法是每次调用就在 handler 内新建连接,并配合显式 commit 与合理连接池配置。
这个问题表面是“读不到刚写的数据”,实则是 Lambda 执行环境复用 + 数据库连接生命周期管理不当 共同导致的典型一致性陷阱。
? 根本原因分析
Lambda 在冷启动后会创建一个执行环境(container),并在一定时间内复用该环境处理多个请求(即“热重用”)。当你把 pymysql.connect() 放在 handler 外部时:
- 连接对象 conn 成为模块级全局变量;
- 同一容器内多次调用 get_users 可能复用同一个 TCP 连接;
- MySQL 默认事务隔离级别为 REPEATABLE READ,且 pymysql 连接若未显式关闭,在复用时可能仍处于某个事务上下文中(即使你没手动 BEGIN);
- 更关键的是:RDS 的只读副本(如有)、DNS 缓存、连接层的查询结果缓存(如 ProxySQL 或某些中间件)或客户端连接自身的隐式状态,都可能导致 SELECT 返回陈旧快照。
虽然你的 create_user 显式调用了 commit(),但若 get_users 复用了旧连接,它可能:
- 仍在旧事务快照中(尤其当上一次操作未正常结束);
- 被路由到未同步完成的只读副本(若 RDS 配置了 Multi-AZ 读写分离);
- 受限于连接空闲超时或网络中间件的连接保持策略,导致状态不一致。
✅ 正确实践:连接即用即建
将数据库连接移入 lambda_handler 内部,确保每次调用都使用全新、干净、隔离的连接:
import pymysql
import json
from your_module import db_utils, sql
def lambda_handler(event, context):
conn_params = db_utils.db_connection_parameters()
# 每次调用新建连接,避免状态污染
conn = pymysql.connect(
host=conn_params['host'],
user=conn_params['username'],
password=conn_params['password'],
database=conn_params['name'],
cursorclass=pymysql.cursors.DictCursor,
autocommit=False, # 显式控制事务更安全
connect_timeout=5,
read_timeout=10,
write_timeout=10
)
try:
with conn.cursor() as cursor:
cursor.execute(sql.GET_ALL_USERS)
users = cursor.fetchall()
conn.commit() # 即使只读也建议 commit 确保事务结束
return {
'statusCode': 200,
'body': json.dumps({'users': users})
}
except Exception as e:
conn.rollback()
raise e
finally:
conn.close() # 务必显式关闭,释放资源⚙️ 进阶优化建议
- 启用 RDS Performance Insights:确认是否存在长事务、锁等待或复制延迟;
- 检查是否误用只读终端节点:get_users 若指向只读副本,需确认其同步延迟(可通过 SHOW SLAVE STATUS 或 CloudWatch ReplicaLag 指标验证);
- 考虑连接池(谨慎使用):如必须复用连接,推荐使用 pymysql 兼容的连接池(如 DBUtils.SteadyDB),但需严格设置 maxusage=1 和 closeable=True,并确保每次获取连接后重置状态;
- 添加简单健康检查:首次查询前执行 SELECT 1,快速探测连接有效性;
- 统一事务语义:对写操作始终 BEGIN → EXECUTE → COMMIT/ROLLBACK;读操作建议加 WITH FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE(如需强一致性)。
? 总结
Lambda 中永远不要在 handler 外维护长期存活的数据库连接。这不是性能优化,而是反模式——它用不可预测的状态换来了微乎其微的连接建立开销节省。现代 RDS 连接建立耗时通常










