Golang Redis操作


1. Redis簡介

Redis是一個開源的、使用C語言編寫的、支持網絡交互的、可基於內存也可持久化的Key-Value數據庫。

 

1.1 特點

  • 支持更多數據類型 
    和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set 有序集合)和hash(哈希類型)。[1]

  • 支持復雜操作 
    這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,Redis支持各種不同方式的排序。[2]

  • 支持主從同步。 
    與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是Redis會周期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。數據可以從主服務器向任意數量的從服務器上同步,從服務器可以是關聯其他從服務器的主服務器。這使得Redis可執行單層樹復制。從盤可以有意無意的對數據進行寫操作。由於完全實現了發布/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發布記錄。同步對讀取操作的可擴展性和數據冗余很有幫助。

Redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部 分場合可以對關系數據庫起到很好的補充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。Redis的官網地址,非常好記,是redis.io。目前,Vmware在資助着Redis項目的開發和維護。

 

2. redigo

redigo是GO語言的一個redis客戶端實現。項目位於https://github.com/garyburd/redigo

 

2.1 安裝redigo

redigo沒有其他依賴項,可以直接通過go get進行安裝 
go get github.com/garyburd/redigo/redis

 

2.2 連接

Conn接口是與Redis協作的主要接口,可以使用Dial,DialWithTimeout或者NewConn函數來創建連接,當任務完成時,應用程序必須調用Close函數來完成

 
  1. package main
  2. import (
  3. "fmt"
  4. "github.com/garyburd/redigo/redis"
  5. )
  6. func main() {
  7. c, err := redis.Dial("tcp", "localhost:6379")
  8. if err != nil {
  9. fmt.Println("Connect to redis error", err)
  10. return
  11. }
  12. defer c.Close()
  13. ...
 

2.3 命令執行

Conn接口中有一個通用方法來執行

n, err := c.Do("APPEND", "key", "value")

Do函數會必要時將參數轉化為二進制字符串

Go Type Conversion
[]byte Sent as is
string Sent as is
int, int64 strconv.FormatInt(v)
float64 strconv.FormatFloat(v, 'g', -1, 64)
bool true -> "1", false -> "0"
nil ""
all other types fmt.Print(v)

Redis 命令響應會用以下Go類型表示:

Redis type Go type
error redis.Error
integer int64
simple string string
bulk string []byte or nil if value not present.
array []interface{} or nil if value not present.

可以使用GO的類型斷言或者reply輔助函數將返回的interface{}轉換為對應類型

 

常用命令[3]

讀寫

GET key
SET key value [EX seconds] [PX milliseconds] [NX|XX]`

GET取對應鍵值,如果鍵值不存在則nil會返回,

 
  1. // 寫入值永不過期
  2. _, err = c.Do("SET", "username", "nick")
  3. if err != nil {
  4. fmt.Println("redis set failed:", err)
  5. }
  6. username, err := redis.String(c.Do("GET", "username"))
  7. if err != nil {
  8. fmt.Println("redis get failed:", err)
  9. } else {
  10. fmt.Printf("Got username %v \n", username)
  11. }

輸出: 
Got username nick 
SET命令還支持附加參數

  • EX seconds -- 指定過期時間,單位為秒.
  • PX milliseconds -- 指定過期時間,單位為毫秒.
  • NX -- 僅當鍵值不存在時設置鍵值.
  • XX -- 僅當鍵值存在時設置鍵值. 
    設置鍵值過期時間為10s
 
  1. // 寫入值10S后過期
  2. _, err = c.Do("SET", "password", "123456", "EX", "10")
  3. if err != nil {
  4. fmt.Println("redis set failed:", err)
  5. }
  6. time.Sleep(11 * time.Second)
  7. password, err := redis.String(c.Do("GET", "password"))
  8. if err != nil {
  9. fmt.Println("redis get failed:", err)
  10. } else {
  11. fmt.Printf("Got password %v \n", password)
  12. }

輸出 
redis get failed: redigo: nil returned 
批量寫入讀取

MGET key [key ...]
MSET key value [key value ...]

批量寫入讀取對象(Hashtable)

HMSET key field value [field value ...]
HMGET key field [field ...]

檢測值是否存在

EXISTS key

刪除

DEL key [key ...]

設置過期時間

EXPIRE key seconds
 

2.4 管道化(Pipelining)

請求/響應服務可以實現持續處理新請求,即使客戶端沒有准備好讀取舊響應。這樣客戶端可以發送多個命令到服務器而無需等待響應,最后在一次讀取多個響應。這就是管道化(pipelining),這個技術在多年就被廣泛使用了。距離,很多POP3協議實現已經支持此特性,顯著加速了從服務器下載新郵件的過程。 
Redis很早就支持管道化,所以無論你使用任何版本,你都可以使用管道化技術

連接支持使用Send(),Flush(),Receive()方法支持管道化操作

Send(commandName string, args ...interface{}) error
Flush() error
Receive() (reply interface{}, err error)

Send向連接的輸出緩沖中寫入命令。Flush將連接的輸出緩沖清空並寫入服務器端。Recevie按照FIFO順序依次讀取服務器的響應。下例展示了一個簡單的管道:

 
  1. c.Send("SET", "foo", "bar")
  2. c.Send("GET", "foo")
  3. c.Flush()
  4. c.Receive() // reply from SET
  5. v, err = c.Receive() // reply from GET

Do方法組合了Send,Flush和 Receive方法。Do方法先寫入命令,然后清空輸出buffer,最后接收全部掛起響應包括Do方發出的命令的結果。如果任何響應中包含一個錯誤,Do返回錯誤。如果沒有錯誤,Do方法返回最后一個響應。如果Do函數的命令參數為"",那么Do方法會清空out緩沖並接受掛起響應而不發送任何命令。

 

2.5 並發

連接並不支持並發調用寫入方法(Send,Flush)或者讀取方法(Receive)。但是連接支持並發的讀寫。 
因為Do方法組合了Send,Flush和Receive,Do不可以與其他方法並發執行。

為了能夠完全並發訪問Redis,在gorouotine中使用線程安全的連接池來獲取和釋放連接。

 

2.6 發布和訂閱(Pub/Sub)

使用Send,Flush和Receive方法來是實現Pub/Sb訂閱者。

 
  1. c.Send("SUBSCRIBE", "example")
  2. c.Flush()
  3. for {
  4. reply, err := c.Receive()
  5. if err != nil {
  6. return err
  7. }
  8. // process pushed message
  9. }

PubSubConn類型封裝了Conn提供了便捷的方法來實現訂閱者模式。Subscribe,PSubscribe,Unsubscribe和PUnsubscribe方法發送和清空訂閱管理命令。Receive將一個推送消息 
轉化為一個在type switch更為方便使用的類型。

 
  1. psc := redis.PubSubConn{c}
  2. psc.Subscribe("example")
  3. for {
  4. switch v := psc.Receive().(type) {
  5. case redis.Message:
  6. fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
  7. case redis.Subscription:
  8. fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
  9. case error:
  10. return v
  11. }
  12. }
 

2.7 響應輔助函數

Bool,Int,Bytes,String,Strings和Values函數將響應轉化為特定類型值。為了允許更方便的封裝連接的Do和Receive方法的調用,函數使用第二個參數類型為error類型。如果這個error為non-nil,輔助函數會返回error。如果error為nil,則function轉化響應為特定類型。

 
  1. exists, err := redis.Bool(c.Do("EXISTS", "foo"))
  2. if err != nil {
  3. // handle error return from c.Do or type conversion error.
  4. }
  5. The Scan function converts elements of a array reply to Go types:
  6. var value1 int
  7. var value2 string
  8. reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
  9. if err != nil {
  10. // handle error
  11. }
  12. if _, err := redis.Scan(reply, &value1, &value2); err != nil {
  13. // handle error
  14. }
 

2.8 事務支持(Transaction)

MULTI, EXEC,DISCARD和WATCH是構成Redis事務的基礎。它們允許在一個步驟中執行一組命令,並提供兩點總要保證:

  • 事務中的全部命令被序列化並且順序執行。它保證不會在Redis事務的處理過程中處理其他客戶端發起的請求。這樣保證命令如同一個單獨操作被執行。
  • 事務中的命令全部被執行或者一個都沒有被執行,這樣一個Redis事務是原子的。Exec命令觸發事務中全部命令的執行,這樣如果客戶端啟動事務后失去連接,在調用MULTI命令后的命令都不會被執行,否則如果EXEC命令被調用,全部操作都會被執行。當配置用只允許添加的文件,Redis保證使用連個獨立的write(2) 系統調用來將事務寫入磁盤。如果Redis服務崩潰或者被系統管理強行關閉可能只有一部分操作被注冊。Redis會在重啟時檢測到這種狀態,並且從中退出附帶一個錯誤。使用redis-check-aoft工具它可以修復只允許添加的文件並且移除事務碎片這樣可以讓服務重啟。

Starting with version 2.2, Redis allows for an extra guarantee to the above two, in the form of optimistic locking in a way very similar to a check-and-set (CAS) operation. This is documented later on this page. 
Usage 
自從版本2.2,Redis允許在以上兩點基礎上增加一點保證, 
A Redis transaction is entered using the MULTI command. The command always replies with OK. At this point the user can issue multiple commands. Instead of executing these commands, Redis will queue them. All the commands are executed once EXEC is called. 
Calling DISCARD instead will flush the transaction queue and will exit the transaction. 
The following example increments keys foo and bar atomically.

MULTI 
OK 
INCR foo 
QUEUED 
INCR bar 
QUEUED 
EXEC 
1) (integer) 1 
2) (integer) 1 
As it is possible to see from the session above, EXEC returns an array of replies, where every element is the reply of a single command in the transaction, in the same order the commands were issued. 
When a Redis connection is in the context of a MULTI request, all commands will reply with the string QUEUED (sent as a Status Reply from the point of view of the Redis protocol). A queued command is simply scheduled for execution when EXEC is called.

使用Send和Do方法來實現管道化事務

 
  1. c.Send("MULTI")
  2. c.Send("INCR", "foo")
  3. c.Send("INCR", "bar")
  4. r, err := c.Do("EXEC")
  5. fmt.Println(r) // prints [1, 1]
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM