天天看點

帶你了解Go怎樣實作二級緩存

帶你了解Go怎樣實作二級緩存

一、需求

  • 實作二級緩存
  • 程式運作起來後提示:”請輸入指令:“,如果輸入

    getall

    ,查詢并顯示所有人員的資訊
  • 第一次時查詢

    mysql

    并将結果緩存在

    redis

    ,設定60秒的過期時間
  • 以後的每次查詢,如果

    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的所有值。