[TOC] #數據庫 不少時候咱們都是把數據存儲在文件中,例如:xxx.txt,讀取都須要對文件進行操做,那若是數據特別多,咱們就得建立N個文件,而且很是很差管理,性能也差,因此就出來了數據庫
這個概念,數據庫
本生也是一套軟件系統,它存儲數據的本質仍是文件,有了數據庫這套系統,咱們只須要學會怎麼去使用這套系統便可mysql
常見的數據庫:SQLlite、MySQL、Mongodb、Oraclegit
#MySQL簡介 MySQL是一個開源的關係型數據庫,它擁有很強大的SQL語句,咱們經過SQL語句實現數據的增刪改查。github
SQL語句 DDL:操做數據庫 DML:表的增刪改查 DCL:用戶及權限sql
存儲引擎 MySQL支持插件式的存儲引擎,常見的有:MyISAM和InnoDB。數據庫
MyISAM:安全
- 查詢速度快
- 只支持表鎖
- 不支持事務 InnoDB:
- 總體速度快
- 支持表鎖和行鎖
- 支持事務(多個SQL操做當成一個總體)
#Go操做MySQL Go語言中的database/sql
包提供了保證SQL或類SQL數據庫的範接口,並不提供具體的數據庫驅動。使用database/sql
包時必須注入(至少)一個數據庫驅動,原生支持鏈接池,是併發安全的。bash
下載驅動服務器
go get -u github.com/go-sql-driver/mysql
使用MySQL驅動併發
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" // init() ) var db *sql.DB // 是一個鏈接池對象 func initDB()(err error){ // 數據庫信息 dsn := "root:123456@tcp(10.4.7.51:3306)/test_sql" // 鏈接數據庫 db, err = sql.Open("mysql",dsn) // 不會校驗用戶名和密碼是否正確 if err != nil { // dsn 格式不正確的時候會報錯 return } err = db.Ping() // 嘗試鏈接數據庫 if err != nil { return } return } // Go鏈接MySQL示例 func main(){ err := initDB() if err !=nil { fmt.Printf("init DB failed,err: %v\n",err) return } fmt.Println("鏈接數據庫成功") }
設置數據庫最大鏈接數tcp
db.SetMaxOpenConns(100)
設置數據庫最大閒置鏈接數
db.SetMaxIdleConns(10)
#CRUD ##建庫建表 咱們先在MySQL中建立一個名爲test_sql
的數據庫
CREATE DATABASE test_sql
進入數據庫
user test_sql
執行如下命令建立用於測試的表結構
CREATE TABLE `user` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT '', `age` INT(11) DEFAULT '0', PRIMARY KEY(`id`) )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
插入多條測試數據
insert into user(name,age) values("Jack",9000); insert into user(name,age) values("Xander",31); insert into user(name,age) values("Eric",25); insert into user(name,age) values("Alim",21); insert into user(name,age) values("小明",22); insert into user(name,age) values("大力哥",30);
#查詢 ##單行查詢 單行查詢db.QueryRow()
執行一次查詢,並最多返回去一行結果(ROW)。QueryRow老是返回非nil的值,直到返回值的Scan方法被調用時,纔會返回去被延遲的錯誤(如:未找到結果)
func queryOnceDemo(id int){ var u1 user // 寫查詢單條記錄的SQL語句 sqlStr := `select id,name,age from user where id=?` // 執行 db.QueryRow(sqlStr,id).Scan(&u1.id,&u1.name,&u1.age) // 從鏈接池裏拿一個鏈接出來去數據庫查詢單條記錄 // 打印結構 fmt.Printf("u1:%#v\n",u1) }
##多行查詢 多行查詢db.Query()
執行一次查詢,返回多行結果(即Rows),通常用於運行select命令。
func queryMultDemo(n int){ // sql語句 sqlStr := `select id,name,age from user where id > ?;` // 執行 rows, err := db.Query(sqlStr,n) if err != nil{ fmt.Sprintf("exec %s query failled,err:%v\n",sqlStr,err) return } // 必定要關閉rows defer rows.Close() // 循環取值 for rows.Next() { var u1 user err := rows.Scan(&u1.id,&u1.name,&u1.age) if err != nil { fmt.Printf("scan failed,err:%v\n",err) } fmt.Printf("u1:%#v\n",u1) } }
#插入數據 Exec執行一次命令(包括查詢、刪除、更新、插入等),返回的Result是對已執行的SQL命令的總結
func insertRowDemo(){ // 寫SQL語句 sqlStr := `insert into user(name,age) values("老王",28)` // exec ret, err := db.Exec(sqlStr) if err != nil { fmt.Printf("insert failed,err:%v\n",err) return } // 若是是插入數據的操做,可以拿到插入數據的id id, err := ret.LastInsertId() if err != nil { fmt.Printf("get last id failed,err:$v",err) return } fmt.Println("id:",id) }
#更新數據
func updateRowDemo(newAge,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 last id failed,err:$v",err) return } fmt.Printf("update row :%v\n",n) }
#刪除數據
func deleteRowDemo(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 last id failed,err:$v",err) return } fmt.Printf("delete row :%v\n",n) }
#MySQL預處理 優化MySQL服務端重複執行SQL的方法,能夠提高服務器性能,提早讓服務端編譯,一次編譯屢次執行,節省後續編譯的成本,也能避免SQL注入的問題。
Go中的Prepare
方法會先將sql語句發送到MySQL服務端,返回一個準備好的狀態用於以後的查詢和命令。返回值能夠同時執行多個查詢和命令。
查詢操做的預處理示例代碼以下:
// 預處理查詢示例 func prepareQueryDemo() { sqlStr := "select id, name, age from user where id > ?" stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed, err:%v\n", err) return } defer stmt.Close() rows, err := stmt.Query(0) if err != nil { fmt.Printf("query failed, err:%v\n", err) return } defer rows.Close() // 循環讀取結果集中的數據 for rows.Next() { var u user err := rows.Scan(&u.id, &u.name, &u.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age) } }
插入、更新和刪除操做的預處理十分相似,這裏以插入操做的預處理爲例:
// 預處理插入示例 func prepareInsertDemo() { sqlStr := "insert into user(name, age) values (?,?)" stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed, err:%v\n", err) return } defer stmt.Close() _, err = stmt.Exec("小王子", 18) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } _, err = stmt.Exec("沙河娜扎", 18) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } fmt.Println("insert success.") }
#MySQL事務操做 什麼是事務? 事務:一個最小的不可再分的工做單元;一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元),同時這個完整的業務須要執行屢次的DML(insert、update、delete)語句共同聯合完成。A轉帳給B,這裏面就須要執行兩次update操做。
在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務。事務處理能夠用來維護數據庫的完整性,保證成批的SQL語句要麼所有執行,要麼所有不執行。 事務:一個最小的不可再分的工做單元;一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元),同時這個完整的業務須要執行屢次的DML(insert、update、delete)語句共同聯合完成。A轉帳給B,這裏面就須要執行兩次update操做。
在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務。事務處理能夠用來維護數據庫的完整性,保證成批的SQL語句要麼所有執行,要麼所有不執行。
事務相關方法 Go語言中使用如下三個方法實現MySQL中的事務操做。 開始事務
func (db *DB) Begin() (*Tx, error)
提交事務
func (tx *Tx) Commit() error
回滾事務
func (tx *Tx) Rollback() error
事務示例 下面的代碼演示了一個簡單的事務操做,該事物操做可以確保兩次更新操做要麼同時成功要麼同時失敗,不會存在中間狀態。
// 事務操做示例 func transactionDemo() { tx, err := db.Begin() // 開啓事務 if err != nil { if tx != nil { tx.Rollback() // 回滾 } fmt.Printf("begin trans failed, err:%v\n", err) return } sqlStr1 := "Update user set age=30 where id=?" _, err = tx.Exec(sqlStr1, 2) if err != nil { tx.Rollback() // 回滾 fmt.Printf("exec sql1 failed, err:%v\n", err) return } sqlStr2 := "Update user set age=40 where id=?" _, err = tx.Exec(sqlStr2, 4) if err != nil { tx.Rollback() // 回滾 fmt.Printf("exec sql2 failed, err:%v\n", err) return } err = tx.Commit() // 提交事務 if err != nil { tx.Rollback() // 回滾 fmt.Printf("commit failed, err:%v\n", err) return } fmt.Println("exec trans success!") }
#sqlx使用 第三方庫sqlx可以簡化操做,提升開發效率。
安裝
go get github.com/jmoiron/sqlx
鏈接數據庫
var db *sqlx.DB func initDB() (err error) { dsn := "user:password@tcp(127.0.0.1:3306)/test" // 也可使用MustConnect鏈接不成功就panic db, err = sqlx.Connect("mysql", dsn) if err != nil { fmt.Printf("connect DB failed, err:%v\n", err) return } db.SetMaxOpenConns(20) db.SetMaxIdleConns(10) return }
查詢 查詢單行數據示例代碼以下:
// 查詢單條數據示例 func queryRowDemo() { sqlStr := "select id, name, age from user where id=?" var u user err := db.Get(&u, sqlStr, 1) if err != nil { fmt.Printf("get failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) }
查詢多行數據示例代碼以下:
// 查詢多條數據示例 func queryMultiRowDemo() { sqlStr := "select id, name, age from user where id > ?" var users []user err := db.Select(&users, sqlStr, 0) if err != nil { fmt.Printf("query failed, err:%v\n", err) return } fmt.Printf("users:%#v\n", users) }
插入、更新和刪除 sqlx中的exec方法與原生sql中的exec使用基本一致:
// 插入數據 func insertRowDemo() { sqlStr := "insert into user(name, age) values (?,?)" ret, err := db.Exec(sqlStr, "沙河小王子", 19) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } theID, err := ret.LastInsertId() // 新插入數據的id if err != nil { fmt.Printf("get lastinsert ID failed, err:%v\n", err) return } fmt.Printf("insert success, the id is %d.\n", theID) } // 更新數據 func updateRowDemo() { sqlStr := "update user set age=? where id = ?" ret, err := db.Exec(sqlStr, 39, 6) 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) } // 刪除數據 func deleteRowDemo() { sqlStr := "delete from user where id = ?" ret, err := db.Exec(sqlStr, 6) 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) }
事務操做 對於事務操做,咱們可使用sqlx中提供的db.Beginx()和tx.MustExec()方法來簡化錯誤處理過程。示例代碼以下:
func transactionDemo() { tx, err := db.Beginx() // 開啓事務 if err != nil { if tx != nil { tx.Rollback() } fmt.Printf("begin trans failed, err:%v\n", err) return } sqlStr1 := "Update user set age=40 where id=?" tx.MustExec(sqlStr1, 2) sqlStr2 := "Update user set age=50 where id=?" tx.MustExec(sqlStr2, 4) err = tx.Commit() // 提交事務 if err != nil { tx.Rollback() // 回滾 fmt.Printf("commit failed, err:%v\n", err) return } fmt.Println("exec trans success!") }