1. 程式人生 > >The Little Redis Book中文版 第三章

The Little Redis Book中文版 第三章

在上一章裡,我們談論了Redis的5種資料結構,對於一些可能的用途也給出了用例。現在是時候來看看一些更高階,但依然很常見的主題和設計模式。

大O表示法(Big O Notation)

在本書中,我們之前就已經看到過大O表示法,包括O(1)和O(N)的表示。大O表示法的慣常用途是,描述一些用於處理一定數量元素的行為的綜合表現。在Redis裡,對於一個要處理一定數量元素的命令,大O表示法讓我們能瞭解該命令的大概執行速度。

在Redis的文件裡,每一個命令的時間複雜度都用大O表示法進行了描述,還能知道各命令的具體效能會受什麼因素影響。讓我們來看看一些用例。

常數時間複雜度O(1)被認為是最快速的,無論我們是在處理5個元素還是5百萬個元素,最終都能得到相同的效能。對於sismember

命令,其作用是告訴我們一個值是否屬於一個集合,時間複雜度為O(1)。sismember命令很強大,很大部分的原因是其高效的效能特徵。許多Redis命令都具有O(1)的時間複雜度。

對數時間複雜度O(log(N))被認為是第二快速的,其通過使需掃描的區間不斷皺縮來快速完成處理。使用這種“分而治之”的方式,大量的元素能在幾個迭代過程裡被快速分解完整。zadd命令的時間複雜度就是O(log(N)),其中N是在分類集合中的元素數量。

再下來就是線性時間複雜度O(N),在一個表格的非索引列裡進行查詢就需要O(N)次操作。ltrim命令具有O(N)的時間複雜度,但是,在ltrim命令裡,N不是列表所擁有的元素數量,而是被刪除的元素數量。從一個具有百萬元素的列表裡用ltrim

命令刪除1個元素,要比從一個具有一千個元素的列表裡用ltrim命令刪除10個元素來的快速(實際上,兩者很可能會是一樣快,因為兩個時間都非常的小)。

根據給定的最小和最大的值的標記,zremrangebyscore命令會在一個分類集合裡進行刪除元素操作,其時間複雜度是O(log(N)+M)。這看起來似乎有點兒雜亂,通過閱讀文件可以知道,這裡的N指的是在分類集合裡的總元素數量,而M則是被刪除的元素數量。可以看出,對於效能而言,被刪除的元素數量很可能會比分類集合裡的總元素數量更為重要。

(譯註:zremrangebyscore命令的具體構成是ZREMRANGEBYSCORE Key max mix。)

對於sort命令,其時間複雜度為O(N+M*log(M)),我們將會在下一章談論更多的相關細節。從sort命令的效能特徵來看,可以說這是Redis裡最複雜的一個命令。

還存在其他的時間複雜度描述,包括O(N^2)和O(C^N)。隨著N的增大,其效能將急速下降。在Redis裡,沒有任何一個命令具有這些型別的時間複雜度。

值得指出的一點是,在Redis裡,當我們發現一些操作具有O(N)的時間複雜度時,我們可能可以找到更為好的方法去處理。

(譯註:對於Big O Notation,相信大家都非常的熟悉,雖然原文僅僅是對該表示法進行簡單的介紹,但限於個人的演算法知識和文筆水平實在有限,此小節的翻譯讓我頭痛頗久,最終成果也確實難以讓人滿意,望見諒。)

仿多關鍵字查詢(Pseudo Multi Key Queries)

時常,你會想通過不同的關鍵字去查詢相同的值。例如,你會想通過電子郵件(當用戶開始登入時)去獲取使用者的具體資訊,或者通過使用者id(在使用者登入後)去獲取。有一種很不實效的解決方法,其將使用者物件分別放置到兩個字串值裡去:

set users:[email protected] "{id: 9001, email: '[email protected]', ...}"
set users:9001 "{id: 9001, email: '[email protected]', ...}"

這種方法很糟糕,如此不但會產生兩倍數量的記憶體,而且這將會成為資料管理的惡夢。

如果Redis允許你將一個關鍵字連結到另一個的話,可能情況會好很多,可惜Redis並沒有提供這樣的功能(而且很可能永遠都不會提供)。Redis發展到現在,其開發的首要目的是要保持程式碼和API的整潔簡單,關鍵字連結功能的內部實現並不符合這個前提(對於關鍵字,我們還有很多相關方法沒有談論到)。其實,Redis已經提供瞭解決的方法:雜湊。

使用雜湊資料結構,我們可以擺脫重複的纏繞:

set users:9001 "{id: 9001, email: [email protected], ...}"
hset users:lookup:email leto@dune.gov 9001

我們所做的是,使用域來作為一個二級索引,然後去引用單個使用者物件。要通過id來獲取使用者資訊,我們可以使用一個普通的get命令:

get users:9001

而如果想通過電子郵箱來獲取使用者資訊,我們可以使用hget命令再配合使用get命令(Ruby程式碼):

id = redis.hget('users:lookup:email', '[email protected]')
user = redis.get("users:#{id}")

你很可能將會經常使用這類用法。在我看來,這就是雜湊真正耀眼的地方。在你瞭解這類用法之前,這可能不是一個明顯的用例。

引用和索引(References and Indexes)

我們已經看過幾個關於值引用的用例,包括介紹列表資料結構時的用例,以及在上面使用雜湊資料結構來使查詢更靈活一些。進行歸納後會發現,對於那些值與值間的索引和引用,我們都必須手動的去管理。誠實來講,這確實會讓人有點沮喪,尤其是當你想到那些引用相關的操作,如管理、更新和刪除等,都必須手動的進行時。在Redis裡,這個問題還沒有很好的解決方法。

我們已經看到,集合資料結構很常被用來實現這類索引:

sadd friends:leto ghanima paul chani jessica

這個集合裡的每一個成員都是一個Redis字串資料結構的引用,而每一個引用的值則包含著使用者物件的具體資訊。那麼如果chani改變了她的名字,或者刪除了她的帳號,應該如何處理?從整個朋友圈的關係結構來看可能會更好理解,我們知道,chani也有她的朋友:

sadd friends_of:chani leto paul

如果你有什麼待處理情況像上面那樣,那在維護成本之外,還會有對於額外索引值的處理和儲存空間的成本。這可能會令你感到有點退縮。在下一小節裡,我們將會談論減少使用額外資料互動的效能成本的一些方法(在第1章我們粗略地討論了下)。

如果你確實在擔憂著這些情況,其實,關係型資料庫也有同樣的開銷。索引需要一定的儲存空間,必須通過掃描或查詢,然後才能找到相應的記錄。其開銷也是存在的,當然他們對此做了很多的優化工作,使之變得更為有效。

再次說明,需要在Redis裡手動地管理引用確實是頗為棘手。但是,對於你關心的那些問題,包括效能或儲存空間等,應該在經過測試後,才會有真正的理解。我想你會發現這不會是一個大問題。

資料互動和流水線(Round Trips and Pipelining)

我們已經提到過,與伺服器頻繁互動是Redis的一種常見模式。這類情況可能很常出現,為了使我們能獲益更多,值得仔細去看看我們能利用哪些特性。

許多命令能接受一個或更多的引數,也有一種關聯命令(sister-command)可以接受多個引數。例如早前我們看到過mget命令,接受多個關鍵字,然後返回值:

keys = redis.lrange('newusers', 0, 10)
redis.mget(*keys.map {|u| "users:#{u}"})

或者是sadd命令,能新增一個或多個成員到集合裡:

sadd friends:vladimir piter
sadd friends:paul jessica leto "leto II" chani

Redis還支援流水線功能。通常情況下,當一個客戶端傳送請求到Redis後,在傳送下一個請求之前必須等待Redis的答覆。使用流水線功能,你可以傳送多個請求,而不需要等待Redis響應。這不但減少了網路開銷,還能獲得性能上的顯著提高。

值得一提的是,Redis會使用儲存器去排列命令,因此批量執行命令是一個好主意。至於具體要多大的批量,將取決於你要使用什麼命令(更明確來說,該引數有多大)。另一方面來看,如果你要執行的命令需要差不多50個字元的關鍵字,你大概可以對此進行數千或數萬的批量操作。

對於不同的Redis載體,在流水線裡執行命令的方式會有所差異。在Ruby裡,你傳遞一個程式碼塊到pipelined方法:

redis.pipelined do
  9001.times do
    redis.incr('powerlevel')
  end
end

正如你可能猜想到的,流水線功能可以實際地加速一連串命令的處理。

事務(Transactions)

每一個Redis命令都具有原子性,包括那些一次處理多項事情的命令。此外,對於使用多個命令,Redis支援事務功能。

你可能不知道,但Redis實際上是單執行緒執行的,這就是為什麼每一個Redis命令都能夠保證具有原子性。當一個命令在執行時,沒有其他命令會執行(我們會在往後的章節裡簡略談論一下Scaling)。在你考慮到一些命令去做多項事情時,這會特別的有用。例如:

incr命令實際上就是一個get命令然後緊隨一個set命令。

getset命令設定一個新的值然後返回原始值。

setnx命令首先測試關鍵字是否存在,只有當關鍵字不存在時才設定值

雖然這些都很有用,但在實際開發時,往往會需要執行具有原子性的一組命令。若要這樣做,首先要執行multi命令,緊隨其後的是所有你想要執行的命令(作為事務的一部分),最後執行exec命令去實際執行命令,或者使用discard命令放棄執行命令。Redis的事務功能保證了什麼?

  • 事務中的命令將會按順序地被執行

  • 事務中的命令將會如單個原子操作般被執行(沒有其它的客戶端命令會在中途被執行)

  • 事務中的命令要麼全部被執行,要麼不會執行

你可以(也應該)在命令列介面對事務功能進行一下測試。還有一點要注意到,沒有什麼理由不能結合流水線功能和事務功能。

multi
hincrby groups:1percent balance -9000000000
hincrby groups:99percent balance 9000000000
exec

最後,Redis能讓你指定一個關鍵字(或多個關鍵字),當關鍵字有改變時,可以檢視或者有條件地應用一個事務。這是用於當你需要獲取值,且待執行的命令基於那些值時,所有都在一個事務裡。對於上面展示的程式碼,我們不能去實現自己的incr命令,因為一旦exec命令被呼叫,他們會全部被執行在一塊。我們不能這麼做:

redis.multi()
current = redis.get('powerlevel')
redis.set('powerlevel', current + 1)
redis.exec()

(譯註:雖然Redis是單執行緒執行的,但是我們可以同時執行多個Redis客戶端程序,常見的併發問題還是會出現。像上面的程式碼,在get執行之後,set執行之前,powerlevel的值可能會被另一個Redis客戶端給改變,從而造成錯誤。)

這些不是Redis的事務功能的工作。但是,如果我們增加一個watchpowerlevel,我們可以這樣做:

redis.watch('powerlevel')
current = redis.get('powerlevel')
redis.multi()
redis.set('powerlevel', current + 1)
redis.exec()

在我們呼叫watch後,如果另一個客戶端改變了powerlevel的值,我們的事務將會執行失敗。如果沒有客戶端改變powerlevel的值,那麼事務會繼續工作。我們可以在一個迴圈裡執行這些程式碼,直到其能正常工作。

關鍵字反模式(Keys Anti-Pattern)

在下一章中,我們將會談論那些沒有確切關聯到資料結構的命令,其中的一些是管理或除錯工具。然而有一個命令我想特別地在這裡進行談論:keys命令。這個命令需要一個模式,然後查詢所有匹配的關鍵字。這個命令看起來很適合一些任務,但這不應該用在實際的產品程式碼裡。為什麼?因為這個命令通過線性掃描所有的關鍵字來進行匹配。或者,簡單地說,這個命令太慢了。

人們會如此去使用這個命令?一般會用來構建一個本地的Bug追蹤服務。每一個帳號都有一個id,你可能會通過一個看起來像bug:account_id:bug_id的關鍵字,把每一個Bug儲存到一個字串資料結構值中去。如果你在任何時候需要查詢一個帳號的Bug(顯示它們,或者當用戶刪除了帳號時刪除掉這些Bugs),你可能會嘗試去使用keys命令:

keys bug:1233:*

更好的解決方法應該使用一個雜湊資料結構,就像我們可以使用雜湊資料結構來提供一種方法去展示二級索引,因此我們可以使用域來組織資料:

hset bugs:1233 1 "{id:1, account: 1233, subject: '...'}"
hset bugs:1233 2 "{id:2, account: 1233, subject: '...'}"

從一個帳號裡獲取所有的Bug標識,可以簡單地呼叫hkeys bugs:1233。去刪除一個指定的Bug,可以呼叫hdel bugs:1233 2。如果要刪除了一個帳號,可以通過del bugs:1233把關鍵字刪除掉。

小結

結合這一章以及前一章,希望能讓你得到一些洞察力,瞭解如何使用Redis去支援(Power)實際專案。還有其他的模式可以讓你去構建各種型別的東西,但真正的關鍵是要理解基本的資料結構。你將能領悟到,這些資料結構是如何能夠實現你最初視角之外的東西。

相關推薦

The Little Redis Book中文版

在上一章裡,我們談論了Redis的5種資料結構,對於一些可能的用途也給出了用例。現在是時候來看看一些更高階,但依然很常見的主題和設計模式。 大O表示法(Big O Notation) 在本書中,我們之前就已經看到過大O表示法,包括O(1)和O(N)的表示。大O表示法的慣常用

The Little Redis Book中文版

在最後一章裡,我們將集中談論Redis執行中的一些管理方面內容。這是一個不完整的Redis管理指南,我們將會回答一些基本的問題,初接觸Redis的新使用者可能會很感興趣。 配置(Configuration) 當你第一次執行Redis的伺服器,它會向你顯示一個警告,指redi

redis教程(The little redis book中文版)

許可證 《The Little Redis Book》是經由Attribution-NonCommercial 3.0 Unported license許可的,你不需要為此書付錢。 你可以自由地對此書進行復制,分發,修改或者展示等操作。當然,你必須知道且認可這本書的作者是

The Little Redis Book中文版 入門

每個人的學習方式都不一樣,有的人喜歡親自實踐學習,有的喜歡觀看教學視訊,還有的喜歡通過閱讀來學習。對於Redis,沒有什麼比親自實踐學習來得效果更好的了。Redis的安裝非常簡單。而且通過隨之安裝的一個簡單的命令解析程式,就能處理我們想做的一切事情。讓我們先花幾分鐘的時間把

Learning Spark中文版----RDD編程(2)

翻譯 瓶頸 並集 ria multi guide 第六章 rabl 函數式 Common Transformations and Actions ??本章中,我們瀏覽了Spark中大多數常見的transformation(轉換)和action(動作)。在包含特定數據類型的R

effective java中文版 對於所有物件都通用的方法

道一聲坑爹。。。。上週末剛把這章整理了。。。忘了儲存了。。。迫於強迫症。。。。不得不再寫一遍。。但是也一帶而過。。。。只是為了哥的強迫症 第8條 覆蓋equals時請遵守通用約定 1,自反性 2,對稱性 3,傳遞性 第9條 覆蓋equals時總要覆蓋hashCode(這條重點記住)

閱讀Book:MultiObjective using Evolutionary Algorithms(9)--Bensons' Method & Value Function Method

(1)Bensons' Method This procedure issimilar to the weighted metric approach,except that the reference solution is taken as a feasibel non-Pare

Redis 1資料結構—字串

Key的定義注意點: 不要太長,或者太短。 統一的命名規範。 儲存String,以二進位制儲存 。最大儲存長度512M。 儲存String常用命令 賦值 取值 刪除 數值增減 擴充套件命令 連線到redis控制檯: //切換到bin目錄 cd /usr/local/bin /

Redis開發與運維之總結-小功能大用處

總結 1.慢查詢中兩個重要引數slowlog-log-slower-than(預設闕值) 和 slowlog-max-len(列表最大長度) 闕值的設立 慢查詢記錄存放在哪裡 超過這個闕值的命令將會記錄在一個地方,Redis採用了列表來儲存慢查詢日誌,slowlog-ma

redis學習 5種資料型別----String型別

跟著Redis入門指南學習 第三章 5種資料型別 3.1 熱身 先了解幾個比較基礎的命令作為熱身,開啟redis-cli,跟著樣例輸入命令來體驗下: 1.獲取符合規範的健名列表 keys patternpattern支援glob風格萬用字元格式,具體規則如下: 符號 含

Redis命令

字串Redis字串可以儲存三種類型的值:位元組串,整數,浮點數。使用者可以通過給定一個任意的數值,對儲存著整數或者浮點數的字串執行自增或者自減操作,有需要的時候,Redis還會將整數轉換成浮點數。Redis中的自增和自減命令INCR:  INCR key-name     將

讀構建之法 :軟件工程師的成長

知識點 可維護 vid -s 評估 不同 fun 可靠 科研 本章理論和知識點:評價軟件工程師水平的主要方法 軟件工程把相關的技術和過程統一到一個體系中,叫“軟件開發流程”,軟件開發流程的目的是為了提高軟件開發、運營、維護的效率,以及提升用戶滿意度、軟件的可靠性和可維護性。

Js高設筆記

efi alert html 產生 數據 span mil blog com 第三章 數據類型 P25 1, var message; //age變量尚未聲明 alert(message); //"undefined" alert(age); //產生錯誤

總結

tsp 領域 style 成長 集體 lib con 需要 能夠 本章主要的理論和知識點是評價軟件工程師水平的主要方法、技能的反面以及TSP對個人的要求。 首先,不同的數據能夠從不同方面一個展示軟件工程師的技術和能力,例如,通過完成時間平均值的比較,兩位工程師或許能決出完成

軟件工程師的成長

出發 int 開始 體系 會議 tor 可重復 設計 標準 軟件工程包括了開發、運營、維護軟件的過程中的很多技術、做法、習慣和思想。軟件工程把這些相關的技術和過程統一到一個體系中,叫“軟件開發流程”,軟件開發流程的目的是為了提高軟件開發、運營、維護和效率,以及提升用戶滿意度

構建之法讀書心得

如何 讀書心得 初級 知識 技能 任務 項目 標準 技術   在構建之法第三章中,我們主要學習了個人能力的衡量與發展。   初級軟件工程師有以下幾個成長階段:1、積累軟件開發相關的知識,提升技術技能。                    2、積累問題領域的知識和經驗。  

-- DNS

linux一、DNS基礎配置 *)客戶端配置 vim /etc/resolv.conf 編輯dns配置文件 *)服務端配置 yum install bind -y 安裝dns服務 systemctl stop firewalld 關閉防火墻 systemctl tart name

Netty In Action中文版 - :Transports(傳輸)

duplicate pipeline 客戶 下列表 bytes 線程安全 get 工具 jsb 本章內容 Transports(傳輸)NIO(non-blocking IO,New IO), OIO(Old IO,blocking IO), Local(本地),

『Python』Numpy學習指南__常用函數

第一個 indices first 填充 del lib ida like otl 感覺心情漸漸變好了,加油!np.eye(2)np.savetxt(‘eye.txt‘,i2)c,v = np.loadtxt(‘data.csv‘, delimiter=‘,‘, useco

補基礎:自學:計算機科學導論 數據存儲

計算機科學 通用 3.1 數據類型 數字 文字 音頻 圖像 視頻 計算機內部的數據 所有計算機外部的數據類型的數據都采用統一的數據表示法轉換後存入計算機中,當數據從計算機輸出時再還原回來。這種通用的格式稱為位模式。 1. 位:是存儲在計算機中的最小