go操作mysql

安装: 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数据库连接成功")

}

go调用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)
 }
}

go—mysql的增删改查

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

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() 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使用

第三方库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操作redis

安装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是目前比较流行的一个分布式消息队列,下面主要是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标准库

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的使用

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)

这样就会将哪个文件哪一行 都记录下来  但是不是特殊需求无需开启这个 因为会增加性能开

到此这篇关于go语言操作数据库及其常规操作的示例代码的文章就介绍到这了,更多相关go语言操作数据库内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!