1. 程式人生 > >golang連線mysql操作及動態連線池設定

golang連線mysql操作及動態連線池設定

golang本身沒有提供連線mysql的驅動,但是定義了標準介面供第三方開發驅動。這裡連線mysql可以使用第三方庫,第三方庫推薦使用https://github.com/Go-SQL-Driver/MySQL這個驅動,更新維護都比較好。下面演示下具體的使用,完整程式碼示例可以參考最後。

下載驅動

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

資料庫連線

db, err := sql.Open("mysql", "user:[email protected](localhost:3306)/dbname?charset=utf8")

其中連線引數可以有如下幾種形式:通常我們都用第二種。

user@unix(/path/to/socket)/dbname?charset=utf8
user:password@tcp(localhost:5555)/dbname?charset=utf8
user:[email protected]/dbname
user: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 main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    insert()
}

//插入demo
func 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)
}

//查詢demo
func 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.DB

func 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 main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "log"
    "net/http"
)

var db *sql.DB

func 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 EOF
packets.go:118: write tcp 192.168.3.90:3306: broken pipe

上面都是錯誤都是go-sql-drive本身的輸出,有的時候還會出現bad connection的錯誤。多請求幾次後連線池會重新開啟新連線這時候就沒有問題了。

相關推薦

golang連線mysql操作動態連線設定

golang本身沒有提供連線mysql的驅動,但是定義了標準介面供第三方開發驅動。這裡連線mysql可以使用第三方庫,第三方庫推薦使用https://github.com/Go-SQL-Driver/MySQL這個驅動,更新維護都比較好。下面演示下具體的使用,完

python3使用pymysql連線Mysql 資料庫 簡單的增刪改查操作

示例表1查詢操作import pymysql #匯入 pymysql #開啟資料庫連線 db= pymysql.connect(host="localhost",user="root", password="123456",db="test",po

.net中連線MYSQL資料庫常用操作

需要有一個工具類:using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Text; using System.Data; using

Java連線MySQL資料庫簡單操作程式碼

Java連線MySql需要下載JDBC驅動MySQL-connector-java-5.0.5.zip(舉例,現有新版本)。然後將其解壓縮到任一目錄。我是解壓到D盤,然後將其目錄下的MySQL-connector-java-5.0.5-bin.jar加到classpath裡,具體如下: “我的電腦”-&

iOS連線mysql資料庫基本操作

然後新增一個實現連線mysql資料庫及對資料庫進行相關操作的方法的類。其實現連線mysql資料庫及對資料庫進行相關操作的方法的實現如下: //連線資料庫-(void)connectHost:(NSString *)host connectUser:(NSString *)

C#連線mysql資料簡單操作

使用C#連線資料庫需要用到mysql官方提供的mysql-connector,下載地址如下 官網下載地址 直接執行 選擇Typical即可 預設安裝路徑在C:\Program Files (x86)\MySQL 新建C#的控制檯

Qt連線MySQL程式設計資料庫效能調優(一)

之前整理過一篇Qt下資料庫程式設計基礎 :最近在進行單元測試,所以把遇到的一些問題整理出來,主要是關於資料庫的 1.遠端連線資料庫 連線語句是: mysql -h 192.168.xx.xx(IP地址) -P 3306(埠) -u remoteuser(登入使用

註冊模組MVC——連線mysql操作

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 T

Java使用JDBC連線mysql資料庫測試是否連線成功的方法

一、連線mysql資料庫: 步驟一:在eclipse下面建一個專案,我的是JDBC_Test, 步驟二:選中專案名稱,右鍵選擇Build Path->Configure Build Path…. 步驟三:進入Java Build Path 選擇L

nodejs連線mysql資料庫基本認識

一、幾個常用的全域性變數 1、__filename獲取當前檔案的路徑 2、__dirname獲取當前檔案的目錄 3、process.cwd()獲取當前工程的目錄 二、檔案的引入與匯出 1

go語言連線mysql操作

一、安裝在windows下安裝1、goland(go IDE) 安裝位置: D:\package\goland 2、go 安裝位置 : D:\package\go 3、GOPATH 位置: D:\package\go\workspaceD:\package\go\wor

node.js連接MySQL操作註意事項

creat 等等 pass 產生 函數返回 密碼 .com 原因 處理 node.js作為服務端的js運行環境已經出現了有幾年了,最近我有個朋友也在做這方面的開發,但是也是剛剛接觸,遇到了很多坑。前幾天他們在操作數據庫的時候出現了點問題,後來我們一起看了看,其實都是

GolangMysql操作

set ron display imp lan fun 圖片 try 增刪改 話說當年武大郎對著電腦一頓劈裏啪啦,,,對mysql增刪改查 增加insert package main import ( "fmt" "github.com

關於node.js mysql操作封裝。

首先,我們先引入mysql庫。建立與mysql的連線池。config裡儲存這資料庫的資訊。用Promise封裝sql語句(用promise封裝可以很好的防止回撥地獄的現象)首先,從連線池中取出一個例項。建立連線。然後執行sql語句。如果發生錯誤,Promise狀態變成erro

關於c++連線mysql產生8小時連線失效的問題

    雖然8小時連線失效可以通過設定mysql來解決,出於嚴謹的態度,嘗試在程式碼上優化,優化的時候發現檢查連線的方法有點問題,沒能檢查到8小時連線已失效,後來通過捕捉異常來重新建立新的連線池 sql::Connection * CSqlConnPool::GetRead

mysql 同時支援多少連線MYSQL 檢視最大連線數和修改最大連線

MySQL檢視最大連線數和修改最大連線數 1、檢視最大連線數 show variables like '%max_connections%'; 2、修改最大連線數 set GLOBAL max_connections = 200;   以下的文章主要是向大家介紹的是M

django 和Navicat 連線 MYSQL 8.0遠端連線

MYSQL 8.0內新增加mysql_native_password函式,通過更改這個函式密碼來進行遠端連線。 更改ROOT使用者的native_password密碼,直接用ROOT使用者的賬號密碼去連線是不行,即時密碼正確。 mysql> ALTER

mysql筆記五——資料庫連線(原理、構建)和java動態代理的使用

資料庫連線池 1、什麼是資料庫連線池?       資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是再重新建立一個;釋放空閒時間超過最大空閒時間的資料庫連線來避免因為沒有釋放資料庫連線而引起的資料庫連線遺漏。這項

使用Jmeter連線Mysql日常操作

注意:想要正常執行該軟體需要安裝Java環境,本站提供了該Java軟體的下載地址,需要的朋友們可以點選下載。 2、將檔案拷貝到Jmeter\lib\ext目錄下,我的是:D:\Program Files\apache-jmeter-2.11\lib\ext 3、啟動Jmeter,在測試計劃點選瀏

SpringBoot2.x預設連線hikaridruid連線

    在SpringBoot2.x的預設連線池是hikari。我們可以通過spring-boot-starter-jdbc的依賴發現。但是HikariCP應該是目前速度最快的連線池了。 1.hikari連線池使用 pom.xml中jdbc的三座標 <depen