装置: 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...