go与数据库
数据库在后端开发中十分重要,这篇博客主要总结用go去操作数据库的相关知识。包括数据库的简单介绍,重点是用go控制MySQL。
1. 数据库简介
关系型数据库:MySQL、SQLlite(嵌入式)、postgreSQL...... 用表存一类数据。
1. SQL 语句
DDL
DML
DCL
2. 存储引擎
常见的如MYISAM,InnoDB。
3. 索引
略。
4. 事务
ACID 四个特性。四个隔离级别。
5. go操作MySQL
go自带的包:database/sql 原生支持连接池并且是并发安全的。
没有具体的实现,列出了一些实现标准。
下载mysql依赖:go get ... 相关方法实现在这里。
go get -u github.com/go-sql-driver/mysql
连接数据库:sql.Open() 、db.Ping()
// 全局
var db *sql.DB
// 数据库用户名:密码@连接对象/具体哪一个数据库
dsn := "root:m3479735881@tcp(127.0.0.1:3306)/go_study"
// Open 时不校验用户名和密码,而是检查dsn的格式,返回值db就是一个数据库的连接池
// 这里一定不是 := ,因为前面声明过了!
db, err = sql.Open("mysql", dsn)
if err != nil {
return
}
// 这里校验
err = db.Ping()
if err != nil {
return
}
// 可以设置db这个数据库连接池中连接的最大个数
db.SetMaxOpenConns(10)
// 设置最大闲置连接数
// db.SetMaxIdleConns(8)
把db对象放在全局,这样各个函数里就可以共享。db就像是一个句柄,里面有数据库的相关信息。
单行查询:db.QueryRow()
// 全局
type stu struct {
id int
name string
age int
}
var stu1 stu
// 1. 查询
sqlStr := "select id,name,age from student where id=?;"
// 从连接池中取出一个连接去查询。
ret := db.QueryRow(sqlStr, i)
// 取出结果放到结构体里,同时Scan内部会释放连接! !所以一般必须调用Scan释放连接
ret.Scan(&stu1.id, &stu1.name, &stu1.age)
fmt.Printf("%#v\n", stu1
需要注意最后一定要调用Scan ,因为Scan 内部会释放该连接,不释放就会造成连接浪费。
多行查询:db.Query()
sqlStr := "select id,name,age from student where id > ?;"
rets, err := db.Query(sqlStr, i)
if err != nil {
fmt.Printf("Query error:%#v\n", err)
return
}
// 这个要自己手动关闭!
defer rets.Close()
// 循环取值
var stu1 stu
for rets.Next() {
rets.Scan(&stu1.id, &stu1.name, &stu1.age)
fmt.Printf("%#v\n", stu1)
}
一定注意使用的连接要手动关闭!
插入数据、更新数据、删除数据:db.Exec()
// 插入数据
sqlStr := "insert into student values (?, ?, ?)"
req, err := db.Exec(sqlStr, id, name, age)
if err != nil {
fmt.Printf("Exec error:%#v\n", err)
return
}
// 获取插入数据的ID
req.LastInsertId()
// 获取受影响的行数
req.RowsAffected()
// 修改数据
sqlStr := "update student set age=1111 where id=1;"
res, err := db.Exec(sqlStr)
if err != nil {
fmt.Printf("Exec error:%#v\n", err)
return
}
// 获取修改数据的ID
res.LastInsertId()
// 获取受影响的行数
res.RowsAffected()
// 删除数据
// 差别不大!!!
6. MySQL的预处理
把多条SQL语句的命令部分和数据部分 分开发给服务端,可以对同一类的语句做优化,提升效率。还可以避免SQL注入问题。适用于批量处理同一类的语句。
普通SQL语句执行过程:
- 客户端对
SQL语句进行占位符替换得到完整的SQL语句。 - 客户端发送完整
SQL语句到MySQL服务端 MySQL服务端执行完整的SQL语句并将结果返回给客户端。
预处理执行过程:
- 把
SQL语句分成两部分,命令部分与数据部分。 - 先把命令部分发送给
MySQL服务端,MySQL服务端进行SQL预处理。 - 然后把数据部分发送给
MySQL服务端,MySQL服务端对SQL语句进行占位符替换。 MySQL服务端执行完整的SQL语句并将结果返回给客户端。
sqlStr := "insert into student values (?,?,?)"
// 预处理
stmt, err := db.Prepare(sqlStr)
if err != nil {
fmt.Printf("prepare error:%#v\n", err)
return
}
defer stmt.Close()
// 插入,填入之前的占位符即可
req, err := stmt.Exec(10, "asdfsadf", 123)
if err != nil {
fmt.Printf("prepare error:%#v\n", err)
return
}
req.LastInsertId()
// req.RowsAffected()
7. MySQL注入
==永远不要自己拼接SQL 语句!!==
// 自己拼接:
func select(name string){
// 拼接,这时若name乱搞,就可能会有问题!!
sqlStr := fmt.Sprintf("select id,name from student where name=%s", name)
}
8. sqlx使用
是对标准库database/sql 的替代。需要安装依赖。
就是比标准库的某些基础操作简单一点点。其实就用标准库就行。
2. Redis
KV数据库
下载第三方依赖:
go get github.com/go-redis/redis/v8
3. NSQ
分布式==消息队列。==
支持横向扩展;支持容错和高可用性;提供可靠的消息交付保证。
使用场景:同步处理转为异步处理(异步化);应用解耦;流量削峰(某一时刻流量非常大时提供一定的缓冲).......
以上就是go和数据库的简单介绍,有兴趣的可以继续深入了解。