Go语言操作数据库及其常规操作
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语言操作数据库及其常规操作的更多相关文章
- c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--单表操作)
一.概述 前面2篇文章,介绍了使用SqlCommand对象利用sql命令来操作数据库. 这篇文章我们来介绍使用c#的DataSet 和 DataAdaper对象操作操作数据库. 先来介绍下这两个对象是 ...
- c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--跨表操作)
上篇文章我们介绍了如何利用DataSet 和 DataAdaper对象来对单张表进行操作. 本文我们将介绍如何进行跨表操作. 我们通过具体例子方式进行演示,例子涉及到三张表. 1)student表(学 ...
- 01.JDBC操作数据库-快速入门操作
/** * 简单入门操作 * 注:先将mysql-connector-java-5.1.36.jar 构建 Build Path环境当中去 * @param args * @throws Except ...
- MySQL操作数据库
2.操作数据库 操作数据库>操作数据库中的表>操作表中的数据 Mysql关键字不区分大小写 2.1操作数据库 2.1.1创建数据库 create database if not EXIS ...
- mysql服务器的常规操作
mysql服务器的常规操作 导读 MySQL是一个小型关系数据库管理系统,目前被广泛的应用在Internet上的中小型网站中,体积小.速度快.总体拥有成本低,尤其是开放源码这一特点,使得许多中小型网站 ...
- Qt 学习之路 2(56):使用模型操作数据库
Qt 学习之路 2(56):使用模型操作数据库 (okgogo: skip) 豆子 2013年6月20日 Qt 学习之路 2 13条评论 前一章我们使用 SQL 语句完成了对数据库的常规操作,包括简单 ...
- EF操作数据库的步骤和一些简单操作语句
这里是写给我自己做记录的,不会写成一篇很好的博客,也不会置顶,如果有朋友看到了,而且觉得里面的内容不咋的,希望见谅哈! 关于这部分内容,这里推荐一篇总结的非常好的博客,如果你点击进来了,那么请略过下面 ...
- Flask中Mysql数据库的常见操作
from flask import Flask,render_template #导入第三方链接库sql点金术 from flask_sqlalchemy import SQLAlchemy #建立对 ...
- Python操作数据库及hashlib模块
一.hashlib模块 hashlib模块,主要用于加密相关的操作,在python3的版本里,代替了md5和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA51 ...
随机推荐
- 「NGK每日快讯」12.14日NGK公链第41期官方快讯!
- HGAME apache
HGAME apache Linux下六十四位可执行文件 IDA找主函数 输入长度为35的字符串,经过一次函数处理,之后if条件函数的返回值为1,就能判定输入就是flag 函数sub_1447的参数 ...
- VOR/DME程序进近、复飞保护区的绘制
今天尝试画一个典型的VOR/DME进近程序保护区. 读图 某机场VOR/DME进近程序平面图部分如下图所示: 该程序剖面图部分如下图所示: 分析 该机场采用了偏置导航台布局(导航台在机场内), ...
- Scrapy 项目:腾讯招聘
目的: 通过爬取腾讯招聘网站(https://careers.tencent.com/search.html)练习Scrapy框架的使用 步骤: 1.通过抓包确认要抓取的内容是否在当前url地址中,测 ...
- 1090 Highest Price in Supply Chain——PAT甲级真题
1090 Highest Price in Supply Chain A supply chain is a network of retailers(零售商), distributors(经销商), ...
- window.onresize绑定事件以及解绑事件
问题描述 在Vue工程中,添加样式,部分需要做到自适应,需要添加resize事件,由于是单页面应用,如果组件初始化的时候绑定事件,在切换页面的时候不去注销事件,如果来回切换,会让resize事件执行多 ...
- 又长又细,万字长文带你解读Redisson分布式锁的源码
前言 上一篇文章写了Redis分布式锁的原理和缺陷,觉得有些不过瘾,只是简单的介绍了下Redisson这个框架,具体的原理什么的还没说过呢.趁年前项目忙的差不多了,反正闲着也是闲着,不如把Rediss ...
- Vue框架简介及简单使用
目录 一.前端框架介绍 二.vue框架简介 三.vue使用初体验 1. vue如何在页面中引入 2. 插值表达式 3. 文本指令 4. 方法指令(事件指令) 5. 属性指令 四.js数据类型补充 1. ...
- golang知识总结
目录 1.slice扩容规则 2.内存寻址.内存对齐,go结构体内存对齐策略 3.go语言map类型分析 3.1 hash冲突 3.2 hash表扩容 3.3 go语言中的map结构是hash表. 3 ...
- DCL之单例模式
所谓的DCL 就是 Double Check Lock,即双重锁定检查,在了解DCL在单例模式中如何应用之前,我们先了解一下单例模式.单例模式通常分为"饿汉"和"懒汉&q ...