经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
Go语言操作数据库及其常规操作
来源:cnblogs  作者:听风走了八千里  时间:2021/5/6 18:01:18  对本文有异议

Go操作MySQL

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

GO语言的操作数据库的驱动原生支持连接池, 并且是并发安全的 标准库没有具体的实现 只是列出了一些需要的第三方库实现的具体内容

  1. //第一次连接MySQL成功
  2. package main
  3. import (
  4. "database/sql"
  5. _ "github.com/go-sql-driver/mysql" // _想当于init()初始化
  6. "log"
  7. )
  8. func main() {
  9. // root 用户名 1qa2ws3ed是密码 后边的书ip:port gouse 库名
  10. dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
  11. db, err := sql.Open("mysql", dsn)
  12. if err != nil {
  13. panic(err)
  14. }
  15. // ping是尝试连接MySQL数据库
  16. if err = db.Ping(); err != nil{
  17. panic(err)
  18. }
  19. log.Fatalln("Mysql数据库连接成功")
  20. }
  • Go调用MySQL封装成函数
  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "fmt"
  6. _ "github.com/go-sql-driver/mysql"
  7. )
  8. var db *sql.DB
  9. func InitDB() (err error) {
  10. dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
  11. db, err = sql.Open("mysql", dsn)
  12. CheckErr(err)
  13. err = db.Ping()
  14. CheckErr(err)
  15. fmt.Println("数据库连接成功...")
  16. // 设置数据库连接池最大连接数
  17. db.SetConnMaxLifetime(10)
  18. //设置最大闲置连接数
  19. db.SetMaxIdleConns(5)
  20. return
  21. }
  22. type data struct {
  23. Username string `json:"username"`
  24. Password string `json:"password"`
  25. }
  26. func main() {
  27. err := InitDB()
  28. CheckErr(err)
  29. query, err := db.Query("select username, password from test")
  30. CheckErr(err)
  31. for query.Next(){
  32. line := data{}
  33. // 查询数据的时候必须要调用scan方法如果 没有 使用scan 连接通道一直保持连接 无法释放连接
  34. _ = query.Scan(&line.Username, &line.Password)
  35. fmt.Println(line)
  36. dataDic := map[string]string{
  37. "username": line.Username,
  38. "password": line.Password,
  39. }
  40. marshal, _ := json.Marshal(dataDic)
  41. fmt.Println(string(marshal))
  42. }
  43. }
  44. func CheckErr(err error) {
  45. if err != nil {
  46. fmt.Println(err)
  47. panic(err)
  48. }
  49. }
  • GO—MySQL的增删改查
  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "fmt"
  6. "time"
  7. _ "github.com/go-sql-driver/mysql"
  8. )
  9. var db *sql.DB
  10. // InitDB 数据库连接初始化
  11. func InitDB() (err error) {
  12. dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
  13. db, err = sql.Open("mysql", dsn)
  14. CheckErr(err)
  15. err = db.Ping()
  16. CheckErr(err)
  17. fmt.Println("数据库连接成功...")
  18. // 设置数据库连接池最大连接数
  19. db.SetConnMaxLifetime(10)
  20. //设置最大闲置连接数
  21. db.SetMaxIdleConns(5)
  22. return
  23. }
  24. type data struct {
  25. Username string `json:"username"`
  26. Password string `json:"password"`
  27. }
  28. // SelectQuery 查询函数
  29. func SelectQuery() {
  30. sqlStr := "select username, password from test where id > ?"
  31. query, err := db.Query(sqlStr, 1)
  32. CheckErr(err)
  33. defer query.Close()
  34. fmt.Printf("现在是北京时间 %s , 你今天进步了吗?\n", time.Now().Format("2006-01-02 15:04:05"))
  35. for query.Next() {
  36. line := data{}
  37. // 查询数据的时候必须要调用scan方法如果 没有 使用scan 连接通道一直保持连接 无法释放连接
  38. _ = query.Scan(&line.Username, &line.Password)
  39. //fmt.Println(line)
  40. dataDic := map[string]string{
  41. "username": line.Username,
  42. "password": line.Password,
  43. }
  44. marshal, _ := json.Marshal(dataDic)
  45. fmt.Printf("查询到的数据为 %s\n", string(marshal))
  46. }
  47. }
  48. // InsertQuery 插入数据
  49. func InsertQuery() {
  50. // sql 语句
  51. sqlStr := `insert into test (username,password) values ("kuQi", "123qwe")`
  52. result, err := db.Exec(sqlStr)
  53. CheckErr(err)
  54. id, err := result.LastInsertId()
  55. CheckErr(err)
  56. fmt.Printf("插入成功数据的id为 %v", id)
  57. }
  58. // UpdateQuery 更新数据函数
  59. func UpdateQuery(dataField string, user string) {
  60. sqlStr := `update test set password=? where username=?`
  61. result, err := db.Exec(sqlStr, dataField, user)
  62. CheckErr(err)
  63. rowsAffected, err := result.RowsAffected()
  64. CheckErr(err)
  65. fmt.Printf("被更新字段的id为%d\n", rowsAffected)
  66. }
  67. // DeleteQuery 删除
  68. func DeleteQuery(id int) {
  69. sqlStr := `delete from test where id=?`
  70. result, err := db.Exec(sqlStr, id)
  71. CheckErr(err)
  72. rowsAffected, err := result.RowsAffected()
  73. CheckErr(err)
  74. if rowsAffected == 0 {
  75. fmt.Printf("没有匹配到要删除的id=%d数据", id)
  76. return
  77. }
  78. fmt.Printf("删除数据库的id为%d", id)
  79. }
  80. //CheckErr 异常捕获函数
  81. func CheckErr(err error) {
  82. if err != nil {
  83. fmt.Println(err)
  84. panic(err)
  85. }
  86. }
  87. // main 主函数 所有函数的入口
  88. func main() {
  89. err := InitDB()
  90. CheckErr(err)
  91. //InsertQuery()
  92. UpdateQuery("hahaGolang123", "kuQi")
  93. SelectQuery()
  94. DeleteQuery(5)
  95. }
  • MySQL的预处理
  1. 什么是预处理?
  2. 普通SQL语句执行过程:
  3. 1.客户端对SQL语句进行占位符的替换得到了完整的SQL语句
  4. 2.客户端发送完整SQL语句到MySQL服务端
  5. 3.MySQL服务端执行完整的SQL语句并将结果返回终端
  6. 预处理的执行过程
  7. 1.先把SQL语句拆分成两部分,SQL语句部分和参数部分
  8. 2.先把SQL语句部分发送给MySQL服务端进行SQL预处理
  9. 3.然后参数部分发送给MySQL服务端,MySQLSQL语句进行拼接
  10. 4.MySQL服务端执行完整的SQL语句返回结果
  11. 为什么要进行预处理?
  12. 1.为了优化MySQL服务器重复执行SQL的方法。可以执行服务器的性能,提前让服务器编译,一次编译多次执行,节省后续重复编译的成本
  13. 2.并且避免SQL注入
  • Go实现MySQL预处理
  1. // prepare方法现将SQL发送到MySQL服务端, 返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令 ; 命令也就是SQL语句
  2. // PrepareInsert 预处理执行插入语句
  3. func PrepareInsert() {
  4. defer wg.Done()
  5. sqlStr := `insert into test (username, password) values (?, ?)`
  6. // - 预处理 stmt 就是编译好的sql语句 之后直接传递参数即可
  7. stmt, err := db.Prepare(sqlStr)
  8. var u1 = uuid.Must(uuid.NewV4())
  9. CheckErr(err)
  10. defer stmt.Close()
  11. i := rand.Int()
  12. username := fmt.Sprintf("yonghuming%d", i)
  13. result, err := stmt.Exec(username, u1.String()[:10])
  14. CheckErr(err)
  15. rowsAffected, err := result.LastInsertId()
  16. CheckErr(err)
  17. fmt.Printf("成功插入id=%d条数据\n", rowsAffected)
  18. }
  • Go语言实现MySQL实现事务操作
  1. // go语言中使用一下三个方法实现MySQL中的事务操作, 开始事务
  2. func (db *DB) Begin()(*Tx, error)
  3. // 提交事务 相当与Python中的conn.commit()
  4. func (tx *Tx) Commit() error
  5. // 回滚事务
  6. func (tx *Tx) Rollback() error
  7. package main
  8. import (
  9. "database/sql"
  10. "fmt"
  11. _ "github.com/go-sql-driver/mysql"
  12. )
  13. var db *sql.DB
  14. type data struct {
  15. Username string `json:"username"`
  16. Password string `json:"password"`
  17. }
  18. // InitDB 数据库连接初始化
  19. func InitDB() (err error) {
  20. dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
  21. db, err = sql.Open("mysql", dsn)
  22. CheckErr(err)
  23. err = db.Ping()
  24. CheckErr(err)
  25. fmt.Println("数据库连接成功...")
  26. // 设置数据库连接池最大连接数
  27. db.SetMaxOpenConns(100)
  28. //设置最大闲置连接数
  29. db.SetMaxIdleConns(5)
  30. return
  31. }
  32. //CheckErr 异常捕获函数
  33. func CheckErr(err error) {
  34. if err != nil {
  35. fmt.Println(err)
  36. panic(err)
  37. }
  38. }
  39. // TranSaCtIon MySQL的事务操作
  40. func TranSaCtIon() {
  41. // 开启事务
  42. tx, err := db.Begin()
  43. CheckErr(err)
  44. // 执行多个SQL操作
  45. sqlStr := `update test set id=id+100000 where password=?`
  46. result, err := tx.Exec(sqlStr, "07f70f7e-4")
  47. CheckErr(err)
  48. id, err := result.LastInsertId()
  49. if err != nil {
  50. // 语句回滚
  51. err := tx.Rollback()
  52. fmt.Println("事务回滚")
  53. CheckErr(err)
  54. }
  55. fmt.Printf("修改后的id为%d\n", id)
  56. }
  57. func main() {
  58. err := InitDB()
  59. CheckErr(err)
  60. TranSaCtIon()
  61. }
  • sqlx使用

第三方库sqlx能够简化操作,提高开发效率

安装go get github.com/jmoiron/sqlx

  1. package main
  2. import (
  3. "fmt"
  4. _ "github.com/go-sql-driver/mysql"
  5. "github.com/jmoiron/sqlx"
  6. )
  7. var db *sqlx.DB
  8. // InitDB 数据库初始化
  9. func InitDB() (err error) {
  10. dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
  11. db, err = sqlx.Connect("mysql", dsn)
  12. CheckErr(err)
  13. db.SetMaxOpenConns(50)
  14. db.SetMaxIdleConns(10)
  15. fmt.Println("goUse 数据库连接成功")
  16. return
  17. }
  18. //CheckErr 异常捕获函数
  19. func CheckErr(err error) {
  20. if err != nil {
  21. fmt.Println(err)
  22. panic(err)
  23. }
  24. }
  25. func main() {
  26. err := InitDB()
  27. CheckErr(err)
  28. }

sqlx相较于原生的sql库好处在于 查询的时候sql原生的需要next scan 回调获取结果

sqlx 查询只需要定义一个存储的变量 然后自动就会将查询的出来的值放入变量中

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. _ "github.com/go-sql-driver/mysql"
  6. "github.com/jmoiron/sqlx"
  7. )
  8. var db *sqlx.DB
  9. type user struct {
  10. ID int `json:"id"`
  11. Username string `json:"username"`
  12. Password string `json:"password"`
  13. }
  14. // InitDB 数据库初始化
  15. func InitDB() (err error) {
  16. dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
  17. // Connect 就是连接的同时db.ping()一下
  18. db, err = sqlx.Connect("mysql", dsn)
  19. CheckErr(err)
  20. db.SetMaxOpenConns(50)
  21. db.SetMaxIdleConns(10)
  22. fmt.Println("goUse 数据库连接成功")
  23. return
  24. }
  25. // SelectDB 查询单条数据的方法
  26. func SelectDB() {
  27. sqlStr := `select * from test where id=?`
  28. var data user
  29. _ = db.Get(&data, sqlStr, 990)
  30. //CheckErr(err)
  31. fmt.Printf("%#v\n", data)
  32. marshal, err := json.Marshal(data)
  33. CheckErr(err)
  34. fmt.Println(string(marshal))
  35. }
  36. // ManySelect 查询多条数据方法
  37. func ManySelect() {
  38. sqlStr := `select * from test where id < ?`
  39. var dataList []user
  40. err := db.Select(&dataList, sqlStr, 1000)
  41. CheckErr(err)
  42. //fmt.Println(dataList)
  43. marshal, err := json.Marshal(dataList)
  44. CheckErr(err)
  45. fmt.Println(string(marshal))
  46. }
  47. //CheckErr 异常捕获函数
  48. func CheckErr(err error) {
  49. if err != nil {
  50. fmt.Println(err)
  51. panic(err)
  52. }
  53. }
  54. func main() {
  55. err := InitDB()
  56. CheckErr(err)
  57. SelectDB()
  58. ManySelect()
  59. }

Go操作Redis

安装go get -u github.com/go-redis/redis

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/go-redis/redis"
  5. )
  6. var redisDB *redis.Client
  7. // InitRedisDB redis数据库初始化
  8. func InitRedisDB() (err error) {
  9. redisDB = redis.NewClient(&redis.Options{
  10. Addr: "127.0.0.1:6379",
  11. Password: "",
  12. DB: 0,
  13. })
  14. _, err = redisDB.Ping(redisDB.Context()).Result()
  15. CheckErr(err)
  16. fmt.Println("redis 连接成功")
  17. return
  18. }
  19. //CheckErr 异常捕获函数
  20. func CheckErr(err error) {
  21. if err != nil {
  22. fmt.Println(err)
  23. panic(err)
  24. }
  25. }
  26. func main() {
  27. _ = InitRedisDB()
  28. }
  1. set(key, value):给数据库中名称为keystring赋予值value
  2. get(key):返回数据库中名称为keystringvalue
  3. getset(key, value):给名称为keystring赋予上一次的value
  4. mget(key1, key2,…, key N):返回库中多个stringvalue
  5. setnx(key, value):添加string,名称为key,值为value
  6. setex(key, time, value):向库中添加string,设定过期时间time
  7. mset(key N, value N):批量设置多个string的值
  8. msetnx(key N, value N):如果所有名称为key istring都不存在
  9. incr(key):名称为keystring1操作
  10. incrby(key, integer):名称为keystring增加integer
  11. decr(key):名称为keystring1操作
  12. decrby(key, integer):名称为keystring减少integer
  13. append(key, value):名称为keystring的值附加value
  14. substr(key, start, end):返回名称为keystringvalue的子串

NSQ分布式消息队列

NSQ是目前比较流行的一个分布式消息队列,下面主要是NSQ及GO语言如何操作NSQ

NSQ是GO语言编写的一个开源的实时分布式内存消息队列, 其性能十分优异, NSQ的优势有:

? 1.NSQ提倡分布式和扩散的拓扑,没有单点故障,支持容错和高可用性,并提供可靠的消息交付保证

? 2.NSQ支持横向扩展, 没有任何集中式代理

? 3.NSQ易于配置和部署,并且内置了管理界面

安装go get -u github.com/nsqio/go-nsq

Context

  1. Go HTTP 包的server中,每一个请求都在对应着一个响应,请求处理函数通常会启动额外的goroutine用来访问后端的服务,比如数据库和rpc服务,用来处理一个请求的goroutine通常需要访问一些与请求特定的数据,比如终端的身份认证信息、验证相关的token、请求和截止时间。当一个请求被取消或超时时,所有用来处理该请求的goroutine都应该迅速退出,然后系统才能释放这些goroutine

如何优雅的结束goroutine释放资源

  1. // 通道版本
  2. package main
  3. import (
  4. "fmt"
  5. "sync"
  6. "time"
  7. )
  8. var wg sync.WaitGroup
  9. func worker(exitChan <-chan struct{}) {
  10. defer wg.Done()
  11. Test:
  12. for {
  13. fmt.Println("worker")
  14. time.Sleep(time.Second)
  15. select {
  16. case <-exitChan:
  17. break Test
  18. default:
  19. }
  20. }
  21. }
  22. func main() {
  23. wg.Add(1)
  24. c := make(chan struct{})
  25. go worker(c)
  26. time.Sleep(10 * time.Second)
  27. c <- struct{}{}
  28. close(c)
  29. wg.Wait()
  30. fmt.Println("Over")
  31. }
  1. // Context版本
  2. package main
  3. import (
  4. "context"
  5. "fmt"
  6. "sync"
  7. "time"
  8. )
  9. var wg sync.WaitGroup
  10. func worker(ctx context.Context) {
  11. defer wg.Done()
  12. Test:
  13. for {
  14. fmt.Println("worker")
  15. time.Sleep(time.Second)
  16. select {
  17. case <-ctx.Done():
  18. break Test
  19. default:
  20. }
  21. }
  22. }
  23. func main() {
  24. wg.Add(1)
  25. ctx, cancelFunc := context.WithCancel(context.Background())
  26. go worker(ctx)
  27. time.Sleep(10 * time.Second)
  28. cancelFunc()
  29. wg.Wait()
  30. fmt.Println("Over")
  31. }

如果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对象更容易使用。

  1. package main
  2. import "log"
  3. func main() {
  4. log.Println("这是第一条工作日志")
  5. v := "THIS is worker log"
  6. log.Printf("%#v\n", v)
  7. // Fatal将会值写入信息之后,执行exit(1)
  8. log.Fatal("之后写一万行代码 我也不执行了哦")
  9. // 可以通过log.Panic 引发异常 会将日志写入之后引发异常
  10. log.Panic("测试panic的日志")
  11. }
  • flag选项(日志输出内容设置)
  1. log标准库提供了如下的flag选项,他们是一系列定义好的常量。
  2. const (
  3. Ldate = 1 << iota
  4. Ltime
  5. Lmicroseconds
  6. Llongfile
  7. Lshortfile
  8. LUTC
  9. LstdFlags = Ldate | Ltime
  10. )
  11. package main
  12. import "log"
  13. func main() {
  14. // 设置默认附加的内容
  15. log.SetFlags(log.Llongfile | log.Ltime)
  16. // 设置日志前缀
  17. log.SetPrefix("[go_log] ")
  18. log.Println("测试日志")
  19. }
  20. output>>>
  21. [go_log] 19:02:14 /Users/mac/GolandProjects/src/day02/go_log库/main.go:19: 测试日志
  • 配置日志输出位置

setoutput函数用来设置logger的输出目的地,默认是标准错误输出

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. )
  6. func main() {
  7. file, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
  8. if err != nil {
  9. log.Panic("文件打开失败")
  10. }
  11. // 设置了写入文件 日志内容就不会打印到终端了
  12. log.SetOutput(file)
  13. log.SetFlags(log.Llongfile | log.Ltime)
  14. log.SetPrefix("[go_log] ")
  15. log.Println("测试日志")
  16. }
  17. 我们可以定义一个init初始化函数 log全部配置好 这样更加标准化

第三方日志库logrus的使用

logrus是GO结构化的logger 与上边的logger标准库完全兼容

安装logrusgo get github.com/sirupsen/logrus

  1. package main
  2. import (
  3. log "github.com/sirupsen/logrus"
  4. )
  5. func main() {
  6. log.WithFields(log.Fields{
  7. "animals": "dog",
  8. "time": log.FieldKeyTime,
  9. }).Info("这是啥")
  10. }
  • 日志级别

Trace、debug、info、warning、error、fatal、panic

  1. log.Trace("跟踪?")
  2. log.Debug("Debug?")
  3. log.Info("信息")
  4. log.Warn("警告?")
  5. log.Error("Something failed but I'm not quitting.")
  6. // 记完日志后会调用os.Exit(1)
  7. log.Fatal("Bye.")
  8. // 记完日志后会调用 panic()
  9. log.Panic("I'm bailing.")
  • 日志记录
  1. package main
  2. import (
  3. "os"
  4. "time"
  5. log "github.com/sirupsen/logrus"
  6. )
  7. func main() {
  8. file, err := os.OpenFile("logrustest.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
  9. if err != nil {
  10. log.Panicln(err)
  11. }
  12. log.SetOutput(file)
  13. for i := 0; i < 100; i++ {
  14. log.WithFields(log.Fields{
  15. "animals": "dog",
  16. "Countey": "China",
  17. "City": "BeiJing",
  18. }).Info("这是啥")
  19. time.Sleep(time.Second)
  20. }
  21. log.Trace("跟踪?")
  22. log.Info("信息")
  23. log.Warn("警告?")
  24. // 设置日志级别, 会记录info以上级别(warn error fatal panic)
  25. log.SetLevel(log.InfoLevel)
  26. }
  27. >>>结果
  28. time="2021-02-04T12:00:15+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog
  29. time="2021-02-04T12:00:17+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog
  30. time="2021-02-04T12:00:18+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog
  31. 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

  1. log.SetFormatter(&log.JSONFormatter{})
  • 追踪函数
  1. log.SetReportCaller(true)
  2. 这样就会将哪个文件哪一行 都记录下来 但是不是特殊需求无需开启这个 因为会增加性能开销

原文链接:http://www.cnblogs.com/zjaiccn/p/14667326.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号