使用 ReJSON 在 Redis 中儲存 Go 結構體

來源:互聯網
上載者:User
![image](https://raw.githubusercontent.com/studygolang/gctt-images/master/rejson/1_w3hPEpsPFtHs36dJMUdl7w.jpeg)> 映像授權 https://Redislabs.com/blog/Redis-go-designed-improve-performance/大部分人可能對 Redis 都很熟悉了。對於外行人來說,Redis 是最廣為人知並廣泛應用的資料庫/緩衝產品,起碼也是之一。官方文檔是這麼描述 Redis 的:> Redis 是一個開源(BSD 許可)的,記憶體中的資料結構儲存系統,它可以用作資料庫、緩衝和訊息中介軟體。 它支援的資料結構有字串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)與範圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial)索引半徑查詢。 Redis 內建了複製(replication),LUA指令碼(Lua scripting), LRU 驅動事件(LRU eviction),事務(transactions) 和不同層級的磁碟持久化(persistence), 並通過 Redis 哨兵(Sentinel)和自動分區(Cluster)提供高可用性(high availability)。將 Redis 與其它(傳統)資料庫區分開來的是,Redis 是一個鍵-值 儲存(並且是在記憶體中)。這意味著在這個資料庫中所有的值都與一個 key 相關聯(想想字典的情況)。不過我跑題了,這篇文章可不是講 Redis 的,讓我們言歸正傳。## 使用 Go 語言與 Redis 進行互動當 Go 開發人員使用 Redis 時,有時會需要將我們的對象緩衝到 Redis 中。我們看看如何通過 Redis 中的 HMSET 來實現這點。一個簡單的 go 結構體可能會像這樣,```gotype SimpleObject struct { FieldA string FieldB int}simpleObject := SimpleObject{"John Doe", 24}```很明顯,為了將對象存到 Redis 中,我們必須將它轉化成一個索引值對,我們將結構體的欄位名作為 key,欄位的值作為這個 key 對應的值。對所有的欄位取一個雜湊值也是非常好的索引值,將對象所有的欄位與這個對象自身綁定起來。在 Redis-cli 中我們可以這麼做:```127.0.0.1:6379> HMSET simple_object fieldA "John Doe" fieldB 24OK```用 HGETALL 命令擷取的結果是,```127.0.0.1:6379> HGETALL simple_objectfieldAJohn DoefieldB24```好吧,現在我們知道對象是怎樣序列化後存入資料庫中的,讓我們繼續用程式的方式完成這個工作!雖然 Redis 的 Go 用戶端很多,但我使用 redigo,它在 github 上有一個很不錯的社區,而且也是最常用的 Redis 的 Go 用戶端之一,有超過 4K 個星星。### Redigo 助手函數 — AddFlat 和 ScanStructRedigo內建了一系列很棒的助手函數,其中我們將用到 AddFlat ,在我們將結構體存入 Redis 之前,用它將結構體扁平化。```go// 獲得連結化物件conn, err := Redis.Dial("tcp", "localhost:6379")if err != nil { return}// 使用 Do 方法調用命令_, err = conn.Do("HMSET", Redis.Args{"simple_object"}.AddFlat(simpleObject)...)if err != nil { return}```現在,如果你希望讀回這個對象,我們可以使用 HGETALL 命令,```govalue, err := Redis.Values(conn.Do("HGETALL", key))if err != nil { return}object := SimpleStruct{}err = Redis.ScanStruct(value, &object)if err != nil { return}```很簡單,對吧?讓我們在看看更深入的一些問題...### Go 結構體中嵌套的對象現在,我們來看一個更複雜的結構體,```gotype Student struct { Info *StudentDetails `json:"info,omitempty"` Rank int `json:"rank,omitempty"`}type StudentDetails struct { FirstName string LastName string Major string}studentJD := Student{ Info: &StudentDetails{ FirstName: "John", LastName: "Doe", Major: "CSE", }, Rank: 1,}```現在我們有一個嵌套的結構體,`StudentDetails` 是 `Student` 對象的一個成員。讓我們再用 `HMSET` 試試看,```go// 用 Do 方法調用命令_, err = conn.Do("HMSET", Redis.Args{"JohnDoe"}.AddFlat(studentJD)...)if err != nil { return}```如果我們再看看 Redis 中存進了什麼,我們可以看到的是這樣的,```127.0.0.1:6379> HGETALL JohnDoeInfo&{John Doe CSE}Rank1```這就是問題點了。當我們想從 Redis 中讀資料並轉化為對象時,**ScanStruct** 會報錯,```redigo.ScanStruct: cannot assign field Info: cannot convert from Redis bulk string to *main.StudentDetails```**EPIC FAIL !**這是因為 Redis 將所有的東西都存為**字串**[大對象使用 bulk 字串]。### 現在該怎麼辦?快速的搜尋可以給你一些解決方案,其中之一是建議使用一個封裝處理器(Marshaler)(`JSON` marshal),其他的的方案建議用 `MessagePack`。以下我將展示採用 `JSON` 的解決方案```gob, err := json.Marshal(&studentJD)if err != nil { return}_, err = conn.Do("SET", "JohnDoe", string(b))if err != nil { return}```需要取回值時,只需要使用 `GET` 命令將 `JSON` 字串讀取回來就可以了。```goobjStr, err = Redis.String(conn.Do("GET", "JohnDoe"))if err != nil { return}b := []byte(objStr)student := &Student{}err = json.Unmarshal(b, student)if err != nil { return}```如果我們是希望**將對象完整的緩衝下來**,這個方案工作的很好。但是,如果我們希望在對象上進行增加、修改或者讀取一個欄位,比如,**John Doe** 將他的專業從 **CSE** 改成了 **EE**,該怎麼辦?唯一的辦法是,先讀出 JSON 字串,轉化為對象,修改對象,然後重新將對象存回 Redis。這看起來可是不少的工作!> 如果你發現,使用 Hash,通過 `HGET`/`HSET` 命令來實現這一點很簡單。如果只是這樣,那就這麼做吧。(原文:If you are wondering, doing this with the Hash is trivial by using the HGET/HSET commands. If only, that worked — bummer!)### ReJSON優秀的 [RedisLabs](https://redislabs.com/) 團隊給我們帶來了一個解決方案,讓我們可以對應像操作傳統 JSON 對象那樣操作 Redis 中的對象。讓我們馬上來看看。我從 [rejson](http://rejson.io/) 的文檔中挑選了這個例子,```127.0.0.1:6379> JSON.SET amoreinterestingexample . '[ true, { "answer": 42 }, null ]'OK127.0.0.1:6379> JSON.GET amoreinterestingexample"[true,{\"answer\":42},null]"127.0.0.1:6379> JSON.GET amoreinterestingexample [1].answer“42”127.0.0.1:6379> JSON.DEL amoreinterestingexample [-1]1127.0.0.1:6379> JSON.GET amoreinterestingexample"[true,{\"answer\":42}]"```用程式來實現這個,我們絕對可以使用 `Redigo` 的原生形態[這意味者我們可以使用 `conn.Do(...)` 命令調用任何 Redis 支援的命令]。然而,我花了一些時間將所有的 `ReJSON` 的命令轉換成了 Go 包,叫做 [go-rejson](https://github.com/nitishm/go-rejson)。回到我們之前的 `Student` 對象,我們可以用以下步驟使用程式將它存入 Redis 中,```goimport "github.com/nitishm/go-rejson"_, err = rejson.JSONSet(conn, "JohnDoeJSON", ".", studentJD, false, false)if err != nil { return}```在 `redis-cli` 中我們可以查到,```127.0.0.1:6379> JSON.GET JohnDoeJSON{"info":{"FirstName":"John","LastName":"Doe","Major":"CSE"},"rank":1}```如果我只想從 Redis 條目(entry)讀取 `info` 欄位,我會執行 `JSON.GET`,如下所示,```127.0.0.1:6379> JSON.GET JohnDoeJSON .info{"FirstName":"John","LastName":”Doe","Major":"CSE"}```類似地,對於 `rank` 欄位,我通過 `.rank` 引用,```127.0.0.1:6379> JSON.GET JohnDoeJSON .rank1```使用程式來擷取 student 對象,我們可以通過 `JSONGet()` 方法調用 `JSON.GET` 命令,```gov, err := rejson.JSONGet(conn, "JohnDoeJSON", "")if err != nil { return}outStudent := &Student{}err = json.Unmarshal(outJSON.([]byte), outStudent)if err != nil { return}```為了給 `rank` 欄位賦值,我們可以在 `.rank` 欄位上使用 `JSONSet()` 方法來調用 `JSON.SET` 命令,```go_, err = rejson.JSONSet(conn, "JohnDoeJSON", ".info.Major", "EE", false, false)if err != nil { return}```在 `redis-cli` 中我們查看這個條目,可以看到,```127.0.0.1:6379> JSON.GET JohnDoeJSON{"info":{"FirstName":"John","LastName":"Doe","Major":"EE"},"rank":1}```## 運行這個例子### 用 Docker 啟動帶 rejson 模組的 Redis```docker run -p 6379:6379 --name Redis-rejson Redislabs/rejson:latest```### 從 github 上複製這個例子```# git clone https://github.com/nitishm/rejson-struct.git# cd rejson-struct# go run main.go```想要瞭解更多 **Go-REJSON** 包,請訪問 https://github.com/nitishm/go-rejson.想要瞭解跟多 **ReJSON**,訪問它們的官方文檔, http://rejson.io/.

via: https://medium.com/@nitishmalhotra/storing-go-structs-in-Redis-using-rejson-dab7f8fc0053

作者:Nitish Malhotra 譯者:MoodWu 校對:polaris1119

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

249 次點擊  ∙  1 贊  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.