解决pymysql查不到最新数据的办法

2020-05-04  本文已影响0人  wintests

最近用 Flask 写了几个接口部署在服务器上,然后用 Pytest 来做测试,但遇到了问题,搞了大半天才把问题解决。

问题场景及原因

问题大概是这样的,我在本地环境用 Pytest 写代码来对服务器上 Flask 的接口进行测试,在测试删除接口的时候,第一步我在 Pytest 中会通过SQL插入数据到MySQL数据库,第二步再调删除接口完成删除用户。最后执行 Pytest 代码没跑通过,会返回用户不存在,但这个情况到数据库里查看,发现该用户实际是存在于MySQL中。

首先,这个接口是没啥大问题的,调注册接口或手工插入数据,再手工请求访问接口可以正常完成删除,只是我在 Pytest 代码中通过SQL插入数据会出现这个问题。

其次,怀疑自己 Pytest 代码写得有问题,但检查后发现没问题,并且加了查询操作发现能查回这个新插入的数据。

最后,在各种排查后,在 Flask 接口服务中,也加了查询操作,但发现压根没查回这个新插入的数据。

于是,感觉接口环境下通过 Python 连接 pymysql ,查询数据库时,获取到的似乎不是最新数据,所以调用户删除接口才会提示用户不存在。接着,在网上查了下资料,发现是事务的隔离级别导致的这个问题。

MySQL默认事务隔离级别是 REPEATABLE READ,当我在本地 Pytest 代码中利用SQL插入用户数据并提交 commit 后,接口环境下的事务A不会读取到我本地环境下的事务B更新的信息,即便事务B已提交,而事务A每次查询到的数据都是最开始创建事务查询到结果的快照,事务A一直没进行更新。

更详细的解释说明,可参考文末列出的网上资料。

问题代码

import pymysql

class MysqlDb():

    def __init__(self, host, port, user, passwd, db):
        # 建立数据库连接
        self.conn = pymysql.connect(
            host=host,
            port=port,
            user=user,
            passwd=passwd,
            db=db
        )
        # 通过 cursor() 创建游标对象,并让查询结果以字典格式输出
        self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor)

    def __del__(self): # 对象资源被释放时触发,在对象即将被删除时的最后操作
        # 关闭游标
        self.cur.close()
        # 关闭数据库连接
        self.conn.close()

    def select_db(self, sql):
        """查询"""
        # 检查连接是否断开,如果断开就进行重连
        self.conn.ping(reconnect=True)
        # 使用 execute() 执行sql
        self.cur.execute(sql)
        # 使用 fetchall() 获取查询结果
        data = self.cur.fetchall()
        return data

    def execute_db(self, sql):
        """更新/新增/删除"""
        try:
            # 检查连接是否断开,如果断开就进行重连
            self.conn.ping(reconnect=True)
            # 使用 execute() 执行sql
            self.cur.execute(sql)
            # 提交事务
            self.conn.commit()
        except Exception as e:
            print("操作出现错误:{}".format(e))
            # 回滚所有更改
            self.conn.rollback()

解决办法

根据以上说明,要解决当前这个问题,我们可以通过以下方式来处理。

方法1:修改MySQL的事务隔离级别
方法2:每次查询操作后,都进行 commit() 提交事务。
方法3:Python创建pymysql连接时,设置 autocommit=True ,即让其操作后自动提交事务。

在这里,我们最好在方法2和方法3任选一个方法来处理就行了,不建议去修改MySQL的事务隔离级别。

    def select_db(self, sql):
        """查询"""
        # 检查连接是否断开,如果断开就进行重连
        self.conn.ping(reconnect=True)
        # 使用 execute() 执行sql
        self.cur.execute(sql)
        # 使用 fetchall() 获取查询结果
        data = self.cur.fetchall()
        # 提交事务
        self.conn.commit()
        return data
    def __init__(self, host, port, user, passwd, db):
        # 建立数据库连接
        self.conn = pymysql.connect(
            host=host,
            port=port,
            user=user,
            passwd=passwd,
            db=db,
            autocommit=True
        )
        # 通过 cursor() 创建游标对象,并让查询结果以字典格式输出
        self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor)

使用以上方法后,再次测试,发现问题已顺利得到解决。

参考资料:
为什么pymysql重连后才能查到被其他地方修改的数据 pymysql缓存?
记一次pymysql查询不到表中最新插入的数据的问题

上一篇下一篇

猜你喜欢

热点阅读