博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
golang连接mysql操作及动态连接池设置
阅读量:7181 次
发布时间:2019-06-29

本文共 7779 字,大约阅读时间需要 25 分钟。

hot3.png

golang本身没有提供连接mysql的驱动,但是定义了标准接口供第三方开发驱动。这里连接mysql可以使用第三方库,第三方库推荐使用这个驱动,更新维护都比较好。下面演示下具体的使用,完整代码示例可以参考最后。

下载驱动

sudo go get github.com/go-sql-driver/mysql

数据库连接

db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname?charset=utf8")

其中连接参数可以有如下几种形式:通常我们都用第二种。

user@unix(/path/to/socket)/dbname?charset=utf8user:password@tcp(localhost:5555)/dbname?charset=utf8user:password@/dbnameuser:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname

插入操作

stmt, err := db.Prepare(`INSERT user (user_name,user_age,user_sex) values (?,?,?)`)checkErr(err)res, err := stmt.Exec("tony", 20, 1)checkErr(err)id, err := res.LastInsertId()checkErr(err)fmt.Println(id)

这里使用结构化操作,不推荐使用直接拼接sql语句的方法。

查询操作

rows, err := db.Query("SELECT * FROM user")checkErr(err)for rows.Next() {    var userId int    var userName string    var userAge int    var userSex int    rows.Columns()    err = rows.Scan(&userId, &userName, &userAge, &userSex)    checkErr(err)    fmt.Println(userId)    fmt.Println(userName)    fmt.Println(userAge)    fmt.Println(userSex)}

这里查询的方式使用声明4个独立变量userId、userName、userAge、userSex来保存查询出来的每一行的值。在实际开发中通常会封装数据库的操作,对这样的查询通常会考虑返回字典类型。

//构造scanArgs、values两个数组,scanArgs的每个值指向values相应值的地址columns, _ := rows.Columns()scanArgs := make([]interface{}, len(columns))values := make([]interface{}, len(columns))for i := range values {    scanArgs[i] = &values[i]}for rows.Next() {    //将行数据保存到record字典    err = rows.Scan(scanArgs...)    record := make(map[string]string)    for i, col := range values {        if col != nil {            record[columns[i]] = string(col.([]byte))        }    }    fmt.Println(record)}

修改操作

stmt, err := db.Prepare(`UPDATE user SET user_age=?,user_sex=? WHERE user_id=?`)checkErr(err)res, err := stmt.Exec(21, 2, 1)checkErr(err)num, err := res.RowsAffected()checkErr(err)fmt.Println(num)

删除操作

stmt, err := db.Prepare(`DELETE FROM user WHERE user_id=?`)checkErr(err)res, err := stmt.Exec(1)checkErr(err)num, err := res.RowsAffected()checkErr(err)fmt.Println(num)

修改和删除操作都比较简单,同插入数据类似,只是使用RowsAffected来获取影响的数据行数。

完整代码

package mainimport (    "database/sql"    "fmt"    _ "github.com/go-sql-driver/mysql")func main() {    insert()}//插入demofunc insert() {    db, err := sql.Open("mysql", "root:@/test?charset=utf8")    checkErr(err)    stmt, err := db.Prepare(`INSERT user (user_name,user_age,user_sex) values (?,?,?)`)    checkErr(err)    res, err := stmt.Exec("tony", 20, 1)    checkErr(err)    id, err := res.LastInsertId()    checkErr(err)    fmt.Println(id)}//查询demofunc query() {    db, err := sql.Open("mysql", "root:@/test?charset=utf8")    checkErr(err)    rows, err := db.Query("SELECT * FROM user")    checkErr(err)    //普通demo    //for rows.Next() {    //    var userId int    //    var userName string    //    var userAge int    //    var userSex int    //    rows.Columns()    //    err = rows.Scan(&userId, &userName, &userAge, &userSex)    //    checkErr(err)    //    fmt.Println(userId)    //    fmt.Println(userName)    //    fmt.Println(userAge)    //    fmt.Println(userSex)    //}    //字典类型    //构造scanArgs、values两个数组,scanArgs的每个值指向values相应值的地址    columns, _ := rows.Columns()    scanArgs := make([]interface{}, len(columns))    values := make([]interface{}, len(columns))    for i := range values {        scanArgs[i] = &values[i]    }    for rows.Next() {        //将行数据保存到record字典        err = rows.Scan(scanArgs...)        record := make(map[string]string)        for i, col := range values {            if col != nil {                record[columns[i]] = string(col.([]byte))            }        }        fmt.Println(record)    }}//更新数据func update() {    db, err := sql.Open("mysql", "root:@/test?charset=utf8")    checkErr(err)    stmt, err := db.Prepare(`UPDATE user SET user_age=?,user_sex=? WHERE user_id=?`)    checkErr(err)    res, err := stmt.Exec(21, 2, 1)    checkErr(err)    num, err := res.RowsAffected()    checkErr(err)    fmt.Println(num)}//删除数据func remove() {    db, err := sql.Open("mysql", "root:@/test?charset=utf8")    checkErr(err)    stmt, err := db.Prepare(`DELETE FROM user WHERE user_id=?`)    checkErr(err)    res, err := stmt.Exec(1)    checkErr(err)    num, err := res.RowsAffected()    checkErr(err)    fmt.Println(num)}func checkErr(err error) {    if err != nil {        panic(err)    }}

golang go-sql-drive mysql连接池的实现

golang内部自带了连接池功能,刚开始接触golang的时候不了解这个,还自己搞了一个 sql.Open的对象管理池,真的非常囧啊。

sql.Open函数实际上是返回一个连接池对象,不是单个连接。在open的时候并没有去连接数据库,只有在执行query、exce方法的时候才会去实际连接数据库。在一个应用中同样的库连接只需要保存一个sql.Open之后的db对象就可以了,不需要多次open。

因为普通程序执行完毕之后资源就会被释放掉,所以这里尝试使用web服务进行演示。

开启web服务

首页先启动一个web服务监听9090端口,比较简单不多做说明。

func startHttpServer() {    http.HandleFunc("/pool", pool)    err := http.ListenAndServe(":9090", nil)    if err != nil {        log.Fatal("ListenAndServe: ", err)    }}

db对象初始化

声明一个全局的db对象,并进行初始化。

var db *sql.DBfunc init() {    db, _ = sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?charset=utf8")    db.SetMaxOpenConns(2000)    db.SetMaxIdleConns(1000)    db.Ping()}

连接池的实现关键在于SetMaxOpenConns和SetMaxIdleConns,其中: 

SetMaxOpenConns用于设置最大打开的连接数,默认值为0表示不限制。 
SetMaxIdleConns用于设置闲置的连接数。

设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。

请求方法

上面开启http请求设置了请求/pool地址的执行方法

func pool(w http.ResponseWriter, r *http.Request) {    rows, err := db.Query("SELECT * FROM user limit 1")    defer rows.Close()    checkErr(err)    columns, _ := rows.Columns()    scanArgs := make([]interface{}, len(columns))    values := make([]interface{}, len(columns))    for j := range values {        scanArgs[j] = &values[j]    }    record := make(map[string]string)    for rows.Next() {        //将行数据保存到record字典        err = rows.Scan(scanArgs...)        for i, col := range values {            if col != nil {                record[columns[i]] = string(col.([]byte))            }        }    }    fmt.Println(record)    fmt.Fprintln(w, "finish")}func checkErr(err error) {    if err != nil {        fmt.Println(err)        panic(err)    }}

pool方法就是从user表中查出一条记录然后存放到map中,最后输出finish。代码到这里就算完了非常简单,下面来测试一下。首先启动http服务,然后使用ab进行并发测试访问:

ab -c 100 -n 1000 'http://localhost:9090/pool'

在数据库中通过show processlist查看连接进程: 

这里写图片描述 
golang数据库连接池

可以看到有100来个进程。

因为避免了重复创建连接,所以使用连接池可以很明显的提高性能。有兴趣的童靴可以去掉连接池代码自己测试一下。完整代码如下:

//数据库连接池测试package mainimport (    "database/sql"    "fmt"    _ "github.com/go-sql-driver/mysql"    "log"    "net/http")var db *sql.DBfunc init() {    db, _ = sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?charset=utf8")    db.SetMaxOpenConns(2000)    db.SetMaxIdleConns(1000)    db.Ping()}func main() {    startHttpServer()}func startHttpServer() {    http.HandleFunc("/pool", pool)    err := http.ListenAndServe(":9090", nil)    if err != nil {        log.Fatal("ListenAndServe: ", err)    }}func pool(w http.ResponseWriter, r *http.Request) {    rows, err := db.Query("SELECT * FROM user limit 1")    defer rows.Close()    checkErr(err)    columns, _ := rows.Columns()    scanArgs := make([]interface{}, len(columns))    values := make([]interface{}, len(columns))    for j := range values {        scanArgs[j] = &values[j]    }    record := make(map[string]string)    for rows.Next() {        //将行数据保存到record字典        err = rows.Scan(scanArgs...)        for i, col := range values {            if col != nil {                record[columns[i]] = string(col.([]byte))            }        }    }    fmt.Println(record)    fmt.Fprintln(w, "finish")}func checkErr(err error) {    if err != nil {        fmt.Println(err)        panic(err)    }}

小结

golang这边实现的连接池只提供了SetMaxOpenConns和SetMaxIdleConns方法进行连接池方面的配置。在使用的过程中有一个问题就是数据库本身对连接有一个超时时间的设置,如果超时时间到了数据库会单方面断掉连接,此时再用连接池内的连接进行访问就会出错。

packets.go:32: unexpected EOFpackets.go:118: write tcp 192.168.3.90:3306: broken pipe

上面都是错误都是go-sql-drive本身的输出,有的时候还会出现bad connection的错误。多请求几次后连接池会重新打开新连接这时候就没有问题了。

转载于:https://my.oschina.net/mickelfeng/blog/1627763

你可能感兴趣的文章
数据驱动的迷思
查看>>
软件工程师欲发动DDoS攻击白宫网站 抗议特朗普就任总统
查看>>
在中关村问小米
查看>>
Duolingo推出聊天机器人功能帮助你学习外语
查看>>
《企业级ios应用开发实战》一3.3 MVC模式
查看>>
巴菲特正寻求可再生能源领域的投资机会
查看>>
《Android程序设计》一3.4 静态应用资源和上下文
查看>>
固态硬盘SSD在关键场合的应用
查看>>
详述机柜的不同分类及常见配件
查看>>
CWA成员称其领导人被捕后将增加对美国政府的攻击
查看>>
谭铁牛当选CVPR 2021大会主席,华人学术力量不容小觑
查看>>
中诚信携手神州控股助推智慧城市到信用城市发展
查看>>
想用好云?先看看这10条安全建议
查看>>
数据中心电源仍然困扰着CIO
查看>>
美南方电力解围东芝西屋困局 计提损失将减一半生 “蝴蝶效应”
查看>>
自然语言处理顶级会议 EMNLP 最佳论文出炉,聚焦神经网络 (下载)
查看>>
视频矩阵的十二种特性是什么
查看>>
智能电网易遭受网络攻击的症结在哪里?
查看>>
微软推视频分析服务 片中人什么心情都知道
查看>>
Windows 10年度更新将启用Hotspot 2.0功能
查看>>