27
2020
03

Python多进程时SQLAlchemy查询缓存引发的数据无法更新

背景

在做项目的时候,我写了两个进程,一个查数据库,增加次数后把数据放入redis

另一个进程读取redis,update数据库

比如:

a进程取出一个数据为次数为1,把这个次数加一然后放入redis

b进程取出redis,把这个次数update到数据库

接着继续,a进程此时取出来的次数应该是2,但是实际情况是取出来的还是1,加一后通过b进程入到数据库导致还是为2

经过搜索资料,发现这个结果跟SQLAlchemy查询缓存有关。

session.query(Alarms.id, Alarms.merge_total, Alarms.attack_count).filter(Alarms.merge_key_md5 == merge_key_md5).first()

当执行这个查询语句的时候,我没有进行commit,这样就会造成缓存问题,会导致在跑多进程的时候会出现数据不同步的情况,a进程对数据进行了修改,b进程获取的到的仍然是修改前的数据。

经过搜索的原因如下:

SQLAlchemy 带有对象缓存机制,在重复查询相同的对象时,直接先查询本地的缓存,而不需要从数据库加载数据。

而且SQLAlchemy并没有什么参数开关设置关闭缓存,那么一定是有深层次的原因。

原因的核心是InnoDB的事务隔离。

InnoDB 的默认隔离级别。它可以防止任何被查询的行被其他事务更改,从而阻止不可重复的读取,而不是 幻读取。它使用中度严格的锁定策略,以便事务内的所有查询都会查看同一快照中的数据,即数据在事务开始时的数据。

当创建查询事务时,事务一直没有进行更新,每次查询到的数据都是之前查询结果的快照,所有才会出现多进程时候数据不同步的情况。

解决方案

查询后更新事务

1、每次查询后进行commit操作

2、创建connect连接时,设autocommit=True,自动进行commit提交。下面是flask中的设置例子:

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'mysql://root:12333@127.0.0.1:3306/test?charset=utf8mb4&autocommit=true'

修改事务隔离级别

设置隔离级别命令 set [global/session] transaction isolation level xxxx;

如果使用global,则全局修改的是数据库的默认隔离级别。

关闭数据库的事务

修改表的存储引擎为MyISAM 。

« 上一篇 下一篇 »