使用Golang怎么操作數據庫?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
golang可以做服務器端開發,但golang很適合做日志處理、數據打包、虛擬機處理、數據庫代理等工作。在網絡編程方面,它還廣泛應用于web應用、API應用等領域。
安裝: go get -u github.com/go-sql-driver/mysql
GO語言的操作數據庫的驅動原生支持連接池, 并且是并發安全的 標準庫沒有具體的實現 只是列出了一些需要的第三方庫實現的具體內容
//第一次連接MySQL成功
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql" // _想當于init()初始化
"log"
)
func main() {
// root 用戶名 1qa2ws3ed是密碼 后邊的書ip:port gouse 庫名
dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
db, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
// ping是嘗試連接MySQL數據庫
if err = db.Ping(); err != nil{
panic(err)
}
log.Fatalln("Mysql數據庫連接成功")
}package main
import (
"database/sql"
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func InitDB() (err error) {
dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
db, err = sql.Open("mysql", dsn)
CheckErr(err)
err = db.Ping()
CheckErr(err)
fmt.Println("數據庫連接成功...")
// 設置數據庫連接池最大連接數
db.SetConnMaxLifetime(10)
//設置最大閑置連接數
db.SetMaxIdleConns(5)
return
}
type data struct {
Username string `json:"username"`
Password string `json:"password"`
}
func main() {
err := InitDB()
CheckErr(err)
query, err := db.Query("select username, password from test")
CheckErr(err)
for query.Next(){
line := data{}
// 查詢數據的時候必須要調用scan方法如果 沒有 使用scan 連接通道一直保持連接 無法釋放連接
_ = query.Scan(&line.Username, &line.Password)
fmt.Println(line)
dataDic := map[string]string{
"username": line.Username,
"password": line.Password,
}
marshal, _ := json.Marshal(dataDic)
fmt.Println(string(marshal))
}
}
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}package main
import (
"database/sql"
"encoding/json"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
// InitDB 數據庫連接初始化
func InitDB() (err error) {
dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
db, err = sql.Open("mysql", dsn)
CheckErr(err)
err = db.Ping()
CheckErr(err)
fmt.Println("數據庫連接成功...")
// 設置數據庫連接池最大連接數
db.SetConnMaxLifetime(10)
//設置最大閑置連接數
db.SetMaxIdleConns(5)
return
}
type data struct {
Username string `json:"username"`
Password string `json:"password"`
}
// SelectQuery 查詢函數
func SelectQuery() {
sqlStr := "select username, password from test where id > ?"
query, err := db.Query(sqlStr, 1)
CheckErr(err)
defer query.Close()
fmt.Printf("現在是北京時間 %s , 你今天進步了嗎?\n", time.Now().Format("2006-01-02 15:04:05"))
for query.Next() {
line := data{}
// 查詢數據的時候必須要調用scan方法如果 沒有 使用scan 連接通道一直保持連接 無法釋放連接
_ = query.Scan(&line.Username, &line.Password)
//fmt.Println(line)
dataDic := map[string]string{
"username": line.Username,
"password": line.Password,
}
marshal, _ := json.Marshal(dataDic)
fmt.Printf("查詢到的數據為 %s\n", string(marshal))
}
}
// InsertQuery 插入數據
func InsertQuery() {
// sql 語句
sqlStr := `insert into test (username,password) values ("kuQi", "123qwe")`
result, err := db.Exec(sqlStr)
CheckErr(err)
id, err := result.LastInsertId()
CheckErr(err)
fmt.Printf("插入成功數據的id為 %v", id)
}
// UpdateQuery 更新數據函數
func UpdateQuery(dataField string, user string) {
sqlStr := `update test set password=? where username=?`
result, err := db.Exec(sqlStr, dataField, user)
CheckErr(err)
rowsAffected, err := result.RowsAffected()
CheckErr(err)
fmt.Printf("被更新字段的id為%d\n", rowsAffected)
}
// DeleteQuery 刪除
func DeleteQuery(id int) {
sqlStr := `delete from test where id=?`
result, err := db.Exec(sqlStr, id)
CheckErr(err)
rowsAffected, err := result.RowsAffected()
CheckErr(err)
if rowsAffected == 0 {
fmt.Printf("沒有匹配到要刪除的id=%d數據", id)
return
}
fmt.Printf("刪除數據庫的id為%d", id)
}
//CheckErr 異常捕獲函數
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
// main 主函數 所有函數的入口
func main() {
err := InitDB()
CheckErr(err)
//InsertQuery()
UpdateQuery("hahaGolang123", "kuQi")
SelectQuery()
DeleteQuery(5)
}什么是預處理?
普通SQL語句執行過程:
1.客戶端對SQL語句進行占位符的替換得到了完整的SQL語句
2.客戶端發送完整SQL語句到MySQL服務端
3.MySQL服務端執行完整的SQL語句并將結果返回終端
預處理的執行過程
1.先把SQL語句拆分成兩部分,SQL語句部分和參數部分
2.先把SQL語句部分發送給MySQL服務端進行SQL預處理
3.然后參數部分發送給MySQL服務端,MySQL對SQL語句進行拼接
4.MySQL服務端執行完整的SQL語句返回結果
為什么要進行預處理?
1.為了優化MySQL服務器重復執行SQL的方法??梢詧绦蟹掌鞯男阅?,提前讓服務器編譯,一次編譯多次執行,節省后續重復編譯的成本
2.并且避免SQL注入
// prepare方法現將SQL發送到MySQL服務端, 返回一個準備好的狀態用于之后的查詢和命令。返回值可以同時執行多個查詢和命令 ; 命令也就是SQL語句
// PrepareInsert 預處理執行插入語句
func PrepareInsert() {
defer wg.Done()
sqlStr := `insert into test (username, password) values (?, ?)`
// - 預處理 stmt 就是編譯好的sql語句 之后直接傳遞參數即可
stmt, err := db.Prepare(sqlStr)
var u1 = uuid.Must(uuid.NewV4())
CheckErr(err)
defer stmt.Close()
i := rand.Int()
username := fmt.Sprintf("yonghuming%d", i)
result, err := stmt.Exec(username, u1.String()[:10])
CheckErr(err)
rowsAffected, err := result.LastInsertId()
CheckErr(err)
fmt.Printf("成功插入id=%d條數據\n", rowsAffected)
}// go語言中使用一下三個方法實現MySQL中的事務操作, 開始事務
func (db *DB) Begin()(*Tx, error)
// 提交事務 相當與Python中的conn.commit()
func (tx *Tx) Commit() error
// 回滾事務
func (tx *Tx) Rollback() error
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
type data struct {
Username string `json:"username"`
Password string `json:"password"`
}
// InitDB 數據庫連接初始化
func InitDB() (err error) {
dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
db, err = sql.Open("mysql", dsn)
CheckErr(err)
err = db.Ping()
CheckErr(err)
fmt.Println("數據庫連接成功...")
// 設置數據庫連接池最大連接數
db.SetMaxOpenConns(100)
//設置最大閑置連接數
db.SetMaxIdleConns(5)
return
}
//CheckErr 異常捕獲函數
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
// TranSaCtIon MySQL的事務操作
func TranSaCtIon() {
// 開啟事務
tx, err := db.Begin()
CheckErr(err)
// 執行多個SQL操作
sqlStr := `update test set id=id+100000 where password=?`
result, err := tx.Exec(sqlStr, "07f70f7e-4")
CheckErr(err)
id, err := result.LastInsertId()
if err != nil {
// 語句回滾
err := tx.Rollback()
fmt.Println("事務回滾")
CheckErr(err)
}
fmt.Printf("修改后的id為%d\n", id)
}
func main() {
err := InitDB()
CheckErr(err)
TranSaCtIon()
}第三方庫sqlx能夠簡化操作,提高開發效率
安裝go get github.com/jmoiron/sqlx
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
var db *sqlx.DB
// InitDB 數據庫初始化
func InitDB() (err error) {
dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
db, err = sqlx.Connect("mysql", dsn)
CheckErr(err)
db.SetMaxOpenConns(50)
db.SetMaxIdleConns(10)
fmt.Println("goUse 數據庫連接成功")
return
}
//CheckErr 異常捕獲函數
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
func main() {
err := InitDB()
CheckErr(err)
}sqlx相較于原生的sql庫好處在于 查詢的時候sql原生的需要next scan 回調獲取結果
sqlx 查詢只需要定義一個存儲的變量 然后自動就會將查詢的出來的值放入變量中
package main
import (
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
var db *sqlx.DB
type user struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
}
// InitDB 數據庫初始化
func InitDB() (err error) {
dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
// Connect 就是連接的同時db.ping()一下
db, err = sqlx.Connect("mysql", dsn)
CheckErr(err)
db.SetMaxOpenConns(50)
db.SetMaxIdleConns(10)
fmt.Println("goUse 數據庫連接成功")
return
}
// SelectDB 查詢單條數據的方法
func SelectDB() {
sqlStr := `select * from test where id=?`
var data user
_ = db.Get(&data, sqlStr, 990)
//CheckErr(err)
fmt.Printf("%#v\n", data)
marshal, err := json.Marshal(data)
CheckErr(err)
fmt.Println(string(marshal))
}
// ManySelect 查詢多條數據方法
func ManySelect() {
sqlStr := `select * from test where id < ?`
var dataList []user
err := db.Select(&dataList, sqlStr, 1000)
CheckErr(err)
//fmt.Println(dataList)
marshal, err := json.Marshal(dataList)
CheckErr(err)
fmt.Println(string(marshal))
}
//CheckErr 異常捕獲函數
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
func main() {
err := InitDB()
CheckErr(err)
SelectDB()
ManySelect()
}安裝go get -u github.com/go-redis/redis
package main
import (
"fmt"
"github.com/go-redis/redis"
)
var redisDB *redis.Client
// InitRedisDB redis數據庫初始化
func InitRedisDB() (err error) {
redisDB = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
})
_, err = redisDB.Ping(redisDB.Context()).Result()
CheckErr(err)
fmt.Println("redis 連接成功")
return
}
//CheckErr 異常捕獲函數
func CheckErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
func main() {
_ = InitRedisDB()
}set(key, value):給數據庫中名稱為key的string賦予值value
get(key):返回數據庫中名稱為key的string的value
getset(key, value):給名稱為key的string賦予上一次的value
mget(key1, key2,…, key N):返回庫中多個string的value
setnx(key, value):添加string,名稱為key,值為value
setex(key, time, value):向庫中添加string,設定過期時間time
mset(key N, value N):批量設置多個string的值
msetnx(key N, value N):如果所有名稱為key i的string都不存在
incr(key):名稱為key的string增1操作
incrby(key, integer):名稱為key的string增加integer
decr(key):名稱為key的string減1操作
decrby(key, integer):名稱為key的string減少integer
append(key, value):名稱為key的string的值附加value
substr(key, start, end):返回名稱為key的string的value的子串
NSQ是目前比較流行的一個分布式消息隊列,下面主要是NSQ及GO語言如何操作NSQ
NSQ是GO語言編寫的一個開源的實時分布式內存消息隊列, 其性能十分優異, NSQ的優勢有:
1.NSQ提倡分布式和擴散的拓撲,沒有單點故障,支持容錯和高可用性,并提供可靠的消息交付保證
2.NSQ支持橫向擴展, 沒有任何集中式代理
3.NSQ易于配置和部署,并且內置了管理界面
安裝go get -u github.com/nsqio/go-nsq
Context
在Go HTTP 包的server中,每一個請求都在對應著一個響應,請求處理函數通常會啟動額外的goroutine用來訪問后端的服務,比如數據庫和rpc服務,用來處理一個請求的goroutine通常需要訪問一些與請求特定的數據,比如終端的身份認證信息、驗證相關的token、請求和截止時間。當一個請求被取消或超時時,所有用來處理該請求的goroutine都應該迅速退出,然后系統才能釋放這些goroutine
如何優雅的結束goroutine釋放資源
// 通道版本
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func worker(exitChan <-chan struct{}) {
defer wg.Done()
Test:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-exitChan:
break Test
default:
}
}
}
func main() {
wg.Add(1)
c := make(chan struct{})
go worker(c)
time.Sleep(10 * time.Second)
c <- struct{}{}
close(c)
wg.Wait()
fmt.Println("Over")
}// Context版本
package main
import (
"context"
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func worker(ctx context.Context) {
defer wg.Done()
Test:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-ctx.Done():
break Test
default:
}
}
}
func main() {
wg.Add(1)
ctx, cancelFunc := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(10 * time.Second)
cancelFunc()
wg.Wait()
fmt.Println("Over")
}如果goroutine開啟了新的goroutine,只需要將ctx傳入到新的goroutine中即可
Background() 和 TODO()
go內置兩個函數: Background() 和TUDO(),這兩個函數分別返回了一個實現了context接口的background和todo. 我們代碼中最開始都是以這兩個內置的上下文對象作為最頂層的partent context,衍生出更多的子上下文對象。
backgroud() 主要用于main函數,初始化以及代碼測試,作為context這個樹結構的最頂層context,也就是跟context。
todo(),他目前還不知道能干點啥?
使用context的注意事項
推薦以參數顯示傳遞context
以context作為參數的函數方法,應該把context作為第一個參數
給一個函數傳遞context的時候,不要nil,如果不知道傳遞什么,就使用context.TODO()
context是并發安全的,可以隨意在多個goroutine中傳遞
log包定義了Logger類型, 該類型提供了一些格式化輸出的方法。本包也提供了一個預定義的標準logger,可以通過調用函數Print系列,fatal系列和panic系列來使用,比自行創建的logger對象更容易使用。
package main
import "log"
func main() {
log.Println("這是第一條工作日志")
v := "THIS is worker log"
log.Printf("%#v\n", v)
// Fatal將會值寫入信息之后,執行exit(1)
log.Fatal("之后寫一萬行代碼 我也不執行了哦")
// 可以通過log.Panic 引發異常 會將日志寫入之后引發異常
log.Panic("測試panic的日志")
}flag選項(日志輸出內容設置)
log標準庫提供了如下的flag選項,他們是一系列定義好的常量。
const (
Ldate = 1 << iota
Ltime
Lmicroseconds
Llongfile
Lshortfile
LUTC
LstdFlags = Ldate | Ltime
)
package main
import "log"
func main() {
// 設置默認附加的內容
log.SetFlags(log.Llongfile | log.Ltime)
// 設置日志前綴
log.SetPrefix("[go_log] ")
log.Println("測試日志")
}
output>>>
[go_log] 19:02:14 /Users/mac/GolandProjects/src/day02/go_log庫/main.go:19: 測試日志setoutput函數用來設置logger的輸出目的地,默認是標準錯誤輸出
package main
import (
"log"
"os"
)
func main() {
file, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Panic("文件打開失敗")
}
// 設置了寫入文件 日志內容就不會打印到終端了
log.SetOutput(file)
log.SetFlags(log.Llongfile | log.Ltime)
log.SetPrefix("[go_log] ")
log.Println("測試日志")
}我們可以定義一個init初始化函數 將log全部配置好 這樣更加標準化
logrus是GO結構化的logger 與上邊的logger標準庫完全兼容
安裝logrusgo get github.com/sirupsen/logrus
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animals": "dog",
"time": log.FieldKeyTime,
}).Info("這是啥")
}Trace、debug、info、warning、error、fatal、panic
log.Trace("跟蹤?")
log.Debug("Debug?")
log.Info("信息")
log.Warn("警告?")
log.Error("Something failed but I'm not quitting.")
// 記完日志后會調用os.Exit(1)
log.Fatal("Bye.")
// 記完日志后會調用 panic()
log.Panic("I'm bailing.")package main
import (
"os"
"time"
log "github.com/sirupsen/logrus"
)
func main() {
file, err := os.OpenFile("logrustest.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Panicln(err)
}
log.SetOutput(file)
for i := 0; i < 100; i++ {
log.WithFields(log.Fields{
"animals": "dog",
"Countey": "China",
"City": "BeiJing",
}).Info("這是啥")
time.Sleep(time.Second)
}
log.Trace("跟蹤?")
log.Info("信息")
log.Warn("警告?")
// 設置日志級別, 會記錄info以上級別(warn error fatal panic)
log.SetLevel(log.InfoLevel)
}
>>>結果
time="2021-02-04T12:00:15+08:00" level=info msg="這是啥" City=BeiJing Countey=China animals=dog
time="2021-02-04T12:00:17+08:00" level=info msg="這是啥" City=BeiJing Countey=China animals=dog
time="2021-02-04T12:00:18+08:00" level=info msg="這是啥" City=BeiJing Countey=China animals=dog
time="2021-02-04T12:00:19+08:00" level=info msg="這是啥" City=BeiJing Countey=China animals=dog日志的條目除了使用withfield 和withfields添加的相關日志,還有一些默認添加的日志字段
time 記錄日志的時間戳 msg 記錄日志信息 level記錄日志級別
logrus內置一下兩種日志格式化程序
logrus.TextFormatter logrus.JSONFormatter
log.SetFormatter(&log.JSONFormatter{})log.SetReportCaller(true)
這樣就會將哪個文件哪一行 都記錄下來 但是不是特殊需求無需開啟這個 因為會增加性能開
關于使用Golang怎么操作數據庫問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。