Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在多协程情况下,gorose.IOrm 对象会存在脏数据,导致sql报错 #122

Closed
laitianxu opened this issue May 19, 2020 · 9 comments

Comments

@laitianxu
Copy link

代码如下
`func main() {
wg = sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go data(1)
}
wg.Wait()

}

func data(id int) {
db := dbUtil.DB().Table("tablename").Where("id", id)
_, err := db.Get()
if err != nil{
fmt.Println(err)
fmt.Println(db.LastSql())
}
wg.Done()
}`
控制台结果如下:
sql: expected 1 arguments, got 2
SELECT * FROM hp_colony WHERE id = ?, [1 1]
sql: expected 1 arguments, got 0
SELECT * FROM hp_colony WHERE id = ?, []
sql: expected 1 arguments, got 2
SELECT * FROM hp_colony WHERE id = ?, [1 1]
sql: expected 1 arguments, got 2
SELECT * FROM hp_colony WHERE id = ?, [1 1]
sql: expected 1 arguments, got 0
SELECT * FROM hp_colony WHERE id = ?, []
sql: expected 1 arguments, got 2
SELECT * FROM hp_colony WHERE id = ?, [1 1]

分析:多协程情况下导致多个协程从连接池中拿到的 gorose.IOrm 会相互影响,导致sql错乱,具体问题原因待分析源码

@laitianxu
Copy link
Author

是否提供有可以重置该对象的方法

@fizzday
Copy link
Member

fizzday commented Jun 1, 2020

使用姿势不对, goroutine或者任何其他情况, 都要避免重复使用db对象.
这是为了上下文做的一个取舍
当然也可以调用 db.Reset()来重置

@laitianxu
Copy link
Author

使用姿势不对, goroutine或者任何其他情况, 都要避免重复使用db对象.
这是为了上下文做的一个取舍
当然也可以调用 db.Reset()来重置

重置了也不行,这个是框架的问题

@fizzday
Copy link
Member

fizzday commented Jun 2, 2020

@laitianxu dbUtil.DB()这个方法贴出来,我看下就知道了

@fwhezfwhez
Copy link

@laitianxu db绑定了一个Ormapi,里面会存储where,table等参数,并且使用的是*Orm,跨协程复用db,就会对同一个对象修改,一定会脏。如果使用Reset,那也是其他的协程也会受影响,因为全是修改的同一个对象的值。

每个协程,都使用一个新的NewOrm()产生的db,就不会有你说的问题了,不过代价就是,每个协程里的db,都绑定了一个session连接。

换个思路,只需要提供一个Clone方法,大概是:

func (dba Orm) Clone() IOrm{
    orm:=new(Orm)
    orm.SetISession(dba.GetISession)
    orm.OrmApi = new(OrmApi)
    return orm
}

这样,对跨协程,就不会干扰了,因为使用了新的OrmApi来分析链式参数,并且数据库连接和之前的db share了。

不知道这样可不可行 @fizzday

@laitianxu
Copy link
Author

使用姿势不对, goroutine或者任何其他情况, 都要避免重复使用db对象.
这是为了上下文做的一个取舍
当然也可以调用 db.Reset()来重置

var engin *gorose.Engin
func DB() gorose.IOrm {
orm := engin.NewOrm()
return orm
}
就是调用了框架的方法,没有特别的,所以没贴

@leeyisoft
Copy link

leeyisoft commented Sep 2, 2021

我是这么解决问题,有效避免了你说的问题

package lib

import (
	_ "github.com/go-sql-driver/mysql"
	"github.com/gohouse/gorose/v2"
	"sync"
)

type SDBModule struct {
	sync.RWMutex
	Map map[string]*gorose.Engin
}

func (sdb *SDBModule) readMap(module string) *gorose.Engin {
	sdb.RLock()
	eg, _ := sdb.Map[module]
	sdb.RUnlock()
	return eg
}

func (sdb *SDBModule) writeMap(module string, engin *gorose.Engin) {
	sdb.Lock()
	sdb.Map[module] = engin
	sdb.Unlock()
}

var dbModule *SDBModule

func init() {
	dbModule = &SDBModule{
		Map: make(map[string]*gorose.Engin),
	}
}

func DB(module string) gorose.IOrm {
	engin := dbModule.readMap(module)
	if engin != nil {
		return engin.NewOrm()
	}

	driver := GetString("database." + module + ".driver")
	if driver == "" {
		driver = "mysql"
	}
	dsn := GetString("database." + module + ".dsn")
	//fmt.Println("dsn: ", dsn)
	setMaxOpenConns := GetInt("database." + module + ".setMaxOpenConns")
	setMaxIdleConns := GetInt("database." + module + ".setMaxIdleConns")

	engin, err := gorose.Open(&gorose.Config{
		Driver:          driver,
		Dsn:             dsn,
		SetMaxOpenConns: setMaxOpenConns,
		SetMaxIdleConns: setMaxIdleConns,
	})
	if err != nil {
		Logger.Error("db/module err:", err, ", driver: ", driver, ", dsn: ", dsn, ", module: ", module, ", ")
	}

	//engin.SetLogger(Logger)
	go engin.Ping()
	dbModule.writeMap(module, engin)
	return engin.NewOrm()
}

@musicsnap
Copy link

协程下多个查询也出现同样的问题

@LazyNeo
Copy link
Contributor

LazyNeo commented Aug 8, 2022 via email

@fizzday fizzday closed this as completed Mar 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants