有哪些做ppt网站,营销官网,注册深圳公司新政策,福州做网站的公司电话背景 分表组件改造的背景#xff0c;我在这篇文章《gorm.io/sharding改造#xff1a;赋能单表#xff0c;灵活支持多分表策略#xff08;上#xff09;》中已经做了详细的介绍——这个组件不支持单表多个分表策略#xff0c;为了突破这个限制做的改造。
在上一篇文章中我在这篇文章《gorm.io/sharding改造赋能单表灵活支持多分表策略上》中已经做了详细的介绍——这个组件不支持单表多个分表策略为了突破这个限制做的改造。
在上一篇文章中我们讨论了注册的改造注册的改造修改逻辑比较简单但是上一篇文章中遗留了一个很重要的议题——在增删改查的实际业务操作中分表组件究竟如何精准地定位到对应的分表策略以确保业务逻辑的顺利执行这篇文章我们重点讨论这个逻辑。
源码解读
首先我们需要看一下当我们执行查询新增更新或是删除逻辑其执行流程是什么。比如这么一个查询。
err : db.Model(Order{}).Where(user_id ?, userID).Find(orders).Error
我们大概梳理一下其执行流程。
初始化查询 当我们执行查询 err : db.Model(Order{}).Where(user_id ?, userID).Find(orders).Error首先会通过 db.Model(Order{}) 初始化一个查询实例设置相关的模型信息。构建查询条件 接着通过 .Where(user_id ?, userID) 方法将查询条件 user_id ? 以及对应的参数 userID 添加到查询实例中。执行查询 调用 .Find(orders) 方法时开始执行查询流程。在 Find 方法中首先通过 db.getInstance() 获取数据库实例。然后检查是否存在查询条件如果有则构建 SQL 条件表达式并将其添加到查询语句中。设置查询结果的目标对象 dest即 orders。func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {tx db.getInstance()if len(conds) 0 {if exprs : tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) 0 {tx.Statement.AddClause(clause.Where{Exprs: exprs})}}tx.Statement.Dest destreturn tx.callbacks.Query().Execute(tx)
} 执行回调和处理 调用 tx.callbacks.Query().Execute(tx) 执行查询回调链。在 Execute 方法中会遍历并执行所有注册的查询前和查询后的回调函数。func (p *processor) Execute(db *DB) *DB {//省略其他代码逻辑
......
for _, f : range p.fns {f(db)}//省略其他代码逻辑
...... return db
} 分片和查询执行 最终调用 pool.QueryContext 方法根据上下文、SQL 查询语句和参数执行实际的数据库查询。在 QueryContext 方法中会调用 pool.sharding.resolve 方法解析并修改查询语句以处理数据库分片逻辑。resolve 方法解析 SQL 查询语句提取表名并根据表名获取相应的分片配置。根据分片配置可能会修改原始查询语句以适应分片策略。func (pool ConnPool) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) {var (curTime time.Now())//该方法根据传入的SQL查询及其参数和上下文信息动态地解析、修改并返回最终的分片 //查询、原始查询、目标表名以及可能出现的错误。_, stQuery, _, err : pool.sharding.resolve(query, args...)if err ! nil {return nil, err}// 省略......return rows, err
} func (s *Sharding) resolve(query string, args ...any) (ftQuery, stQuery, tableName string, err error) {ftQuery querystQuery queryif len(s.configs) 0 {return}expr, err : sqlparser.NewParser(strings.NewReader(query)).ParseStatement()if err ! nil {return ftQuery, stQuery, tableName, nil}// 省略......tableName table.Name.Namer, ok : s.configs[tableName]if !ok {return} // 省略......return
}返回结果 执行查询后将结果填充到目标对象 orders 中并返回查询结果或错误。
我们重点关注resolve方法这个方法包含了分表逻辑的处理逻辑r, ok : s.configs[tableName]获取对应表的分表策略。
通过上述代码的解析我们现在应该有了解决方案。原来的逻辑获取分表策略是根据表明获取的。那我们只要修改这个逻辑根据表名分表键名作为唯一键获取对应的分表策略就能实现我们的目标。
方案
接下来我们需要思考的是如何把分表键传进来呢
我一开始想的是通过解析query获取查询条件中的分表键。但是当我深入的看了这个逻辑之后发现这个设想不能实现因为value, id, keyFind, err s.nonInsertValue(r.ShardingKey, condition, args...)这个方法中获取查询条件的字段是在这个函数内部实现的不能保持一个统一的结构而且改造复杂度比较高。
context在go语言有着广泛的使用场景所以我想着通过context的方式把分表键传递进来。有了这个想法改造起来就很简单了。我们只需要resolve方法增加一个context的传参并且r, ok : s.configs[tableName]这个获取分表策略改成用表名从context中获取的分表键作为键来获取分表策略即可。 如此我们就实现了根据表名分表键获取对应分表策略的逻辑至此我们的改造任务完成。
案例
我目前也只是简单的测试了两种分表策略的场景仅仅只覆盖了查询和插入的场景。更复杂的场景还没有测试。诸如并发情况下的场景。
package testimport (contextfmttestingtimegorm.io/driver/mysqlgorm.io/gormgorm.io/sharding
)
var globalDB *gorm.DBtype Order struct {ID int64 gorm:primaryKeyOrderId string gorm:sharding:order_id // 指明 OrderId 是分片键UserID int64 gorm:sharding:user_idProductID int64OrderDate time.TimeOrderYear int
}
// 自定义 ShardingAlgorithm
func customShardingAlgorithm4(value any) (suffix string, err error) {if year, ok : value.(int); ok {return fmt.Sprintf(_%d, year), nil}return , fmt.Errorf(invalid order_date)
}func customShardingAlgorithmUserId(value any) (suffix string, err error) {if userId, ok : value.(int64); ok {return fmt.Sprintf(_%d, userId%4), nil}return , fmt.Errorf(invalid user_id)
}// customePrimaryKeyGeneratorFn 自定义主键生成函数
func customePrimaryKeyGeneratorFn(tableIdx int64) int64 {var id int64seqTableName : gorm_sharding_orders_id_seq // 序列表名db : globalDB// 使用事务来确保主键生成的原子性tx : db.Begin()defer func() {if r : recover(); r ! nil {tx.Rollback()}}()// 锁定序列表以确保并发安全可选取决于你的 MySQL 配置和并发级别// 注意在某些 MySQL 版本和配置中使用 LOCK TABLES 可能不是最佳选择// 这里仅作为示例实际应用中可能需要更精细的并发控制策略tx.Exec(LOCK TABLES seqTableName WRITE)// 查询当前的最大 IDtx.Raw(SELECT id FROM seqTableName ORDER BY id DESC LIMIT 1).Scan(id)// 更新序列表这里直接递增 1实际应用中可能需要更复杂的逻辑newID : id 1tx.Exec(INSERT INTO seqTableName (id) VALUES (?), newID) // 这里假设序列表允许插入任意 ID实际应用中可能需要其他机制来确保 ID 的唯一性和连续性// 释放锁定tx.Exec(UNLOCK TABLES)// 提交事务if err : tx.Commit().Error; err ! nil {panic(err) // 实际应用中应该使用更优雅的错误处理机制}return newID
}// Test_Gorm_Sharding 用于测试 Gorm Sharding 插件
func Test_Gorm_Sharding6(t *testing.T) {// 连接到 MySQL 数据库dsn : dev:xxxxtcp(ip:port)/sharding_db2?charsetutf8mb4parseTimeTruelocLocaldb, err : gorm.Open(mysql.New(mysql.Config{DSN: dsn,}), gorm.Config{})if err ! nil {panic(failed to connect database)}globalDB dbconfig1 : sharding.Config{ShardingKey: order_year,ShardingAlgorithm: customShardingAlgorithm4, // 使用自定义的分片算法//PrimaryKeyGenerator: sharding.PKMySQLSequence,PrimaryKeyGenerator: sharding.PKCustom,PrimaryKeyGeneratorFn: customePrimaryKeyGeneratorFn,}config2 : sharding.Config{ShardingKey: user_id,NumberOfShards: 4,ShardingAlgorithm: customShardingAlgorithmUserId, // 使用自定义的分片算法PrimaryKeyGenerator: sharding.PKSnowflake, // 使用 Snowflake 算法生成主键}mapConfig : make(map[string]sharding.Config)mapConfig[orders_order_year] config1mapConfig[orders_user_id] config2// 配置 Gorm Sharding 中间件使用自定义的分片算法middleware : sharding.RegisterWithKeys(mapConfig) // 逻辑表名为 ordersdb.Use(middleware)// 查询示例var orders []Orderctx, cancel : context.WithCancel(context.Background())defer cancel()ctx context.WithValue(ctx, sharding_key, order_year)db db.WithContext(ctx)err db.Model(Order{}).Where(order_year? and product_id?, 2025, 102).Find(orders).Errorif err ! nil {fmt.Println(Error querying orders:, err)}fmt.Printf(sharding key order_year Selected orders: %#v\n, orders)// 查询示例FindByUserID2(db, int64(1))// 示例插入订单数据InsertOrderByUserId(db)InsertOrderByOrderYear(db)
}func FindByUserID2(db *gorm.DB, userID int64) ([]Order, error) {var orders []Order// 查询示例ctx, cancel : context.WithCancel(context.Background())defer cancel()ctx context.WithValue(ctx, sharding_key, user_id)db db.WithContext(ctx)err : db.Model(Order{}).Where(user_id ?, userID).Find(orders).Errorif err ! nil {fmt.Println(Error querying orders:, err)}fmt.Printf(no sharding key user_id Selected orders: %#v\n, orders)return orders, err
}type OrderByUserId struct {ID int64 gorm:primaryKeyOrderId string gorm:sharding:order_id // 指明 OrderId 是分片键UserID int64 gorm:sharding:user_idProductID int64OrderDate time.Time
}func InsertOrderByUserId(db *gorm.DB) error {ctx, cancel : context.WithCancel(context.Background())defer cancel()ctx context.WithValue(ctx, sharding_key, user_id)db db.WithContext(ctx)// 示例插入订单数据order : OrderByUserId{OrderId: 20240101ORDER0001,UserID: 100,ProductID: 100,OrderDate: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),}err : db.Table(orders).Create(order).Errorif err ! nil {fmt.Println(Error creating order:, err)}order2 : OrderByUserId{OrderId: 20250101ORDER0001,UserID: 105,ProductID: 100,OrderDate: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),}err db.Table(orders).Create(order2).Errorif err ! nil {fmt.Println(Error creating order:, err)}return err
}func InsertOrderByOrderYear(db *gorm.DB) error {ctx, cancel : context.WithCancel(context.Background())defer cancel()ctx context.WithValue(ctx, sharding_key, order_year)db db.WithContext(ctx)orderYear : 2024// 示例插入订单数据order : Order{OrderId: 20240101ORDER0002,UserID: 1,ProductID: 100,OrderDate: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),OrderYear: orderYear,}err : db.Create(order).Errorif err ! nil {fmt.Println(Error creating order:, err)}orderYear 2025order2 : Order{OrderId: 20250101ORDER0002,UserID: 1,ProductID: 100,OrderDate: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),OrderYear: orderYear,}err db.Create(order2).Errorif err ! nil {fmt.Println(Error creating order:, err)}return err
}总结
通过改造gorm.io/sharding组件我们实现了根据表名分表键获取对应分表策略的逻辑。这一改造使得组件能够支持单表多个分表策略更加灵活和强大。目前我们已经简单测试了查询和插入场景更复杂的场景和并发情况还需进一步测试和优化。通过这一改造我们为业务逻辑的执行提供了更加精准和高效的分表策略定位。 文章转载自: http://www.morning.plydc.cn.gov.cn.plydc.cn http://www.morning.dxpqd.cn.gov.cn.dxpqd.cn http://www.morning.xdjsx.cn.gov.cn.xdjsx.cn http://www.morning.fnfxp.cn.gov.cn.fnfxp.cn http://www.morning.tfgkq.cn.gov.cn.tfgkq.cn http://www.morning.kjyhh.cn.gov.cn.kjyhh.cn http://www.morning.rpwht.cn.gov.cn.rpwht.cn http://www.morning.tnrdz.cn.gov.cn.tnrdz.cn http://www.morning.lkthj.cn.gov.cn.lkthj.cn http://www.morning.gfrtg.com.gov.cn.gfrtg.com http://www.morning.ktqtf.cn.gov.cn.ktqtf.cn http://www.morning.pwqyd.cn.gov.cn.pwqyd.cn http://www.morning.blznh.cn.gov.cn.blznh.cn http://www.morning.lfdmf.cn.gov.cn.lfdmf.cn http://www.morning.qzsmz.cn.gov.cn.qzsmz.cn http://www.morning.wjplr.cn.gov.cn.wjplr.cn http://www.morning.kpxky.cn.gov.cn.kpxky.cn http://www.morning.mkrjf.cn.gov.cn.mkrjf.cn http://www.morning.ydrn.cn.gov.cn.ydrn.cn http://www.morning.nqlx.cn.gov.cn.nqlx.cn http://www.morning.zrgx.cn.gov.cn.zrgx.cn http://www.morning.nhpgm.cn.gov.cn.nhpgm.cn http://www.morning.lwdzt.cn.gov.cn.lwdzt.cn http://www.morning.hgsylxs.com.gov.cn.hgsylxs.com http://www.morning.bmlcy.cn.gov.cn.bmlcy.cn http://www.morning.mlbn.cn.gov.cn.mlbn.cn http://www.morning.przc.cn.gov.cn.przc.cn http://www.morning.nnhrp.cn.gov.cn.nnhrp.cn http://www.morning.lqqqh.cn.gov.cn.lqqqh.cn http://www.morning.btblm.cn.gov.cn.btblm.cn http://www.morning.tphjl.cn.gov.cn.tphjl.cn http://www.morning.rswtz.cn.gov.cn.rswtz.cn http://www.morning.kyctc.cn.gov.cn.kyctc.cn http://www.morning.rxgnn.cn.gov.cn.rxgnn.cn http://www.morning.nchsz.cn.gov.cn.nchsz.cn http://www.morning.wptrm.cn.gov.cn.wptrm.cn http://www.morning.jfqqs.cn.gov.cn.jfqqs.cn http://www.morning.qgwpx.cn.gov.cn.qgwpx.cn http://www.morning.tntqr.cn.gov.cn.tntqr.cn http://www.morning.ahscrl.com.gov.cn.ahscrl.com http://www.morning.qbdqc.cn.gov.cn.qbdqc.cn http://www.morning.nmqdk.cn.gov.cn.nmqdk.cn http://www.morning.nfmtl.cn.gov.cn.nfmtl.cn http://www.morning.bwkzn.cn.gov.cn.bwkzn.cn http://www.morning.krdxz.cn.gov.cn.krdxz.cn http://www.morning.wphzr.cn.gov.cn.wphzr.cn http://www.morning.zdqsc.cn.gov.cn.zdqsc.cn http://www.morning.lwqst.cn.gov.cn.lwqst.cn http://www.morning.qrhh.cn.gov.cn.qrhh.cn http://www.morning.nzklw.cn.gov.cn.nzklw.cn http://www.morning.lkfhk.cn.gov.cn.lkfhk.cn http://www.morning.gjssk.cn.gov.cn.gjssk.cn http://www.morning.cnbdn.cn.gov.cn.cnbdn.cn http://www.morning.bkpbm.cn.gov.cn.bkpbm.cn http://www.morning.mqnbm.cn.gov.cn.mqnbm.cn http://www.morning.zlff.cn.gov.cn.zlff.cn http://www.morning.hyryq.cn.gov.cn.hyryq.cn http://www.morning.mfmbn.cn.gov.cn.mfmbn.cn http://www.morning.bwxph.cn.gov.cn.bwxph.cn http://www.morning.dpwcl.cn.gov.cn.dpwcl.cn http://www.morning.lpmdy.cn.gov.cn.lpmdy.cn http://www.morning.kkzwn.cn.gov.cn.kkzwn.cn http://www.morning.njhyk.cn.gov.cn.njhyk.cn http://www.morning.kxrhj.cn.gov.cn.kxrhj.cn http://www.morning.tdttz.cn.gov.cn.tdttz.cn http://www.morning.sqmbb.cn.gov.cn.sqmbb.cn http://www.morning.ljcf.cn.gov.cn.ljcf.cn http://www.morning.xqspn.cn.gov.cn.xqspn.cn http://www.morning.csnch.cn.gov.cn.csnch.cn http://www.morning.czgtt.cn.gov.cn.czgtt.cn http://www.morning.ptzf.cn.gov.cn.ptzf.cn http://www.morning.xskbr.cn.gov.cn.xskbr.cn http://www.morning.stph.cn.gov.cn.stph.cn http://www.morning.sphft.cn.gov.cn.sphft.cn http://www.morning.dtlnz.cn.gov.cn.dtlnz.cn http://www.morning.ppqzb.cn.gov.cn.ppqzb.cn http://www.morning.rnnwd.cn.gov.cn.rnnwd.cn http://www.morning.zrlms.cn.gov.cn.zrlms.cn http://www.morning.fsnhz.cn.gov.cn.fsnhz.cn http://www.morning.rlwcs.cn.gov.cn.rlwcs.cn