
aws lambda 函数若在 handler 外部初始化数据库连接,会导致连接被复用并可能携带事务隔离、查询缓存或连接级状态(如未刷新的 mvcc 快照),从而读不到其他事务已提交的新数据。正确做法是每次调用在 handler 内创建新连接。
在 AWS Lambda 中,数据库连接不应在函数顶层(即 lambda_handler 外)初始化,而应在每次请求处理时动态建立。你遇到的“写入后立即读不到新数据”问题,根本原因在于 长生命周期的复用连接(reused connection)与 MySQL 的事务隔离机制(尤其是默认的 REPEATABLE READ)相互作用,导致 get_users Lambda 重用了某个旧连接——该连接在首次执行时已开启了一个事务快照,后续 SELECT 仍基于该快照(即使其他连接已 COMMIT),因此无法看到新插入的行。
? 为什么外部连接会导致延迟可见性?
- Lambda 容器可能被复用(warm start),外部定义的 conn 对象会在多次调用间持续存在;
- MySQL 默认隔离级别为 REPEATABLE READ,一个连接内开启的第一个 SELECT(或任何语句)会固定一个一致性读视图(consistent read view),后续查询在同一事务中均复用该快照;
- 即使 create_user 显式调用了 commit(),get_users 若复用的是一个尚未开启新事务的老连接,其 SELECT 仍可能落在旧快照中;
- 更隐蔽的是:某些 RDS 代理或连接池中间件(如 ProxySQL)也可能引入缓存或连接粘滞,但本例中根源是 Lambda 连接复用本身。
✅ 正确实践:连接生命周期绑定到单次调用
import pymysql
import db_utils
import sql # your SQL statements
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=True # 推荐:避免隐式事务,简化控制
)
try:
if event.get('httpMethod') == 'POST':
# create_user logic
user = parse_user_from_event(event)
with conn.cursor() as cursor:
cursor.execute(sql.INSERT_USER, (
user.first_name,
user.last_name,
user.birth_date,
user.sex,
user.firebase_id
))
return {'statusCode': 201, 'body': 'User created'}
elif event.get('httpMethod') == 'GET':
# get_users logic — now always sees latest committed data
with conn.cursor() as cursor:
cursor.execute(sql.GET_ALL_USERS)
users = cursor.fetchall()
return {'statusCode': 200, 'body': users}
finally:
conn.close() # ✅ 显式关闭,释放资源(虽 Lambda 销毁时也会回收,但显式更安全)⚠️ 补充注意事项
- 不要依赖 autocommit=False + 手动 commit() 在 Lambda 中:易遗漏或异常中断导致悬挂事务;推荐设 autocommit=True,除非需多语句原子事务;
- 连接数限制:RDS 实例有最大连接数(max_connections),需配合 Lambda 并发配额合理规划;可考虑使用 RDS Proxy 缓解连接风暴;
- 性能考量:新建连接约增加 50–200ms 延迟,但相比数据不一致,这是必要权衡;若对延迟极度敏感,可结合连接池(如 pymysql 不原生支持,可用 SQLAlchemy + QueuePool,但需谨慎管理生命周期);
- 验证是否修复:部署后,用 curl 连续调用 create_user → get_users,观察响应是否秒级一致(而非等待 10 分钟)。
通过将数据库连接移入 lambda_handler,你放弃了连接复用的微小性能收益,却换来了 ACID 合规性、调试确定性与业务逻辑的可预测性——这正是 Serverless 场景下“简单即可靠”的最佳体现。










