gorm的并发安全问题
最近负责一个小项目的后端,需要用到sql数据库,本着能偷懒就绝不多写一行代码的精神,我决定用gorm帮助我偷懒。
由于代码逻辑十分简单所以entity、dao等等各层写的不亦乐乎,但是多次高并发程序的开发经历告诉我事情没有这么简单,果然gorm并没有说自己对多线程操作保证安全…
下面是gorm关于并发安全的叙述
https://gorm.io/zh_CN/docs/method_chaining.html
链式方法
新建会话方法特指
gorm.DB.Session(&gorm.Session{})链式方法一般都是返回当前类一个实例指针的方法,比如gorm源码中这两句:
对
*DB 类型实例的调用返回了一个 *DB指针
如果我要对 Session方法的返回值调用 Create方法,
可以这么写:但是既然这Session返回了一个
*DB指针,那么可以这样调用这样写出的代码更优雅一些,但是
这不是本文章的重点,算是前置知识吧
gorm中并发安全的关键
gorm定义了一个
*DB 类型
这个类型里面有一个很重要的成员: DB.clonegorm中还有另一个函数,大多数的链式方法首先会执行下面这个函数
我们把这个函数的逻辑简化一下:
我们每对
*DB的方法进行一次调用都有可能对它*Statement字段进行修改,比如Limit方法修改了一次Statement( tx.Statement.AddClause(args) )Where方法也会修改
如果两次条件不同的查询都使用同一个
*DB,那么就会并发地对同一个Statement进行修改,就会出现安全问题,比如他们向同一个statement写入了不同查询条件,最终的结果几乎一定会出错。
因此问题的关键就在于, 对不同的查询要使用不同的
*DB,有一个很实用的方*DB.Session(), 如果传入的*gorm.Session{}结构体指针中NewDB字段为true,那么就会返回一个clone为1的新*DB,对这个*DB 的调用就不会影响到原来的*DB或者诸位可以自己查看各个链式方法和终结方法的源码,确定该方法对老的
*DB 会进行什么操作,只要能够保证不同的sql操作条件作用在不同的*DB 上就好了