MySQL
摘要:
MySQL 是最流行的关系型数据库管理系统,后端必会!!
下面我们对MySQL做一个系统的介绍:
数据库
常见的数据库SQLlite、MySQL、 SQLServer、postgreSQL、Oracle
主流的关系型数据库,类似的还有postgreSQL
关系型数据库:
用表来存一类数据。
表结构设计的三大范式:《漫画数据库》
MySQL知识点
SQL语句
DDL: 操作数据库的
DML: 表的增删改查
DCL:用户及权限
存储引擎
MySQL 支持插件式的存储引擎。
常见的存储引擎:MylSAM和InnoDB
MyLSAM:
1.查询速度快
2.只支持表锁
3.不支持事务
InnoDB:
1.整体速度快
2.支持表锁和行锁
3.支持事务
事务:
把多个操作当成一个整体
事务的特点:
ACID:
1.原子性:事务要么成功要么失败,没有中间状态。
2.一致性:数据库的完整性没有被破坏。
3.隔离性:事务之间是相互隔离的。
(1)隔离的四个级别
4.持久性:事务操作的结果是不会丢失的。
索引:
索引的原理:B树和B+树
索引的类型
索引的命中
分库分表
SQL注入
SQL慢查询优化
MySQL主从:
binlog
MySQL读写分离
Mysql安装
①安装服务:
②初始化:
1
| mysqld --initialize --console
|
③开启服务:
④关闭服务:
⑤登录mysql:
Enter Password:(密码)
⑥修改密码:
1
| alter user 'root'@'localhost' identified by 'root'(by 接着的是密码)
|
⑦标记删除mysql服务:
Go操作MySQL:
database/sql
原生支持连接池,是并发安全的
这个标准库没有具体的实现,知识列出了一些需要第三方库实现的具体内容
下载驱动
1
| go get -u github.com/go-sql-driver/mysql
|
go get
包的路径就是下载第三方的依赖
将第三方的依赖默认保存在&GOPATH/src
使用
初始化连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package main
import ( "database/sql" "fmt" _"github.com/go-sql-driver/mysql" )
func main() { dsn:= "root:Polaris6G123@tcp127.0.0.1:3306/sql_test" db, err := sql.Open("mysql",dsn) if err !=nil{ fmt.Printf("dsn:%s invaild, err:%v\n",dsn,err) return } err = db.Ping() if err != nil{ fmt.Printf("open %s failes, err:%v\n",dsn,err) return } fmt.Println("连接数据库资源成功!") }
|
SetMaxOpenConns
1
| func (db *DB) SetMaxOpenConns(n int)
|
SetMaxOpenConns
设置与数据库建立连接的最大数目。 如果n大于0且小于最大闲置连接数,会将最大闲置连接数减小到匹配最大开启连接数的限制。 如果n<=0,不会限制最大开启连接数,默认为0(无限制)。
SetMaxIdleConns
1
| func (db *DB) SetMaxIdleConns(n int)
|
SetMaxIdleConns设置连接池中的最大闲置连接数。 如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制。 如果n<=0,不会保留闲置连接。
单条+多条查询
单行查询db.QueryRow()
执行一次查询,并期望返回最多一行结果(即Row)。QueryRow总是返回非nil的值,直到返回值的Scan方法被调用时,才会返回被延迟的错误。(如:未找到结果)
1
| func (db *DB) QueryRow(query string, args ...interface{}) *Row
|
多行查询db.Query()执行一次查询,返回多行结果(即Rows),一般用于执行select命令。参数args表示query中的占位参数。
1
| func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| package main
import ( "database/sql" "fmt" _"github.com/go-sql-driver/mysql" )
var db *sql.DB
func initDB()(err error){ dsn:= "root:Polaris6G@tcp(127.0.0.1:3306)/sql_test" db, err = sql.Open("mysql",dsn) if err !=nil{ return } err = db.Ping() if err != nil{ return } db.SetMaxOpenConns(10) db.SetMaxIdleConns(5) return } type user struct { id int name string age int }
func queryOne(id int) { var u1 user sqlStr := `select id, name, age from user where id=?;` rowObj :=db.QueryRow(sqlStr,id) rowObj.Scan(&u1.id, &u1.name, &u1.age ) fmt.Printf("u1: %v\n", u1) }
func queryMore(n int){ sqlStr := `select id ,name,age from user where id>=?;` rows, err :=db.Query(sqlStr, n) if err != nil{ fmt.Printf("exed %s query failed, err:%v\n",sqlStr, err) return } defer rows.Close() for rows.Next() { var u2 user err := rows.Scan(&u2.id, &u2.name, &u2.age) if err != nil{ fmt.Printf("scan failed,err=%v\n ",err) return } fmt.Printf("u2:%v\n",u2) } return } func main() { err := initDB() if err != nil { fmt.Printf("init DB failed, err:%v\n", err) return } fmt.Println("连接数据库成功!") queryOne(2) queryMore(2) return }
|
插入、更新和删除操作都使用Exec
方法。
1
| func (db *DB) Exec(query string, args ...interface{}) (Result, error)
|
插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func insert(){ sqlStr :=`insert into user(name, age)values(?, ?)` ret, err := db.Exec(sqlStr,"Jason", 23) if err !=nil{ fmt.Printf("insert failed,err:%v\n",err) return } id, err := ret.LastInsertId() if err !=nil{ fmt.Printf("get id failed,err:%v\n", err) return } fmt.Println("id:",id) num, err :=ret.RowsAffected() if err !=nil{ fmt.Printf("get row number failed,err:%v\n", err) return } fmt.Println("affected row:",num) }
|
更新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func updaterow(newage int, id int) { sqlStr := "update user set age=? where id =?" ret, err := db.Exec(sqlStr, newage, id) if err != nil { fmt.Printf("update failed,err:%v\n", err) return } n, err := ret.RowsAffected() if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("update success, affected rows:%d\n", n) }
|
删除数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func deleterow(id int){ sqlStr := `delete from user where id=?` ret, err :=db.Exec(sqlStr, id) if err != nil { fmt.Printf("delete failed,err:%v\n", err) return } n, err := ret.RowsAffected() if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("delete success, affected rows:%d\n", n) }
|
预处理
什么是预处理?
普通SQL语句执行过程:
- 客户端对SQL语句进行占位符替换得到完整的SQL语句。
- 客户端发送完整SQL语句到MySQL服务端
- MySQL服务端执行完整的SQL语句并将结果返回给客户端。
预处理执行过程:
- 把SQL语句分成两部分,命令部分与数据部分。
- 先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
- 然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
- MySQL服务端执行完整的SQL语句并将结果返回给客户端。
为什么要预处理?
- 优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。
- 避免SQL注入问题。
具体实现
database/sql
中使用下面的Prepare
方法来实现预处理操作。
1
| func (db *DB) Prepare(query string) (*Stmt, error)
|
Prepare
方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func prepareinsert(){ sqlStr :=`insert into user(name, age)values(?,?)` stmt, err := db.Prepare(sqlStr) if err!=nil { fmt.Printf("prepare failed,err:%v\n", err) } defer stmt.Close() var m = map[string]int{ "王铁柱":33, "田二妞":32, } for k, v :=range m{ stmt.Exec(k,v) } }
|
对应关系:
数据表<—>结构体
数据行<—>结构体实例
字段<—>结构体字段
1 2 3 4 5 6 7 8 9 10
| type UserInfo struct{ ID uint Name string Gender string Hobby string } func main(){ u1:=UserInfo{1,"Polaris","男","足球"} }
|