分享Redis使用全攻略:如何跳出SQL這個坑

來源:互聯網
上載者:User
關鍵字 我們 通過 可以 刪除 name

隨著資料體積的激增,MySQL+memcache已經滿足不了大型互聯網類應用的需求,許多機構也紛紛選擇Redis作為其架構上的補充,然而Redis的使用門檻並不低,比如不支援SQL等,這裡為大家分享Redis的使用全攻略。

Redis,備受關注的NoSQL資料庫之一,已為眾多知名互聯網公司使用,比如新浪微博、Pinterest及Viacom。 然而,天生不支援SQL卻讓他看起來很不容易接近,這裡我們一起看@utopiar的博文——探索Redis。

探索之一:Redis? What is it?

簡而言之,Redis是一種強大的key-value資料庫,之所以強大有兩點:回應速度快(所以資料記憶體存儲,只在必要時寫入磁片),特性豐富(支援多種資料類型,以及各類型上的複雜操作)。

事實上,Redis的一個重要特性就是它並非通常意義上的資料庫,雖然稱之為資料庫是因為它可以為你存儲和維護資料,但它並不像關係資料庫那樣提供任何的SQL方言。 不過不用擔心,Redis並不是吞噬資料的黑洞,它只是不支援SQL及相關功能,但卻提供了穩健的協定用於與之交互。

在Redis中,沒有資料表的概念,也無須關心select、join、view等操作或功能,同時也不提供類似于int或Varchar的資料欄位。 你面對的將是相對原始的資料集合及資料類型。

探索之二:Available datatypes

下面我們深入看下這個奇怪的資料庫是如何工作的。 如上所見,Redis是基於key-value范式存儲資料,所以先來重點看下"key"的概念。

key本質上就是簡單的字串,諸如"username"、"password"等。 在定義key時,除了不能使用空格,你可以隨意的使用普通的字元、數位等,像".",":","_"等在定義key時都能正常使用,所以像"user_name", "user:123:age", "user:123:username "都是不錯的key的定義方式。

不像RDBMS中的欄位名稱,這裡的key是Redis中的重要組成部分,所以我們必須在處理key時多加小心。 在下面的講述中,Redis並沒有table的概念,所以像"SELECT username from users WHERE user_id=123;" 這種簡單任務都只能換種方式實現,為了達到這種目的,在Redis上,一種方式是通過key "user:123:username"來獲取結果value。 如你所見,key的定義中攜帶了神秘資訊(像user ids)。 在Redis中,key的重要性可見一斑。 (其他key-value資料庫中key的地位也是如此。 )

現在你應該對key有了清楚的瞭解,下面帶你進入可用的資料類型的神奇世界。

Strings

String是Redis中最基本的資料類型,它就是普通的二進位安全的字串,支援最大資料長度為1Gb。

可以通過SET命令給一個key設置String類型的資料,並可通過GET命令根據key取得結果。 如果你想存儲數位資訊,像計數器,你也可以把它存儲到String資料中,並可使用INCR和DECR對之做自增和自減操作。

Lists

List是string資料的集合,其中各資料項目按插入順序排列。 你可以把list當作一個鎖鏈(chain),所以可以在鎖鏈最左邊(鏈頭)或最右邊(鏈尾)添加一個新的鏈結(link);當然也可以加在鎖鏈中間,但卻要打斷某個鏈結。

可能通過LPUSH和RPUSH命令給list添加資料(L:left, R:right),通過LPOP或RPOP命令彈出元素(同時刪除該元素),也可以通過LRANGE獲取指定範圍的元素(僅返回資料,不會刪除任何元素)。 另外也可通過LSET在指定位置添加元素,但通常這種操作比簡單的LPUSH或RPUSH要慢很多。

Hashes

Hashes以簡潔的方式存儲關係更為緊密的資料。 Hashes為每個存儲的key實現一個內置的key-value對來存儲資料,例如對於"user"這個key,它的值可以是多個欄位以及與每個字元相應的值對組成資料集。 如果你熟悉像ruby或javascript等程式設計語言,這裡的hashes與那些語言中的hash概念大同小異。

Sets

Sets和它在數學上的同名概念"集合"意義相同,是包含不重複元素的集合。 在Redis中,這些物件變成了Redis裡的String類型。 正如你想,sets與lists不同的地方在於:sets中的元素是無序的,並且不能重複,你不能在sets中放進兩個相同的資料。

可以通過SADD往set中添加資料,SREM刪除資料,或者通過SPOP返回並刪除此資料。 此外還可以通過SUNION, SINTER, SDIFF命令分別實現集合上的"並集", "交集", "差集"操作。

Ordered sets

Ordered sets與sets類似,不同地方在於Ordered set中的每個元素都有一個權重,用於與其他元素比較並排序。

當然ordered set與普通sets具有類似的操作,ZADD和ZREM分別是添加和刪除元素。 Ordered set也有自己獨有的操作:ZINCR和ZSCORE,前則用於為元素的權重+1,後則則返回元素的權重值。

探索之三:Where are my tables?

使用Redis與我們之前使用的SQL資料表完全不同,沒有語言支援你在伺服器上查詢資料,這裡僅有一些命令幫你運算元據庫中的keys值。 Redis中的命令是資料類型敏感型的,也就是說你不能在list上執行set命令,否則你將得到一個執行錯誤的提示。 可以通過redis-cli或其他你使用的程式設計語言中的介面給Redis server發送命令。 在下面的示例中,我們只強調命令本身,而不關注你通過哪種方式提交給Redis server。

想像一下,一個簡單的SQL資料庫表,像一些應用中會用到的保存使用者資料的表:

存儲資料

假如我們想把上面的資料存儲到Redis中,你會如何在Redis中設計資料庫方案呢? 也許以應用的視覺來看會更直觀一些。 使用SQL,我們在SELECT中通過指定使用者id來獲得一個使用者資訊,換句話說就是需要有用於區分不同資料實體的方式,所以我們可以通過一個唯一的標識來標識和獲取使用者資訊。 所以如果在redis的key中加入使用者的id資訊,那麼我們的查詢需求就解決了,在redis中,資料被存儲成如下形式:

那麼,給出任一個使用者id,我們就可以通過key user:id:username,user:id:password,user:id:name,user:id:surname的形式讀出使用者資訊。

使用者登錄

上面的存儲形式也能用於使用者登錄,但需要一種方式能根據username來查詢使用者的id。 也就是說我們還需要在username和id之間建立聯繫。 這可以通過添加另外一個redis key"user:username:id"來實現。

現在如果Mario Rossi想要登錄進來,我們可以通過key"user:user2:id"先查出username,進而獲得使用者的所有資訊。

主鍵

在Redis中如何保證id值的唯一性呢。 在SQL中,可以通過"id int primary key auto_increment"定義自增主鍵來實現,現在我們也需要一種類似的方式為每個使用者生成一個不同的id。 根據前面可用的資料類型中提到的數位資料,Redis中的方案是這樣的:創建一個key"user:next_id",並把它作為計數器,每當要添加新使用者時,就對key"user:next_id"執行INCR命令。

SELECT * FROM users;

下一個面臨的問題是查詢使用者清單。 也許你認為我們上面的資料存儲已經足以查詢出使用者清單:可以先獲得"user:next_id"的當前值counter,然後通過一步或多步遍歷0到counter獲得使用者資料。 但如果某個使用者從系統中刪除(下面會講到刪除操作),而我們會遍歷0到counter中的所有id,這時就會有些id查詢不到任何資料。

儘管這通常不是問題,但我們不想在不存在的使用者資料上浪費時間,所以需要創建另外一個key"user:list",其value為list或set類型,用於存儲每一個新增的使用者id,並在必要的時候從"user:list" 中刪除該id。 我更傾向于使用list,因為它可能通過LRANGE命令實現分頁功能。

刪除使用者

還有一個要面臨的問題是"資料完整性",看看我們在刪除使用者時會發生什麼吧。 我們需要刪除每一個對此使用者的引用,也就是說,需要刪除下面所有的key"user:id:*","user:username:id",以及"user:list"中的使用者id。

探索之四:A Simple use case

為了學習致用,我們嘗試設計一個虛擬圖書館,並能根據主題對圖書分組。 下面的例子比上面的使用者表會稍微複雜些,但你將學會如何在Redis中處理關聯關係。

在應用中,我們需要收集圖書,並存儲他們的title,author(s), topic(s), pages, price, ISBN和description。 顯然有些圖書的作者不止一個,並且它也許會涵蓋不同的主題(例如一本書可以是程式設計主題,也可以是描述的ruby程式設計)。 另外一個作者可能寫了很多本書,而一個主題必然會包含很多本書。 可以看出,這裡出現了作者和圖書、主題和圖書的多對多關係。

SQL場景

首先,我們嘗試使用SQL資料表為此種場景建資料模型,以便於我們更直觀的在Redis領域中類比:

Redis場景

前面已經介紹了如何在Redis中存儲資料,所以這裡理解Books,Authors和Topics這三張表應該不成問題。 但當面對Books-Authors和Book-Topics這種表之間的多對多關聯時,問題變得複雜起來。 下面以Topic為例來看如何解決Book與Topic之間的關聯,一旦對這個關係清楚了,Book與Author之間的關係也就迎刃而解了。

對於每本book,我們需要知道它屬於哪些topics;同樣對於每個topic,也要處理它包含的每本book。 換句話說,對每本book,需要一個存儲它所關聯的topic的id清單,對於每個topic,同樣需要一個存儲它關聯的book的id清單。 這正是set大展身手的地方。 我們將創建兩個sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id清單,後者存儲topic的books'id清單。 以前面SQL場景中的資料為例,圖書"Programming Erlang"(books表中的id為2),將有一個key為"book:2:topics",value為set類型且資料為(1,3)的資料資訊;而主題" programming"則會有一個key為"topic:1:books",值為(1,2)的資料集。

經過分析,就得出了Redis場景下的資料模型:

可以看出,在SQL中的多對多關聯,在Redis中可以通過兩個set來實現。 你會發現這種實現相當有用,它給我們提供了一種可以自由獲得其他資訊的能力:可以通過對所有感興趣的"topic:id:books"集合中交集操作,從而獲得隸屬于多個主題的圖書。 例如對集合"topic:1:books"(programming主題)和"topic:2:books"(ruby主題)做交集,會得到只有一個元素(1)的集合,從而獲得id=1的圖書:programming Ruby。

對於這種實現,你必須特別關注對資料的刪除操作。 因為topics裡有對books的引用,同樣books裡有對topics的引用,那刪除如何操作? 以刪除books中的資料為例,首先想到的是要刪除每個key為"book:id:*"的資料,但執行此操作前需要先遍歷topics中的所有key為"topic:id:books"的集合,並從中刪除待刪除圖書的id, 當然也要從books中key為"book:list"的清單中刪除此id。 如果想刪除一個topic,操作也很類似:從topics中刪除所有key為"topic:id:*"資訊之前,需要先遍歷books中的key為"books:id:topics"的topic id集, 並從中刪除待刪除topic的id,同時從"topic:list"清單中也刪除此id。 同樣的操作對於authors一樣適用。

探索之五:Back home

對於Redis的探索到一段落,現在回頭看看我們的旅行包裡收穫了哪些精彩。

我們學習了Redis中的資料類型及操作命令,還有一些其他有趣的東西。 是不是還有幾段記憶深刻的故事呢:

通過對String資料執行INCR命令解決唯一自增主鍵問題

通過含義豐富的key:"user:username:id"處理使用者登錄場景

通過set實現資料間的多對多關聯

到此為止,Redis之旅已經結束,希望未給你帶來不快。 最後送上一副良濟:having fun coding free software!

原文連結:HTTP://www.cnblogs.com/enjiex/p/3618546.html

相關文章

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.