Golang Connection MySQL operation and dynamic connection pooling settings

Source: Internet
Author: User
Tags connection pooling mysql connection pool
This is a creation in Article, where the information may have evolved or changed.

The Golang itself does not provide a driver to connect to MySQL, but it defines a standard interface for third-party development drivers. Here to connect MySQL can use third-party libraries, third-party libraries recommend the use of Https://github.com/Go-SQL-Driver/MySQL this driver, update maintenance is better. The following shows the specific use, the complete code example can refer to the last.

Download driver

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

Database connection

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

The connection parameters can be in the following ways: Usually we use the second one.

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

Insert operation

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)

Using structured operations here, it is not recommended to use a method that directly stitching SQL statements.

Query operations

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)}

The query here uses the declaration of 4 independent variables userid, UserName, Userage, usersex to save the values of each row queried. The operation of the database is typically encapsulated in real-world development, and queries such as this typically take into account the return dictionary type.

//构造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)}

Modify Operation

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)

Delete operation

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)

Both the modify and delete operations are simple, similar to inserting data, using only rowsaffected to get the number of rows affected.

Full code

Package Mainimport ("Database/sql" "Fmt" _ "Github.com/go-sql-driver/mysql") func main () {insert ()}//Insert Demofu NC 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", 1) checkerr (ERR) ID, err: = Res. Lastinsertid () Checkerr (err) fmt. Println (ID)}//queries demofunc query () {db, err: = SQL. Open ("MySQL", "Root:@/test?charset=utf8") Checkerr (Err) rows, err: = db. Query ("SELECT * from User") Checkerr (ERR)//Normal demo//for rows. Next () {//var userId int//var userName string//var userage int//var usersex int//R oWS. Columns ()//err = rows. Scan (&userid, &username, &userage, &usersex)//Checkerr (ERR)//FMT. PRINTLN (userId)//FMT. PRINTLN (UserName)//FMT. PRINTLN (userage)//FMT. PRINTLN (Usersex)//}   Dictionary type//construct Scanargs, values two arrays, each value of Scanargs points to the address of values corresponding columns, _: = Rows. Columns () Scanargs: = Make ([]interface{}, Len (Columns)) Values: = Do ([]interface{}, Len (Columns)) for I: = Rang E values {scanargs[i] = &values[i]} for rows. Next () {//saves row data to the record dictionary err = rows.        Scan (Scanargs ...) Record: = Make (map[string]string) for I, col: = range values {if col! = Nil {Record[col Umns[i]] = string (col. ([]byte))}} FMT. Println (record)}}//Update data 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 (2, 1) checkerr (err) num, err: = Res. Rowsaffected () Checkerr (err) fmt. PRINTLN (num)}//Delete data 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 Connection pool implementation

Golang internal with the connection pool function, just start contact Golang When do not know this, also made a SQL. Open Object Management pool, really very embarrassing ah.

Sql. The Open function actually returns a connection pool object, not a single connection. When the open is not connected to the database, only when the query, Exce method to actually connect to the database. In one application, the same library connection requires only one SQL to be saved. The DB object after open is ready and does not require multiple open.

The resource is freed after the normal program executes, so try using a Web service to demonstrate it.

Turn on Web Services

First start a Web service to listen to 9090 port, relatively simple to do not more instructions.

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

DB Object Initialization

Declares a global DB object and initializes it.

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()}

The key to the implementation of connection pooling is Setmaxopenconns and Setmaxidleconns, where:
The Setmaxopenconns is used to set the maximum number of open connections, and the default value of 0 means no limit.
The Setmaxidleconns is used to set the number of idle connections.

By setting the maximum number of connections, you can avoid too many connections errors that occur when you connect to MySQL with too high concurrency. Set the number of idle connections when a connection is opened, it can be placed in the pool for the next use.

Request method

The HTTP request above sets the execution method of the request/pool address.

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)    }}

The pool method is to find a record from the user table and store it in the map, and finally output the finish. The code is over here. Very simple, test it below. Start the HTTP service first, and then use AB for concurrent test access:

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

View the connection process through show processlist in the database:

Golang Database Connection Pool

You can see that there are 100 or so processes.

The use of connection pooling can significantly improve performance because it avoids duplicate creation of the connection. Interested child boots can remove the connection pool code and test it yourself. The complete code is as follows:

Database connection Pool Test 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 (+) db. Setmaxidleconns (+) 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: = Rang E values {scanargs[j] = &values[j]} record: = Make (map[string]string) for rows. Next () {//saves row data to the record dictionary 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)}}

Summary

Golang the connection pool implemented here provides only the Setmaxopenconns and Setmaxidleconns methods for connection pooling configuration. One problem with the process is that the database itself has a time-out setting for the connection, and if the time-out is reached, the database will unilaterally disconnect, and then access with the connection in the connection pool will be an error.

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

All errors above are the output of the go-sql-drive itself, and sometimes bad connection errors occur. After several requests, the connection pool will reopen the new connection. This is no problem.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.