一、背景
在業務場景開發的過程中, 隨着數據量的增加,相同表結構不同表名的分表策略是常用的方案選擇之一。如下以golang做為后端業務開發,嘗試修改beego的orm庫做一個相同表結構不同表名的分表實現。
二、orm相同表結構不同表名的修改邏輯
三、orm分表對比
操 作 |
不分表代碼使用 | 分表代碼使用 |
寫 入 |
o := orm.NewOrm() user := User{Name: "slene"} // insert id, err := o.Insert(&user) |
o := orm.NewOrm() user := User{Name: "slene"} // set table name to `user_1` o.ShardingTable( func(tableName string) string { return tableName + "_1" }, ) // insert id, err := o.Insert(&user) |
更 新 |
o := orm.NewOrm() user := User{Name: "slene"} // update user.Name = "astaxie" num, err := o.Update(&user)
|
o := orm.NewOrm() user := User{Name: "slene"} // set table name to `user_1` o.ShardingTable( func(tableName string) string { return tableName + "_1" }, ) // update user.Name = "astaxie" num, err := o.Update(&user)
|
查 詢 |
o := orm.NewOrm() user := User{id: 1} // select o.QueryTable(user).One(&user) |
o := orm.NewOrm() user := User{id: 1} // set table name to `user_1` o.ShardingTable( func(tableName string) string { return tableName + "_1" }, ) // select o.QueryTable(user).Offset(offset).Limit(limit).One(&user) // select id, name from user_1 where id=1 limit 0,1 |
刪 除 |
o := orm.NewOrm() user := User{id: 1} // delete o.Delete(user) // delete from user_1 where id=1 |
o := orm.NewOrm() user := User{id: 1} // set table name to `user_1` o.ShardingTable( func(tableName string) string { return tableName + "_1" }, ) // delete o.Delete(user) // delete from user_1 where id=1 |
從如上的CURD中,我們可以看到如下一段實時修改表名的代碼
// set table name to `user_1` o.ShardingTable( func(tableName string) string { return tableName + "_1" }, )
我們一步步分析一下orm是如何實現表名的實時修改。
四、分表實現分析
4.1 修改代碼文件:https://github.com/gityf/orm/blob/master/orm/types.go
// Ormer define the orm interface
type Ormer interface {
// ...
// set sharding table in time for different table name with same struct.
// ormer.ShardingTable(
// func(tableName string) string {
// return tableName + "_" + tableNameSuffix
// },
// )
ShardingTable(func(string) string)
}
修改types.go的Ormer接口,增加分表的函數,參數是一個獲取分表的函數,即每一個分表的表名的設置是一個orm實例。通過orm.NewOrm()創建。
4.2 修改代碼文件:https://github.com/gityf/orm/blob/master/orm/orm.go
修改orm.go文件,給結構orm增加如下兩個分表相關的函數
type orm struct {
alias *alias
db dbQuerier
isTx bool
sharding func(string) string
shardingTable func(string) string
}
有了如上的sharding函數,我們只要在創建orm時,實時的設置這個sharding函數,通過函數獲取新表名。
在函數NewOrm中給新創建的
o := new(orm)
設置一個分表函數如下
// switch to another registered database driver by given name. func (o *orm) Using(name string) error { // ... if al, ok := dataBaseCache.get(name); ok { o.sharding = func(table string) string { if o.shardingTable == nil { return table } return o.shardingTable(table) } } // ... }
orm結構體有了分表函數的設置,我們需要給orm的成員dbQuerier增加分表函數如下
o.db = &DB{
RWMutex: al.DB.RWMutex,
DB: al.DB.DB,
stmtDecorators: al.DB.stmtDecorators,
sharding: o.sharding,
}
4.3 修改代碼文件:https://github.com/gityf/orm/blob/master/orm/db_alias.go
DB是dbQuerier接口的一個實現,對DB結構體的實現增加分表函數
func (d *DB) Sharding(table string) string {
return d.sharding(table)
}
4.4 修改代碼文件:https://github.com/gityf/orm/blob/master/orm/db.go
對db代碼文件中的dbBase的CURD中的sql生成時,增加如下實時修改表名的實現 q.Sharding(mi.table)
// create insert sql preparation statement object.
func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string, error) {
Q := d.ins.TableQuote()
// ...
query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s)", Q, q.Sharding(mi.table), Q, Q, columns, Q, qmarks)
// ...
}
其他的orm轉sql的通過dbQuerier的q.Sharding(mi.table)獲取表名
增加orm相同表結果不同表名的代碼實現:https://github.com/gityf/orm
祝玩的開心~