AWS Lambda Python调用MySQL RDS出现结果缓存问题?
问题分析与解决方案
你遇到的这个问题,核心原因是Lambda的容器重用机制结合全局数据库连接的使用导致的。
AWS确实推荐把数据库连接放在handler外部,这样能在Lambda容器被复用时重用连接,减少每次调用的连接建立开销。但这里的坑在于:当Lambda容器被保留时,后续调用会复用同一个全局conn对象,而MySQL默认的事务隔离级别是REPEATABLE READ——在这个隔离级别下,同一个数据库连接(会话)内的多次查询会读取到同一个事务快照,哪怕数据库里的数据已经更新了。也就是说,第一次调用时的查询会建立一个快照,后续复用连接的调用都会读取这个旧快照,直到连接被销毁(容器被回收,或者你重新保存Lambda触发冷启动)。
另外还有一种可能:如果你的连接曾经执行过写操作但没有提交事务,也会导致后续读操作一直读取事务内的旧数据。
解决方法
这里有几个可行的方案,你可以根据自己的需求选择:
1. 在handler内部创建并关闭连接(最简单直接)
虽然会损失一点连接复用的性能,但能彻底避免容器重用带来的缓存问题。修改代码如下:
import pymysql def lambda_handler(event, context): # 每次调用都新建连接 conn = pymysql.connect( host='your-rds-host', user='your-user', password='your-password', db='your-db' ) try: with conn.cursor() as cur: cur.execute("SELECT column FROM DB.table ORDER BY create_time DESC LIMIT 1;") row = cur.fetchone() return row[0] finally: # 确保连接关闭 conn.close()
2. 保留全局连接,但每次查询前重置事务状态
如果你想保留连接复用的优势,可以在每次查询前执行COMMIT(结束可能存在的未提交事务),或者修改会话的事务隔离级别为READ COMMITTED,这样每次查询都会读取最新的已提交数据:
import pymysql # 全局连接 conn = pymysql.connect( host='your-rds-host', user='your-user', password='your-password', db='your-db' ) def lambda_handler(event, context): # 先检查连接是否有效,避免RDS断开闲置连接 try: conn.ping(reconnect=True) except Exception as e: # 重新创建连接 conn = pymysql.connect( host='your-rds-host', user='your-user', password='your-password', db='your-db' ) with conn.cursor() as cur: # 修改事务隔离级别为READ COMMITTED cur.execute("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;") # 或者执行COMMIT来结束之前的事务 # cur.execute("COMMIT;") cur.execute("SELECT column FROM DB.table ORDER BY create_time DESC LIMIT 1;") row = cur.fetchone() return row[0]
3. 禁用MySQL的查询缓存(仅适用于MySQL 5.7及以下版本)
注意MySQL 8.0已经移除了查询缓存,但如果你用的是旧版本,可能查询缓存会导致重复返回相同结果。可以在查询语句后加SQL_NO_CACHE:
SELECT SQL_NO_CACHE column FROM DB.table ORDER BY create_time DESC LIMIT 1;
总结
最稳妥的方案是方案1,适合数据更新频繁、对实时性要求高的场景;如果对性能要求更高,方案2是更好的选择,既保留连接复用,又能确保读取最新数据。
内容的提问来源于stack exchange,提问作者NBajanca




