Closed flycash closed 6 months ago
所谓改为 DB 维度执行,就是现在我们生成的 Query 都是表维度的单一的 SQL,比如说:
qs := []Query{
Query{
SQL: "SELECT * FROM db_0.tab0 WHER ..."
},
Query{
SQL: "SELECT * FROM db_0.tab1 WHER ..."
},
}
而修改为 DB 维度之后,我们的目标 SQL 就变成了:
qs := []Query{
Query{
SQL: "SELECT * FROM db_0.tab0 WHER ...;SELECT * FROM db_0.tab1 WHER ..."
}
}
// DB represents a database
type DB struct {
core
ds datasource.DataSource
multiStatements bool
interpolateParams bool
}
func DBWithMultiStatements(m bool) DBOption {
return func(db *DB) {
db.multiStatements = m
}
}
func DBWithInterpolateParams(p bool) DBOption {
return func(db *DB) {
db.interpolateParams = p
}
}
func Open(driver string, dsn string, opts ...DBOption) (*DB, error) {
dl, err := dialect.Of(driver)
if err != nil {
return nil, err
}
orm := &DB{
core: core{
metaRegistry: model.NewMetaRegistry(),
dialect: dl,
// 可以设为默认,因为原本这里也有默认
valCreator: valuer.PrimitiveCreator{
Creator: valuer.NewUnsafeValue,
},
},
}
for _, o := range opts {
o(orm)
}
db, err := single.OpenDB(driver, dsn,
single.DBWithMultiStatements(orm.multiStatements),
single.DBWithInterpolateParams(orm.interpolateParams))
if err != nil {
return nil, err
}
orm.ds = db
return orm, nil
}
在最外层的 db 里需要指定这个参数,然后 在 single db 里还要再指定一次,通过最外层的DB 传给 single db
这里感觉设计和写法很差,还有其他的方案吗,如果是直接传参那更差了
// DB represents a database
type DB struct {
db *sql.DB
multiStatements bool
interpolateParams bool
}
type Option func(db *DB)
func DBWithMultiStatements(m bool) Option {
return func(db *DB) {
db.multiStatements = m
}
}
func DBWithInterpolateParams(p bool) Option {
return func(db *DB) {
db.interpolateParams = p
}
}
func OpenDB(driver string, dsn string, opts ...Option) (*DB, error) {
res := &DB{}
for _, opt := range opts {
opt(res)
}
if res.multiStatements {
dsn = dsn + "?multiStatements=true"
}
if res.interpolateParams {
dsn = dsn + "&interpolateParams=true"
}
db, err := sql.Open(driver, dsn)
if err != nil {
return nil, err
}
res.db = db
return res, nil
}
根据文档,multiStatements 要和 interpolateParams 参数一起用,多条sql语句才生效,明佬可以再次求证一下
OKKK,我现在担心的就是,怎么跟用户解释这个事情=。=
mark
OpenDB 里面不要帮用户拼接,因为问题就是你拼接了,但是大部分用户是不看源码的,所以他不懂。然后出问题了他就会很奇怪。 所以 OpenDB 里面就检测一下有没有 dsn 有咩有传入 multiplestatmes 和 interpolate 这两个参数。
if !strings.contains("multiStatements=true") {
return errors.New("分库分表下 dsn 必须传入 multiStatements=true 参数")
}
一个令人悲伤的事情是,Go sql driver 有一个 BUG,https://github.com/go-sql-driver/mysql/issues/314 。 这个 BUG 导致这么一个问题:如果你要在数据事务里面,发起查询之后,没有 close 掉结果集,那么再次发起查询的时候,就会报 buzy buffer 的错误
由事务衍生出来的 PrepareStatement 同样如此。
例如利用集成测试的数据库,运行这个测试就可以复现:
错误信息是:
在 sql SDK 不打算解决这个 BUG 的情况下,我们有两个可选的方案:
修改已有的结果集处理这个会引入非常严重的问题, 所以几乎不能选。
第二种方案是我之前就提到过的,它本身就属于性能优化的一点。因此现在我们需要做:
为了支持这种特性,用户必须指定 multiStatements 参数为 true。这就带来另外一层麻烦:
但是可以预见的是,multiStatements=true 肯定会带来很多困扰,也就是很多用户搞不明白为啥一定要有 multiStatements=true,要么是没读文档,要么是没读明白。