黄东旭解析 TiDB 的核心优势
1242
2023-06-20
本文将展示如何使用 TiDB 和 Golang 来构造一个简单的 CRUD 应用程序。
本节将介绍 TiDB 集群的启动方法。
TiDB Cloud
本地集群
Gitpod
创建 TiDB Serverless 集群。
第 2 步:获取代码
git clone https://github.com/pingcap-inc/tidb-example-golang.git
使用 GORM(推荐)
使用 go-sql-driver/mysql
当前开源比较流行的 Golang ORM 为 GORM,此处将以 v1.23.5 版本进行说明。
封装一个用于适配 TiDB 事务的工具包 util,编写以下代码备用:
package utilimport ( "gorm.io/gorm")// TiDBGormBegin start a TiDB and Gorm transaction as a block. If no error is returned, the transaction will be committed. Otherwise, the transaction will be rolled back.func TiDBGormBegin(db *gorm.DB, pessimistic bool, fc func(tx *gorm.DB) error) (err error) { session := db.Session(&gorm.Session{}) if session.Error != nil { return session.Error } if pessimistic { session = session.Exec("set @@tidb_txn_mode=pessimistic") } else { session = session.Exec("set @@tidb_txn_mode=optimistic") } if session.Error != nil { return session.Error } return session.Transaction(fc) }
进入目录 gorm
:
cd gorm
目录结构如下所示:
. ├── Makefile ├── go.mod├── go.sum└── gorm.go
其中,gorm.go
是 gorm
这个示例程序的主体。使用 gorm 时,相较于 go-sql-driver/mysql,gorm 屏蔽了创建数据库连接时,不同数据库差异的细节,其还封装了大量的操作,如 AutoMigrate、基本对象的 CRUD 等,极大的简化了代码量。
Player
是数据结构体,为数据库表在程序内的映射。Player
的每个属性都对应着 player
表的一个字段。相较于 go-sql-driver/mysql,gorm 的 Player
数据结构体为了给 gorm 提供更多的信息,加入了形如 `gorm:"primaryKey;type:VARCHAR(36);column:id"`
的注解,用来指示映射关系。
package mainimport ( "fmt" "math/rand" "github.com/google/uuid" "github.com/pingcap-inc/tidb-example-golang/util" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/logger")type Player struct { ID string `gorm:"primaryKey;type:VARCHAR(36);column:id"` Coins int `gorm:"column:coins"` Goods int `gorm:"column:goods"`}func (*Player) TableName() string { return "player"}func main() { // 1. Configure the example database connection. db := createDB() // AutoMigrate for player table db.AutoMigrate(&Player{}) // 2. Run some simple examples. simpleExample(db) // 3. Explore more. tradeExample(db) }func tradeExample(db *gorm.DB) { // Player 1: id is "1", has only 100 coins. // Player 2: id is "2", has 114514 coins, and 20 goods. player1 := &Player{ID: "1", Coins: 100} player2 := &Player{ID: "2", Coins: 114514, Goods: 20} // Create two players "by hand", using the INSERT statement on the backend. db.Clauses(clause.OnConflict{UpdateAll: true}).Create(player1) db.Clauses(clause.OnConflict{UpdateAll: true}).Create(player2) // Player 1 wants to buy 10 goods from player 2. // It will cost 500 coins, but player 1 cannot afford it. fmt.Println("\nbuyGoods:\n => this trade will fail") if err := buyGoods(db, player2.ID, player1.ID, 10, 500); err == nil { panic("there shouldn't be success") } // So player 1 has to reduce the incoming quantity to two. fmt.Println("\nbuyGoods:\n => this trade will success") if err := buyGoods(db, player2.ID, player1.ID, 2, 100); err != nil { panic(err) } }func simpleExample(db *gorm.DB) { // Create a player, who has a coin and a goods.. if err := db.Clauses(clause.OnConflict{UpdateAll: true}). Create(&Player{ID: "test", Coins: 1, Goods: 1}).Error; err != nil { panic(err) } // Get a player. var testPlayer Player db.Find(&testPlayer, "id = ?", "test") fmt.Printf("getPlayer: %+v\n", testPlayer) // Create players with bulk inserts. Insert 1919 players totally, with 114 players per batch. bulkInsertPlayers := make([]Player, 1919, 1919) total, batch := 1919, 114 for i := 0; i < total; i++ { bulkInsertPlayers[i] = Player{ ID: uuid.New().String(), Coins: rand.Intn(10000), Goods: rand.Intn(10000), } } if err := db.Session(&gorm.Session{Logger: db.Logger.LogMode(logger.Error)}). CreateInBatches(bulkInsertPlayers, batch).Error; err != nil { panic(err) } // Count players amount. playersCount := int64(0) db.Model(&Player{}).Count(&playersCount) fmt.Printf("countPlayers: %d\n", playersCount) // Print 3 players. threePlayers := make([]Player, 3, 3) db.Limit(3).Find(&threePlayers) for index, player := range threePlayers { fmt.Printf("print %d player: %+v\n", index+1, player) } }func createDB() *gorm.DB { dsn := "root:@tcp(127.0.0.1:4000)/test?charset=utf8mb4" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) if err != nil { panic(err) } return db }func buyGoods(db *gorm.DB, sellID, buyID string, amount, price int) error { return util.TiDBGormBegin(db, true, func(tx *gorm.DB) error { var sellPlayer, buyPlayer Player if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). Find(&sellPlayer, "id = ?", sellID).Error; err != nil { return err } if sellPlayer.ID != sellID || sellPlayer.Goods < amount { return fmt.Errorf("sell player %s goods not enough", sellID) } if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). Find(&buyPlayer, "id = ?", buyID).Error; err != nil { return err } if buyPlayer.ID != buyID || buyPlayer.Coins < price { return fmt.Errorf("buy player %s coins not enough", buyID) } updateSQL := "UPDATE player set goods = goods + ?, coins = coins + ? WHERE id = ?" if err := tx.Exec(updateSQL, -amount, price, sellID).Error; err != nil { return err } if err := tx.Exec(updateSQL, amount, -price, buyID).Error; err != nil { return err } fmt.Println("\n[buyGoods]:\n 'trade success'") return nil }) }第 3 步:运行代码
本节将逐步介绍代码的运行方法。
第 3 步第 1 部分:go-sql-driver/mysql 表初始化
使用 GORM(推荐)
使用 go-sql-driver/mysql
注意
在 Gitpod Playground 中尝试 GORM: 现在就试试
无需手动初始化表。
第 3 步第 2 部分:TiDB Cloud 更改参数
使用 GORM(推荐)
使用 go-sql-driver/mysql
若你使用 TiDB Serverless 集群,更改 gorm.go
内 dsn
参数值:
dsn := "root:@tcp(127.0.0.1:4000)/test?charset=utf8mb4"
若你设定的密码为 123456
,而且从 TiDB Serverless 集群面板中得到的连接信息为:
Endpoint: xxx.tidbcloud.com
Port: 4000
User: 2aEp24QWEDLqRFs.root
那么此处应将 mysql.RegisterTLSConfig
和 dsn
更改为:
mysql.RegisterTLSConfig("register-tidb-tls", &tls.Config { MinVersion: tls.VersionTLS12, ServerName: "xxx.tidbcloud.com", }) dsn := "2aEp24QWEDLqRFs.root:123456@tcp(xxx.tidbcloud.com:4000)/test?charset=utf8mb4&tls=register-tidb-tls" 第 3 步第 3 部分:运行
使用 GORM(推荐)
使用 go-sql-driver/mysql
运行 make all
,这是以下两个操作的组合:
构建二进制 (make build):go build -o bin/gorm-example
运行 (make run):./bin/gorm-example
你也可以单独运行这两个 make 命令或原生命令。
第 4 步:预期输出
使用 GORM(推荐)
使用 go-sql-driver/mysql
以上就是小编为大家整理的关于如何使用 TiDB 和 Golang 构造CRUD 应用程序的相关内容。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。