Golang对数据库操作--高并发与线程安全

2020-12-28 11:44:44 浏览数 (1)

前些日子为了给java组同事提供直播端的商品详情接口,这些接口原本是由PHP开发组提供的,但由于直播并发压力问题,改由Golang来提供。

在由go开发接口过程中,发现在高并发下出现数据错乱--用A商品的ID查到B商品的详情,即线程安全问题,这主要是由数据查询构造器引起的。在低并发测环境并不会复现线程安全引起的数据错乱

一开始,数据库操作对像包括了连接器和查询器,只要实例化一个服务即实例化一个数据库操作对像提供数据库操作服务

这样设计,好处是共享链接,减少打开连接句柄的开销,提升数据库操作的并发能力和服务器的连接开销。但缺点是共享的链接是在同一个数据库操作对像中,在构造查询器中涉及查询语句的构建,这样,就会在多个商品高并发查询时,引起查询语句在多线程下获得本不是所属商品的查询语句。

改良的办法就是将数据库查询构造器从数据库操作对像中分离出来,并引用同一个连接器对像,这里一定要使用指针,不然就会发起多个数据库连接。

附上数据库操作对像

DB.go

代码语言:go复制
...
//创建连接器
type DbConnection struct{
    DB *sql.DB
}
//构建查询器
type DbQuery struct {
    Wher string //[]map[string]string
    Joinn string
    Wherestring string
    order string
    limit string
    alias string
    table string
    group string
    update string
    save string
    field string
    DB *sql.DB
    Rows interface{}
    Row interface{}
    sync.RWMutex
}
//构建连接
func (DbConnection *DbConnection) Connt (cnt map[string]string) {
	dsn := fmt.Sprintf("%s:%s@%s(%s:%s)/%s",cnt["username"],cnt["password"],cnt["network"],cnt["server"],cnt["port"],cnt["database"])
    db,err := sql.Open("mysql",dsn)
    if err != nil{
        fmt.Printf("Open mysql failed,err:%vn",err)
    }
    db.SetConnMaxLifetime(100*time.Second)
    db.SetMaxOpenConns(500)
    db.SetMaxIdleConns(16)
    DbConnection.DB = db
}
...

使用:

server.go

代码语言:javascript复制
...
	flag.Parse()

	s := server.NewServer()
	addRegistryPlugin(s)

	dbcnt:=make(map[string]string)
	dbcnt["username"] = "dbname"
	dbcnt["password"] = "*******"
	dbcnt["port"] = "3306"
	dbcnt["database"] = "mall"
	dbcnt["network"]  = "tcp"
	dbcnt["server"]   = "127.0.0.1"
	// var Db *sql.DB
	d:=new(db.DbConnection)
	d.Connt(dbcnt)
	goodsmodel := goods.Goods{*d}//注册对像时传递数据库连接对像指针
	s.RegisterName("Goods", &goodsmodel, "")
	s.Serve("tcp", *addr)
...

service.go

共享数据库连接器,但每次查询都构建一个新的查询器实例 d:=new(db.DbQuery),完美解决高并发下对数据库查询的线程安全问题

代码语言:javascript复制
...
//conn *db.DbConnection 共享连接
func getShopGoodBaseInfo(conn *db.DbConnection,goodsId int) *map[string]string {
    //获取商品基础信息
    field := `goods_base `
    d:=new(db.DbQuery)
    d.Builder(conn)
    d.Table("goods")
    d.Field(field)
    where := fmt.Sprintf("goods_id = %d", goodsId)
    d.Where(where)
    rows := d.Find()
    goodsBaseInfo :=d.GetRow(rows)
    return &goodsBaseInfo
}
...

0 人点赞