在Golang中使用Redis
周五上班的主要任務是在公司老平臺上用redis處理一個隊列問題,順便復習了一下redis操作的基礎知識,回來後就想著在自己的博客demo裏,用redis來優化一些使用場景,學習一下golang開發下redis的使用。
Redis簡單介紹
簡介
關於Redis的討論,其實在現在的後臺開發中已經是個老生常談的問題,基本上也是後端開發面試的基本考察點。其中 Redis的背景介紹和細節說明在這裏就不贅述。不管怎麽介紹,核心在於Redis是一個基於內存的key-value的多數據結構存儲,並可以提供持久化服務。基於內存的特性決定了Redis天然適合高並發的數據讀寫緩存優化,同時也帶來了內存開銷過大的問題。所以在一些特定情景下,Redis是一把無往不利的大殺器,值得深入學習。
學習Redis的一個難點或者說入門點,我個人感覺在於對象存儲理念的轉變。剛接觸Redis
時,我剛從大學畢業,腦子裏基本都是關系型數據存儲的理念,使用時總想著靠數據內的關系來建立數據之間的聯系,用起來很不順手。後來慢慢入門了才感受到了一些操作的好處。舉個栗子,比如查詢用戶在某個文章下的評論,用sql
的思路就是搜索評論表裏面用戶ID和文章ID匹配的數據,有時還需要聯合查詢出其他信息,但是如果是Redis
操作,以‘前綴:文章ID:用戶ID‘為key,比如‘comment:666:888‘就可以快速取出用戶評論,十分方便。Redis
的強大遠不僅如此,可以在實踐中慢慢體會。
主要數據結構
Redis主要有五種基本數據結構,滿足了絕大多數緩存結構的需要,如果你在使用一種結構存儲時感覺別扭時,很有可能是選錯了存儲結構,可以考慮一下其他結構的正確實現。
- String ,可以是字符串、整數和浮點數。如果是序列化數據,並涉及到修改操作的話,不推薦用
string
,可以考慮用Hash
- Hash, key-value 對象,可以存放對象數據,比如用戶信息之類。
- List,有序數據集合,元素可以重復,用
LPUSH
、LPOP
、RPUSH
、RPOP
等指令組合可以實現棧和隊列操作。 - Set,無序集合,元素唯一。
- Sorted Set,Sort的有序版,可以設定
Score
值來決定元素排序,適合用戶排名這樣的業務場景。
常見使用場景
- 高並發下數據緩存。 比如在某個場景下,大量日誌同時寫入數據庫會給服務器帶來巨大壓力,這時可以先將數據寫入
redis
中,再由redis
- 熱點信息快速顯示。假設現在有一個新聞首頁,需要快速顯示各欄目前20條熱點新聞,如果直接查詢數據庫,在大量用戶同時訪問下,會消耗極大數量的數據庫請求。這時就可以用
redis
來優化,在新聞錄入的時候將標題、時間和來源寫入redis
中,客戶端訪問時,可以從內存中一次性取出當天熱單新聞列表,極大地提高請求速度和節約了服務器開銷。 - 保存會話信息。可以將登錄後用戶信息緩存入
redis
並同時設置key
過期時間,這樣後臺api
過濾請求時,就可以從內存中讀取用戶信息,而且redis
的過期機制,天然支持用戶身份有效期校驗,用起來十分方便。 - 統計計數。比如系統中常見一個功能是限制同一用戶固定時間段內的登錄次數或者所有請求次數,這時就可以以用戶id為key,次數值為value,將計數信息緩存起來,並且有
INCRBY
命令原生支持。 - 其他。Redis的應用場景十分廣發,隊列、發布訂閱、統計分析等等,可以看看其他文章的介紹說明。
Golang連接Redis
使用Golang
開發的一大直觀感受就是,基本上你日常遇到的開發問題,都有官方或者第三方包幫你輔助實現,同時這些包都是開源的,只要你感興趣,都可以深入到包的內部實現去學習理解包的實現思路和方法。當然這也有利有弊,第三包的不穩定和質量參差不齊也增加了一些開發成本,目前還是感受利大於弊。研究好的包源碼實現,也是目前我的一個學習方向。
garyburd/redigo 包簡介
garyburd/redigo
包是網上很多博文都在推薦使用的一個高Star的Redis
連接包,但是當我自己去Github
的項目地址 garyburd/redigo 上查看API
時,發現這個項目目前是歸檔狀態,項目已經遷移到了gomodule/redigo,同時包的獲取也理所當然地改成了go get github.com/gomodule/redigo/redis
,這已經不是我第一次感受了第三方包的不穩定,之前用dep
進行包管理時,就遇到過dep
拉取的包版本和本地包版本API
沖突的問題,這個有時間單獨再說。總之,暫時不管這兩個包的詳細區別,以下就以新包為準,介紹下redigo
包使用。
建立連接池
Redigo Pool 結構維護一個 Redis 連接池。應用程序調用 Get 方法從池中獲取連接,並使用連接的 Close 方法將連接的資源返回到池中。一般我們在系統初始化時聲明一個全局連接池,然後在需要操作redis
時獲得連接,執行指令。
pool := &redis.Pool{
MaxIdle: 3, /*最大的空閑連接數*/
MaxActive: 8, /*最大的激活連接數*/
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", ‘鏈接地址,例如127.0.0.1:6379‘, redis.DialPassword(‘密碼‘))
if err != nil {
return nil, err
}
return c, nil
}
}
c:=pool.Get()
defer c.Close()
執行指令
查看源碼,發現Conn
接口有一個執行 Redis 命令的通用方法:
```
//gomodule/redigo/redis/redis.go
// Conn represents a connection to a Redis server.
type Conn interface {
// Close closes the connection.
Close() error
// Err returns a non-nil value when the connection is not usable.
Err() error
// Do sends a command to the server and returns the received reply.
Do(commandName string, args ...interface{}) (reply interface{}, err error)
// Send writes the command to the client‘s output buffer.
Send(commandName string, args ...interface{}) error
// Flush flushes the output buffer to the Redis server.
Flush() error
// Receive receives a single reply from the Redis server
Receive() (reply interface{}, err error)
}
```
http://redis.io/commands 中的 Redis 命令參考列出了可用的命令。do
的參數和redis-cli
命令參數格式一致,比如SET key value EX 360
對應函數調用為Do("SET", "key", "value","EX",360)
,常用的命令示例有:
c:=pool.Get()
defer c.Close()
//存值,
_, err := c.Do("SET", "key", "value")
//設置過期時間
_, err := c.Do("SET", "key", "value","EX",360)
//存int
_, err := c.Do("SET", "key", 2)
//取值
v,err:=redis.String(c.Do("GET","key"))
bytes, err := redis.Bytes(c.Do("GET", "key"))
總結
golang
中連接使用redis
相對比較簡單,所以暫時也沒什麽其他好說的,如果後面自己使用過程中發現有遺漏再進行補充,關鍵還是在於熟悉redis-cli
原生的指令操作。
在Golang中使用Redis