帶你了解Go怎樣實作二級緩存
一、需求
- 實作二級緩存
- 程式運作起來後提示:”請輸入指令:“,如果輸入
,查詢并顯示所有人員的資訊getall
- 第一次時查詢
并将結果緩存在mysql
,設定60秒的過期時間redis
- 以後的每次查詢,如果
有資料就從redis
加載,沒有則重複上一步的操作redis
二、實作連接配接Mysql并執行查詢語句
先實作需求二,當輸入指令
getall
時,查詢并顯示所有人員的資訊。
package main
import (
"fmt"
_"github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type Human struct {
Name string `db:"name"`
Age int `db:"age"`
}
func main() {
var cmd string
for{
fmt.Println("請輸入指令:")
fmt.Scan(&cmd)
switch cmd{
case "getall":
//顯示所有人的資訊
GetAllPeople()
case "exit":
//退出程式
goto GAMEOVER
default:
fmt.Println("輸入的指令有誤,請重新輸入!")
}
}
GAMEOVER:
fmt.Println("GAME OVER")
}
func GetAllPeople() {
fmt.Println("allPeople")
//先嘗試拿緩存
GetPeopleFromRedis()
db, _ := sqlx.Connect("mysql", "root:123456@tcp(localhost:3306)/mydb")
defer db.Close()
var people []Human
err := db.Select(&people, "select name,age from person")
if err!=nil{
fmt.Println("查詢失敗!err=",err)
}
fmt.Println(people)
CachePeople2Redis(people)
}
第一步還是導包,需要在mysql驅動包前面加上下劃線
_
,因為它隻是一個驅動檔案,并不需要在代碼中調用它的有關API接口.
接下來的這個結構體中後面的
db:"name"
db:"age"
一定要加反單引号,否則運作時會報錯。(傻傻的編者剛開始這裡就出現問題啦~)
type Human struct {
Name string `db:"name"`
Age int `db:"age"`
}
然後main函數裡面都是一些基本文法知識,用了
switch
和
goto
這兩個内容。
接下來就是連接配接資料庫了,這裡要用到資料庫擴充包
Sqlx
,
Sqlx包
其實最大最大的優點是在查詢方面,也就是使用select時優化得比較好。比原來的使用查詢友善了不止一點。
db, _ := sqlx.Connect("mysql", "root:123456@tcp(localhost:3306)/mydb")
driverName:mysql,表示驅動器的名稱是mysql也就上面"github.com/go-sql-driver/mysql"導入的驅動器。
dataSourceName是root:123456@tcp(localhost:3306)/mydb 它的含義是 賬戶名:密碼@tcp(ip:端口)/資料庫名稱。
将緩存查詢結果到Redis,就是通過這個函數CachePeople2Redis(people)。
三、寫一個錯誤處理函數
func HandleError(err error,why string) {
if err != nil{
fmt.Println(err,why)
os.Exit(1)
}
}
因為後面需要處理很多錯誤,而錯誤處理也是GO的一個特性,是以我們這先寫一個錯誤處理函數。
四、設定二級緩存
func CachePeople2Redis(people []Human) {
conn, _ := redis.Dial("tcp", "localhost:6379")
defer conn.Close()
for _,human := range people{
humanStr := fmt.Sprint(human)
_, err := conn.Do("rpush", "people", humanStr)
if err != nil{
fmt.Println("緩存失敗(rpush people),err=",err)
return
}
}
_, err := conn.Do("expire", "people", 66)
if err!=nil{
HandleError(err,"@expire people 60")
}
fmt.Println("緩存成功!")
}
redis.Dial()
這個函數是用來連接配接redis的,需要給定網絡協定和IP位址及端口号,redis的端口号預設為6379.
defer conn.Close()
表示延時結束與redis的連接配接,為了節省系統的io資源,需要及時關閉連接配接!剛入門時我們很容易忘記這個,需要我們養成習慣!
conn.Do()
是用來執行資料庫指令的,第一個參數是指令名,後面的參數是資料庫指令的參數。它傳回的結果中reply是位元組數組[]byte類型,需要根據具體的業務類型進行資料類型轉換。
這段代碼先将people數組中的每一個human放入到redis的people清單中。然後再執行expire指令,将清單設定過期時間。
執行成功!下面是運作結果:
請輸入指令:
getall
allPeople
[{大揚 21} {小飛 21} {大紅袍 1} {小芳 18}]
緩存成功!
請輸入指令:
然後去看看資料庫裡面存進去沒有。
127.0.0.1:6379> lrange people 0 -1
1) "{\xe5\xa4\xa7\xe6\x89\xac 21}"
2) "{\xe5\xb0\x8f\xe9\xa3\x9e 21}"
3) "{\xe5\xa4\xa7\xe7\xba\xa2\xe8\xa2\x8d 1}"
4) "{\xe5\xb0\x8f\xe8\x8a\xb3 18}"
過了一分鐘之後,再檢視redis資料庫内的資料。
127.0.0.1:6379> lrange people 0 -1
(empty list or set)
已經消失了。
再寫一個函數:
func GetPeopleFromRedis() (peopleStrs []string) { //連資料庫 conn, _ := redis.Dial("tcp", "localhost:6379") //延遲關閉 defer conn.Close() //執行指令 reply, err := conn.Do("lrange", "people", 0, -1) //處理錯誤 HandleError(err,"@lrange people 0 -1") //類型轉換 peopleStrs, err = redis.Strings(reply, err) //列印結果 fmt.Println("緩存拿取結果:",peopleStrs,err) return}
如果
redis
裡面有就不需要從
mysql
裡面取資料了。直接從
redis
裡面利用
lrange
指令來擷取people的所有值。