关于mysql:Go操作MySQL

6次阅读

共计 11777 个字符,预计需要花费 30 分钟才能阅读完成。

装置: go get -u github.com/go-sql-driver/mysql

GO 语言的操作数据库的驱动原生反对连接池, 并且是并发平安的 规范库没有具体的实现 只是列出了一些须要的第三方库实现的具体内容

// 第一次连贯 MySQL 胜利 package mainimport (“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 数据库连贯胜利 ”)

}
Go 调用 MySQL 封装成函数

package mainimport (“database/sql”
“encoding/json”
“fmt”
_ “github.com/go-sql-driver/mysql”)var db *sql.DBfunc 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)
}
}
GO—MySQL 的增删改查

package mainimport (“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)
}
MySQL 的预处理

什么是预处理?
一般 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 注入
Go 实现 MySQL 预处理

// 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 实现事务操作

// go 语言中应用一下三个办法实现 MySQL 中的事务操作,开始事务 func (db DB) Begin()(Tx, error)// 提交事务 相当与 Python 中的 conn.commit()func (tx Tx) Commit() error // 回滚事务 func (tx Tx) Rollback() errorpackage mainimport ( “database/sql”
“fmt”

_ “github.com/go-sql-driver/mysql”)var db *sql.DBtype 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 应用

第三方库 sqlx 可能简化操作,进步开发效率

装置 go get github.com/jmoiron/sqlx

package mainimport (“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 mainimport (“encoding/json”
“fmt”

_ “github.com/go-sql-driver/mysql”
“github.com/jmoiron/sqlx”)var db *sqlx.DBtype 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 操作 Redis
装置 go get -u github.com/go-redis/redis

package mainimport (“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 缩小 integerappend(key, value):名称为 key 的 string 的值附加 value
substr(key, start, end):返回名称为 key 的 string 的 value 的子串

NSQ 分布式音讯队列
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 mainimport (“fmt”
“sync”
“time”)var wg sync.WaitGroupfunc 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 mainimport (“context”
“fmt”
“sync”
“time”)var wg sync.WaitGroupfunc 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 规范库
log 包定义了 Logger 类型, 该类型提供了一些格式化输入的办法。本包也提供了一个预约义的规范 logger,能够通过调用函数 Print 系列,fatal 系列和 panic 系列来应用,比自行创立的 logger 对象更容易应用。

package mainimport “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 mainimport “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 mainimport (“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 的应用
logrus 是 GO 结构化的 logger 与上边的 logger 规范库齐全兼容

装置 logrusgo get github.com/sirupsen/logrus

package mainimport (
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 mainimport (“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)
这样就会将哪个文件哪一行 都记录下来 然而不是非凡需要无需开启这个 因为会减少性能开销

本文只代表个人观点!
原文链接:http://www.662p.com/article/3…

正文完
 0