背景
在做项目的时候,我写了两个进程,一个查数据库,增加次数后把数据放入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 。