您现在的位置是:网站首页> 内容页

SQLAlchemy并发写入引发的思考

  • 英国威廉希尔官方网址
  • 2019-03-07
  • 403人已阅读
简介背景近期公司项目中加了一个积分机制,用户登录签到会获取登录积分,但会出现一种现象就是用户登录时会增加双倍积分,然后生成两个积分记录。此为问题 问题分析 项目采用微服务架构,下图为积分机

背景

近期公司项目中加了一个积分机制,用户登录签到会获取登录积分,但会出现一种现象就是用户登录时会增加双倍积分,然后生成两个积分记录。此为问题 

问题分析 

项目采用微服务架构,下图为积分机制流程

 

 worker通过分析日志记录从而判断用户当天积分是否增加,进而进行积分增加增添记录或者无操作。两个worker对积分数据库进行同时写入,造成积分双倍增加的情况,那问题找到了,就是对数据库并发写入的问题。解决方法,加锁 

锁 

共享锁 定义:共享锁就是允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有。举例:比如有一个房间,你和你的女朋友都有要是可以进入这个房间,这就是共享锁,这个房间是你跟你的女朋友共享的 互斥锁 定义:互斥锁,也称作独占锁,排它锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁。举例:就比如蹲厕所吧,同一时间一个茅坑由你一个人独占,这就叫做独占锁,互斥锁,其他人是不可以用的(当然,特殊情况除外)乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 当然,这次解决问题采用的是悲观锁,互斥锁  

SQLAlchemy 

项目是采用SQLAlchemy对mysql进行操作,题目是SQLAlchemy引发的思考,当然就少不了SQLAlchemy这个主角。SQLAlchemy 提供 with_for_update 函数 进行 锁的操作 .

session.Query(User).with_for_update().first()session.Query(User).with_for_update(read=True).first()

完整形式为:

with_for_update(read=False, nowait=False, of=None)

read是标识加互斥锁还是共享锁. 当为 True 时, 即 for share 的语句, 是共享锁. 多个事务可以获取共享锁, 互斥锁只能一个事务获取. 有"多个地方"都希望是"这段时间我获取的数据不能被修改, 我也不会改", 那么只能使用共享锁.nowait其它事务碰到锁, 是否不等待直接"报错".of指明上锁的表, 如果不指明, 则查询中涉及的所有表(行)都会加锁.

补充 

在用SQLAlchemy对数据库操作的过程中出现这样一个现象,当在session.query()时,如果之前有session.add()操作,即使尚未进行commit操作,在query时也会查询到这个尚未真正插入数据库的对象。这个有待探究 ,后续补充    

参考链接: 

https://blog.csdn.net/yanlinwang/article/details/41172697 http://www.codexiu.cn/python/SQLAlchemy%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/531/#toc26 

文章评论

Top