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语言操作数据库及其常规操作的更多相关文章

  1. c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--单表操作)

    一.概述 前面2篇文章,介绍了使用SqlCommand对象利用sql命令来操作数据库. 这篇文章我们来介绍使用c#的DataSet 和 DataAdaper对象操作操作数据库. 先来介绍下这两个对象是 ...

  2. c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--跨表操作)

    上篇文章我们介绍了如何利用DataSet 和 DataAdaper对象来对单张表进行操作. 本文我们将介绍如何进行跨表操作. 我们通过具体例子方式进行演示,例子涉及到三张表. 1)student表(学 ...

  3. 01.JDBC操作数据库-快速入门操作

    /** * 简单入门操作 * 注:先将mysql-connector-java-5.1.36.jar 构建 Build Path环境当中去 * @param args * @throws Except ...

  4. MySQL操作数据库

    2.操作数据库 操作数据库>操作数据库中的表>操作表中的数据 Mysql关键字不区分大小写 2.1操作数据库 2.1.1创建数据库  create database if not EXIS ...

  5. mysql服务器的常规操作

    mysql服务器的常规操作 导读 MySQL是一个小型关系数据库管理系统,目前被广泛的应用在Internet上的中小型网站中,体积小.速度快.总体拥有成本低,尤其是开放源码这一特点,使得许多中小型网站 ...

  6. Qt 学习之路 2(56):使用模型操作数据库

    Qt 学习之路 2(56):使用模型操作数据库 (okgogo: skip) 豆子 2013年6月20日 Qt 学习之路 2 13条评论 前一章我们使用 SQL 语句完成了对数据库的常规操作,包括简单 ...

  7. EF操作数据库的步骤和一些简单操作语句

    这里是写给我自己做记录的,不会写成一篇很好的博客,也不会置顶,如果有朋友看到了,而且觉得里面的内容不咋的,希望见谅哈! 关于这部分内容,这里推荐一篇总结的非常好的博客,如果你点击进来了,那么请略过下面 ...

  8. Flask中Mysql数据库的常见操作

    from flask import Flask,render_template #导入第三方链接库sql点金术 from flask_sqlalchemy import SQLAlchemy #建立对 ...

  9. Python操作数据库及hashlib模块

    一.hashlib模块 hashlib模块,主要用于加密相关的操作,在python3的版本里,代替了md5和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA51 ...

随机推荐

  1. PBN转弯保护区作图回顾

    假期的最后一天,是该小结一下的时候了. 风螺旋有了自己中式风格的Logo,大家是否喜欢? 过去的春节假期,我们从学习CAD入手,回顾了风螺旋在PBN中的多种情况,画了很多的图,写了不少的文字,或许现在 ...

  2. centos7 SNMP错误记录

    如果本地测试ok,远程测试出现如下报错: No Such Object available on this agent at this OID或No more variables left in th ...

  3. nacos服务注册之服务器端Distro

    一致性协议算法Distro阿里自己的创的算法吧,网上能找到的资料很少.Distro用于处理ephemeral类型数据 Distro协议算法看代码大体流程是: nacos启动首先从其他远程节点同步全部数 ...

  4. jQuery实现全网热播视频

    <section id="play"> <h1>全网热播视频</h1> <ul> <li><img src=&qu ...

  5. dubbo实战之三:使用Zookeeper注册中心

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. 选择 FreeBSD 而不是 Linux 的技术性原因4

    Linux 二进制兼容性 FreeBSD 提供了与 Linux 的二进制兼容.这使得用户可以在 FreeBSD 系统上安装和运行许多 Linux 二进制文件, 而无需首先修改二进制文件.在某些特定情况 ...

  7. 「NOIP模拟赛」Round 3

    Tag 计数+LIS, 二分+ST表, 计数+记搜 A. 改造二叉树 Description 题面 Solution 如果目标序列非严格递增,或者说目标序列是不下降的,那么答案就是 \(n\) 减去最 ...

  8. PTE 准备之 Read aloud

    Read aloud A text appears on screen.Read the text aloud rext up tp 60 words varies by task, dependin ...

  9. Nuxt.js vue服务端渲染

    一.为什么要用Nuxt.js 原因其实不用多说,就是利用Nuxt.js的服务端渲染能力来解决Vue项目的SEO问题. 二.Nuxt.js和纯Vue项目的简单对比 1. build后目标产物不同 vue ...

  10. Dart 学习

    语言特性 Dart所有的东西都是对象, 即使是数字numbers.函数function.null也都是对象,所有的对象都继承自Object类. Dart动态类型语言, 尽量给变量定义一个类型,会更安全 ...