You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动