本篇介绍如何使用Golang访问MemFireDB数据库。
如果大家有个人项目或者公司内部测试项目,推荐大家尝试MemFire Cloud,不用自己搭建数据库,在家或者公司随时可以访问。
gopg是Golang中最常用的访问数据库的ORM库,MemFireDB兼容Postgres接口,所以可以直接把MemFireDB当Postgres使用。
在 memfiredb.com 上创建数据库时,可以选择创建密码或者证书两种认证模式,在公网上部署的数据库,如果存储的是比较重要的数据,建议使用证书认证模式,该模式安全系数要高很多,撞库、彩虹表、暴力破解等传统的密码破解方式对证书认证是无能为力的。如果只是简单测试,可以使用密码认证方式,这种方式配置简单些。
下面是示例代码:
import (
"context"
"flag"
"fmt"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"strings"
"sync"
)
var (
addr = flag.String("addr", "192.168.80.161:5433", "memfire address to connect")
user = flag.String("user", "test", "memfire user")
passwd = flag.String("passwd", "test", "memfire password")
dbname = flag.String("db", "dbname", "memfire database name to connect")
)
func panicIf(err error) {
if err != nil {
panic(err)
}
}
func test_transaction_try_again(db *pg.DB) {
incrInTx := func(db *pg.DB) error {
// Transaction is automatically rolled back on error.
return db.RunInTransaction(db.Context(), func(tx *pg.Tx) error {
var counter int
_, err := tx.QueryOne(
pg.Scan(&counter), `SELECT counter FROM counters FOR UPDATE`)
if err != nil {
return err
}
counter++
_, err = tx.Exec(`UPDATE counters SET counter = ?`, counter)
return err
})
}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
err := incrInTx(db)
if err != nil {
if strings.Contains(err.Error(), "40001") ||
strings.Contains(err.Error(), "Try again") ||
strings.Contains(err.Error(), "Restart read required") {
fmt.Println("Try again")
continue
}
panic(err)
}
break
}
}()
}
wg.Wait()
}
type Counter struct {
Counter int64
}
type User struct {
Id int64
Name string
Emails []string
}
// createSchema creates database schema for Counter/ User
func createSchema(db *pg.DB) error {
models := []interface{}{
(*Counter)(nil),
(*User)(nil),
}
for _, model := range models {
err := db.Model(model).CreateTable(&orm.CreateTableOptions{
Temp: false,
IfNotExists: true,
})
if err != nil {
return err
}
}
return nil
}
func main() {
flag.Parse()
opt := pg.Options{
Addr: *addr,
User: *user,
Password: *passwd,
Database: *dbname,
OnConnect: func(ctx context.Context, cn *pg.Conn) error {
println("new connection created")
return nil
},
}
db := pg.Connect(&opt)
defer db.Close()
err := createSchema(db)
panicIf(err)
_, err = db.Exec("delete from counters")
panicIf(err)
cnt := &Counter{
Counter: 1,
}
_, err = db.Model(cnt).Insert()
panicIf(err)
test_transaction_try_again(db)
}