本章主要內容
字符串命令、列表命令和集合命令散列命令和有序集合命令發布命令與訂閱命令其他命令
本章將介紹一些沒有在第1章和第2章出現過的Redis命令,學習這些命令有助于讀者在已有示例的基礎上構建更為復雜的程序,并學會如何更好地去解決自己遇到的問題。本章將使用客戶端與Redis服務器進行簡單的互動,并以此來介紹命令的用法,如果讀者想要看一些更為具體的代碼示例,那么可以閱讀第2章。
根據結構或者概念的不同,本章將多個命令分別放到了多個不同的節里面進行介紹,并且這里展示的命令都是各種應用程序最經常會用到的。和第1章介紹各個結構時的做法類似,本章也是通過與客戶端進行互動的方式來介紹各個命令,在有需要的時候,文中還會說明本書在哪些章節用到了正在介紹的命令。
在每個不同的數據類型的章節里,展示的都是該數據類型所獨有的、最具代表性的命令。首先讓我們來看看,除了GET和SET之外,Redis的字符串還支持哪些命令。
查閱本章未介紹命令的文檔 患于未然,本章只會介紹最常用的Redis命令或者本書后續章節會用到的命令,如果讀者需要一份完整的命令文檔作為參考,那么可以訪問http://redis.io/commands.
Redis 2.4和Redis 2.6 正如附錄A所說,在本書編寫之際,Windows平臺上面只有Redis 2.4可用,而本書卻會用到只有Redis 2.6或以上版本才支持的特性。Redis 2.4和Redis 2.6之間的主要區別包括(但不限于)Lua腳本(將在第11章介紹)、毫秒精度的過期操作(相關的PTTL命令、PEXPIRE命令和PEXPIREAT命令將在本章介紹)、一些二進制位操作(BITOP命令和BITCOUNT命令),另外還有一些在Redis 2.6以前只能接受單個參數的命令,比如RPUSH、LPUSH、SADD、SREM、HDEL、ZADD和ZREM,從Redis 2.6開始都可以接受多個參數了。
3.1 字符串
本書在第1章和第2章曾經說過,Redis的字符串就是一個由字節組成的序列,它們和很多編程語言里面的字符串沒有什么顯著的不同,跟C或者C++風格的字符數組也相去不遠。在Redis里面,字符串可以存儲以下3種類型的值。字節串(byte string)。
整數。
浮點數。
用戶可以通過給定一個任意的數值,對存儲著整數或者浮點數的字符串執行自增(increment)或者自減(decrement)操作,在有需要的時候,Redis還會將整數轉換成浮點數。整數的取值范圍和系統的長整數(long integer)的取值范圍相同(在32位系統上,整數就是32位有符號整數,在64位系統上,整數就是64位有符號整數),而浮點數的取值范圍和精度則與IEEE 754標準的雙精度浮點數(double)相同。Redis明確地區分字節串、整數和浮點數的做法是一種優勢,比起只能夠存儲字節串的做法,Redis的做法在數據表現方面具有更大的靈活性。
本節將對Redis里面最簡單的結構——字符串進行討論,介紹基本的數值自增和自減操作,以及二進制位(bit)和子串(substring)處理命令,讀者可能會驚訝地發現,Redis里面最簡單的結構居然也有如此強大的作用。
表3-1展示了對Redis字符串執行自增和自減操作的命令。
當用戶將一個值存儲到Redis字符串里面的時候,如果這個值可以被解釋(interpret)為十進制整數或者浮點數,那么Redis會察覺到這一點,并允許用戶對這個字符串執行各種INCR*和DECR*操作。如果用戶對一個不存在的鍵或者一個保存了空串的鍵執行自增或者自減操作,那么Redis在執行操作時會將這個鍵的值當作是0來處理。如果用戶嘗試對一個值無法被解釋為整數或者浮點數的字符串鍵執行自增或者自減操作,那么Redis將向用戶返回一個錯誤。代碼清單3-1展示了對字符串執行自增操作和自減操作的一些例子。
代碼清單3-1 這個交互示例展示了Redis的INCR操作和DECR操作
在讀完本書其他章節之后,讀者可能會發現本書只調用了 incr(),這是因為 Python的Redis庫在內部使用INCRBY命令來實現incr()方法,并且這個方法的第二個參數是可選的:如果用戶沒有為這個可選參數設置值,那么這個參數就會使用默認值1。在編寫本書的時候,Python的Redis客戶端庫支持Redis 2.6的所有命令,這個庫通過incrbyfloat()方法來實現INCRBYFLOAT命令,并且incrbyfloat()方法也有類似于incr()方法的可選參數特性。
除了自增操作和自減操作之外,Redis還擁有對字節串的其中一部分內容進行讀取或者寫入的操作(這些操作也可以用于整數或者浮點數,但這種用法并不常見),本書在第9章將展示如何使用這些操作來高效地將結構化數據打包(pack)存儲到字符串鍵里面。表3-2展示了用來處理字符串子串和二進制位的命令。
表3-2 供Redis處理子串和二進制位的命令
GETRANGE和SUBSTR Redis現在的GETRANGE命令是由以前的SUBSTR命令改名而來的,因此,Python客戶端至今仍然可以使用substr()方法來獲取子串,但如果讀者使用的是2.6或以上版本的Redis,那么最好還是使用getrange()方法來獲取子串。
在使用SETRANGE或者SETBIT命令對字符串進行寫入的時候,如果字符串當前的長度不能滿足寫入的要求,那么Redis會自動地使用空字節(null)來將字符串擴展至所需的長度,然后才執行寫入或者更新操作。在使用GETRANGE讀取字符串的時候,超出字符串末尾的數據會被視為是空串,而在使用GETBIT讀取二進制位串的時候,超出字符串末尾的二進制位會被視為是0。代碼清單3-2展示了一些字符串處理命令的使用示例。
代碼清單3-2 這個交互示例展示了Redis的子串操作和二進制位操作
很多鍵值數據庫只能將數據存儲為普通的字符串,并且不提供任何字符串處理操作,有一些鍵值數據庫允許用戶將字節追加到字符串的前面或者后面,但是卻沒辦法像Redis一樣對字符串的子串進行讀寫。從很多方面來講,即使Redis只支持字符串結構,并且只支持本節列出的字符串處理命令,Redis也比很多別的數據庫要強大得多;通過使用子串操作和二進制位操作,配合WATCH命令、MULTI命令和EXEC命令(本書的3.7.2節將對這3個命令進行初步的介紹,并在第4章對它們進行更深入的講解),用戶甚至可以自己動手去構建任何他們想要的數據結構。第9章將介紹如何使用字符串去存儲一種簡單的映射,這種映射可以在某些情況下節省大量內存。
只要花些心思,我們甚至可以將字符串當作列表來使用,但這種做法能夠執行的列表操作并不多,更好的辦法是直接使用下一節介紹的列表結構,Redis為這種結構提供了豐富的列表操作命令。
3.2 列表
在第1章曾經介紹過,Redis的列表允許用戶從序列的兩端推入或者彈出元素,獲取列表元素,以及執行各種常見的列表操作。除此之外,列表還可以用來存儲任務信息、最近瀏覽過的文章或者常用聯系人信息。
本節將對列表這個由多個字符串值組成的有序序列結構進行介紹,并展示一些最常用的列表處理命令,閱讀本節可以讓讀者學會如何使用這些命令來處理列表。表3-3展示了其中一部分最常用的列表命令。
表3-3 一些常用的列表命令
因為本書在第1章已經對列表的幾個推入和彈出操作進行了簡單的介紹,所以讀者應該不會對上面列出的推入和彈出操作感到陌生,代碼清單3-3展示了這些操作的用法。
代碼清單3-3 這個交互示例展示了Redis列表的推入操作和彈出操作
這個示例里面第一次用到了LTRIM命令,組合使用LTRIM和LRANGE可以構建出一個在功能上類似于LPOP或者RPOP的操作,它能夠一次返回并彈出多個元素。本章稍后將會介紹原子地①執行多個命令的方法,而更高級的Redis事務特性則會在第4章介紹。
有幾個列表命令可以將元素從一個列表移動到另一個列表,或者阻塞(block)執行命令的客戶端直到有其他客戶端給列表添加元素為止,這些命令在第1章都沒有介紹過,表3-4列出了這些阻塞彈出命令和元素移動命令。
表3-4 阻塞式的列表彈出命令以及在列表之間移動元素的命令
在第6章討論隊列時,這組命令將會非常有用。代碼清單3-4展示了幾個使用BRPOPLPUSH移動列表元素的例子以及使用BLPOP從列表里面彈出多個元素的例子。
代碼清單3-4 這個交互示例展示了Redis列表的阻塞彈出命令以及元素移動命令
對于阻塞彈出命令和彈出并推入命令,最常見的用例就是消息傳遞(messaging)和任務隊列(task queue),本書將在第6章對這兩個主題進行介紹。練習:通過列表來降低內存占用
在2.1節和2.5節中,我們使用了有序集合來記錄用戶最近瀏覽過的商品,并把用戶瀏覽這些商品時的時間戳設置為分值,從而使得程序可以在清理舊會話的過程中或是執行完購買操作之后,進行相應的數據分析。但由于保存時間戳需要占用相應的空間,所以如果分析操作并不需要用到時間戳的話,那么就沒有必要使用有序集合來保存用戶最近瀏覽過的商品了。為此,請在保證語義不變的情況下,將update_token()函數里面使用的有序集合替換成列表。提示:如果讀者在解答這個問題時遇上困難的話,可以到6.1.1節中找找靈感。
列表的一個主要優點在于它可以包含多個字符串值,這使得用戶可以將數據集中在同一個地方。Redis的集合也提供了與列表類似的特性,但集合只能保存各不相同的元素。接下來的一節中就讓我們來看看不能保存相同元素的集合都能做些什么。
3.3 集合
Redis的集合以無序的方式來存儲多個各不相同的元素,用戶可以快速地對集合執行添加元素操作、移除元素操作以及檢查一個元素是否存在于集合里。第1章曾經對集合進行過簡單的介紹,并在構建文章投票網站時,使用集合記錄文章已投票用戶名單以及群組屬下的所有文章。
本節將對最常用的集合命令進行介紹,包括插入命令、移除命令、將元素從一個集合移動到另一個集合的命令,以及對多個集合執行交集運算、并集運算和差集運算的命令。閱讀本節也有助于讀者更好地理解本書在第7章介紹的搜索示例。
表3-5展示了其中一部分最常用的集合命令。
表3-5 一些常用的集合命令
表3-5里面的不少命令都已經在第1章介紹過了,代碼清單3-5展示了這些命令的使用示例。
代碼清單3-5 這個交互示例展示了Redis中的一些常用的集合命令
通過使用上面展示的命令,我們可以將各不相同的多個元素添加到集合里面,比如第1章就使用集合記錄了文章已投票用戶名單,以及文章屬于哪個群組。但集合真正厲害的地方在于組合和關聯多個集合,表3-6展示了相關的命令。
表3-6 用于組合和處理多個集合的Redis命令
這些命令分別是并集運算、交集運算和差集運算這3個基本集合操作的“返回結果”版本和“存儲結果”版本,代碼清單3-6展示了這些命令的使用示例。
代碼清單3-6 這個交互示例展示了Redis的差集運算、交集運算以及并集運算
和Python的集合相比,Redis的集合除了可以被多個客戶端遠程地進行訪問之外,其他的語義和功能基本都是相同的。
接下來的一節將對Redis的散列處理命令進行介紹,這些命令允許用戶將多個相關的鍵值對存儲在一起,以便執行獲取操作和更新操作。
3.4 散列
第1章提到過,Redis的散列可以讓用戶將多個鍵值對存儲到一個Redis鍵里面。從功能上來說,Redis為散列值提供了一些與字符串值相同的特性,使得散列非常適用于將一些相關的數據存儲在一起。我們可以把這種數據聚集看作是關系數據庫中的行,或者文檔數據庫中的文檔。
本節將對最常用的散列命令進行介紹:其中包括添加和刪除鍵值對的命令、獲取所有鍵值對的命令,以及對鍵值對的值進行自增或者自減操作的命令。閱讀這一節可以讓讀者學習到如何將數據存儲到散列里面,以及這樣做的好處是什么。表3-7展示了一部分常用的散列命令。
表3-7 用于添加和刪除鍵值對的散列操作
在表3-7列出的命令當中,HDEL命令已經在第1章中介紹過了,而HLEN命令以及用于一次讀取或者設置多個鍵的HMGET和HMSET則是新出現的命令。像HMGET和HMSET這種批量處理多個鍵的命令既可以給用戶帶來方便,又可以通過減少命令的調用次數以及客戶端與Redis之間的通信往返次數來提升Redis的性能。代碼清單3-7展示了這些命令的使用方法。
代碼清單3-7 這個交互示例展示了Redis中的一些常用的散列命令
第1章介紹的HGET命令和HSET命令分別是HMGET命令和HMSET命令的單參數版本,這些命令的唯一區別在于單參數版本每次執行只能處理一個鍵值對,而多參數版本每次執行可以處理多個鍵值對。
表3-8列出了散列的其他幾個批量操作命令,以及一些和字符串操作類似的散列命令。
表3-8 展示Redis散列的更高級特性
盡管有HGETALL存在,但HKEYS和HVALUES也是非常有用的:如果散列包含的值非常大,那么用戶可以先使用HKEYS取出散列包含的所有鍵,然后再使用HGET一個接一個地取出鍵的值,從而避免因為一次獲取多個大體積的值而導致服務器阻塞。
HINCRBY和HINCRBYFLOAT可能會讓讀者回想起用于處理字符串的INCRBY和INCRBYFLOAT,這兩對命令擁有相同的語義,它們的不同在于HINCRBY和HINCRBYFLOAT處理的是散列,而不是字符串。代碼清單3-8展示了這些命令的使用方法。
代碼清單3-8 這個交互示例展示了Redis散列的一些更高級的特性
正如前面所說,在對散列進行處理的時候,如果鍵值對的值的體積非常龐大,那么用戶可以先使用HKEYS獲取散列的所有鍵,然后通過只獲取必要的值來減少需要傳輸的數據量。除此之外,用戶還可以像使用SISMEMBER檢查一個元素是否存在于集合里面一樣,使用HEXISTS檢查一個鍵是否存在于散列里面。另外第1章也用到了本節剛剛回顧過的HINCRBY來記錄文章被投票的次數。
在接下來的一節中,我們要了解的是之后的章節里面會經常用到的有序集合結構。
3.5 有序集合
和散列存儲著鍵與值之間的映射類似,有序集合也存儲著成員與分值之間的映射,并且提供了分值②處理命令,以及根據分值大小有序地獲取(fetch)或掃描(scan)成員和分值的命令。本書曾在第1章使用有序集合實現過基于發表時間排序的文章列表和基于投票數量排序的文章列表,還在第2章使用有序集合存儲過cookie的過期時間。
本節將對操作有序集合的命令進行介紹,其中包括向有序集合添加新元素的命令、更新已有元素的命令,以及對有序集合進行交集運算和并集運算的命令。閱讀本節可以加深讀者對有序集合的認識,從而幫助讀者更好地理解本書在第1章、第5章、第6章和第7章展示的有序集合示例。
表3-9展示了一部分常用的有序集合命令。
表3-9 一些常用的有序集合命令
在上面列出的命令當中,有一部分命令已經在第1章和第2章使用過了,所以讀者應該不會對它們感到陌生,代碼清單3-9回顧了這些命令的用法。
代碼清單3-9 這個交互示例展示了Redis中的一些常用的有序集合命令
因為ZADD、ZREM、ZINCRBY、ZSCORE和ZRANGE都已經在第1章和第2章介紹過了,所以讀者應該不會對它們感到陌生。ZCOUNT命令和其他命令不太相同,它主要用于計算分值在給定范圍內的成員數量。
表3-10展示了另外一些非常有用的有序集合命令。
表3-10 有序集合的范圍型數據獲取命令和范圍型數據刪除命令,以及并集命令和交集命令(略)
在表3-10展示的命令里面,有幾個是之前沒介紹過的新命令。除了使用逆序來處理有序集合之外,ZREV*命令的工作方式和相對應的非逆序命令的工作方式完全一樣(逆序就是指元素按照分值從大到小地排列)。代碼清單3-10展示了ZINTERSTORE和ZUNIONSTORE的用法。
代碼清單3-10 這個交互示例展示了ZINTERSTORE命令和ZUNIONSTORE命令的用法
有序集合的并集運算和交集運算在剛開始接觸時可能會比較難懂,所以本節將使用圖片來展示交集運算和并集運算的執行過程。圖3-1展示了對兩個輸入有序集合執行交集運算并得到輸出有序集合的過程,這次交集運算使用的是默認的聚合函數sum,所以輸出有序集合成員的分值都是通過加法計算得出的。
圖3-1 執行conn.zinterstore('zset-i', ['zset-1', 'zset-2'])將使得同時存在于zset-1和zset-2里面的元素被添加到zset-i里面
并集運算和交集運算不同,只要某個成員存在于至少一個輸入有序集合里面,那么這個成員就會被包含在輸出有序集合里面。圖3-2展示了使用聚合函數min執行并集運算的過程,min函數在多個輸入有序集合都包含同一個成員的情況下,會將最小的那個分值設置為這個成員在輸出有序集合的分值。
圖3-2 執行conn.zunionstore('zset-u', ['zset-1', 'zset-2'], aggregate='min')會將存在于zset-1或者zset-2里面的元素通過min函數組合到zset-u里面
在第1章中,我們就基于“集合可以作為ZUNIONSTORE操作和ZINTERSTORE操作的輸入”這個事實,在沒有使用額外的有序集合來存儲群組文章的評分和發布時間的情況下,實現了群組文章的添加和刪除操作。圖3-3展示了如何使用ZUNIONSTORE命令來將兩個有序集合和一個集合組合成一個有序集合。
圖3-3 執行conn.zunionstore('zset-u2', ['zset-1', 'zset-2', 'set-1'])將使得所有存在于zset-1、zset-2或者set-1里面的元素都被添加到zset-u2里面
第7章將使用ZINTERSTORE和ZUNIONSTORE來構建幾個不同類型的搜索系統,并說明如何通過可選的WEIGHTS參數來以幾種不同的方式組合有序集合的分值,從而使得集合和有序集合可以用于解決更多問題。
讀者在開發應用的過程中,也許曾經聽說過發布與訂閱(publish/subscribe)模式,又稱pub/sub模式,Redis也實現了這種模式,接下來的一節將對其進行介紹。
本文摘自《Redis實戰》,【美】Josiah L. Carlson(約西亞 L.卡爾森)著,黃健宏譯。近期本書在京東做半價促銷,如果喜歡可以入手
《Redis實戰》([美]約西亞 L.卡爾森(Josiah,L.,Carlson))【摘要 書評 試讀】- 京東圖書?item.jd.com
本書對Redis本身以及它的鍵值對模型進行了介紹,讀者將接觸到包括緩存、分布式廣告定向等實際使用案例,學到如何從小型的作業任務開始,擴展Redis以適應大規模的數據集,以及如何與其他傳統的關系數據庫或是其他NoSQL存儲系統進行集成。有經驗的開發者應該會對集群和服務器腳本編程等較為深入的內容感興趣。
本書主要內容全面介紹Redis
預處理實時數據
管理內存數據集
發布/訂閱及配置
持久化到磁盤
本書面向熟悉數據庫概念的開發者。閱讀本書既不要求讀者預先了解NoSQL數據庫概念,也不要求讀者有任何Redis使用經驗。本書也適合具備編程能力的系統管理員閱讀。