1. Overview
非常非常的快,有測評說比Memcached還快(當大家都是單CPU的時候),而且是無短板的快,讀寫都一般的快,所有API都差不多快,也沒有MySQL Cluster、MongoDB那樣更新同一條記錄如Counter時慢下去的毛病。
豐富的數據結構,超越了一般的Key-Value數據庫而被認為是一個數據結構服務器。組合各種結構,限制Redis用途的是你自己的想象力,作者自己捉刀寫的用途入門
因為是個人作品,Redis目前只有2.3萬行代碼,Keep it simple的死硬做法,使得普通公司而不需淘寶那個級別的文藝公司也可以吃透它。Redis宣言就是作者的自白,我最喜歡其中的“代碼像首詩”,”設計是一場與復雜性的戰斗“,“Coding是一件艱苦的事情,唯一的辦法是享受它。如果它已不能帶來快樂就停止它。為了防止這一天的出現,我們要盡量避免把Redis往乏味的路上帶?!?/p>
讓人又愛又恨的單線程架構,使得代碼不用處理平時最讓人頭痛的并發而大幅簡化,但也帶來CPU的瓶頸,而且單線程被慢操作所阻塞時,其他請求的延時變得不確定。
那Redis不是什么?
Redis 不是Big Data,數據都在內存中,無法以T為單位。在Redis-Cluster發布并被穩定使用之前,Redis沒有真正的平滑水平擴展能力。Redis 不支持Ad-Hoc Query,提供的只是數據結構的API,沒有SQL一樣的查詢能力。最普通的key-value類型,說是String,其實是任意的byte[],比如圖片,最大512M。 所有常用命令的復雜度都是O(1),普通的Get/Set方法,可以用來做Cache,存session,為了簡化架構甚至可以替換掉Memcached。
Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用來做計數器,做自增序列。key不存在時會創建并貼心的設原值為0。IncrByFloat專門針對float,沒有對應的decrByFloat版本?用負數啊。
SetNx, 僅當key不存在時才Set??梢杂脕磉x舉Master或做分布式鎖:所有Client不斷嘗試使用SetNx master myName搶注Master,成功的那位不斷使用Expire刷新它的過期時間。如果Master倒掉了key就會失效,剩下的節點又會發生新一輪搶奪。
其他Set指令:
SetEx, Set + Expire 的簡便寫法,p字頭版本以毫秒為單位。GetSet, 設置新值,返回舊值。比如一個按小時計算的計數器,可以用GetSet獲取計數并重置為0。這種指令在服務端做起來是舉手之勞,客戶端便方便很多。MGet/MSet/MSetNx, 一次get/set多個key。2.6.12版開始,Set命令已融合了Set/SetNx/SetEx三者,SetNx與SetEx可能會被廢棄。GetBit/SetBit/BitOp,與或非/BitCount, BitMap的玩法,比如統計今天的獨立訪問用戶數時,每個注冊用戶都有一個offset,他今天進來的話就把他那個位設為1,用BitCount就可以得出今天的總人數。
Append/SetRange/GetRange/StrLen,對文本進行擴展、替換、截取和求長度,只對特定數據格式如字段定長的有用,json就沒什么用。
Key-HashMap結構,相比String類型將這整個對象持久化成JSON格式,Hash將對象的各個屬性存入Map里,可以只讀取/更新對象的某些屬性。這樣有些屬性超長就讓它一邊呆著不動,另外不同的模塊可以只更新自己關心的屬性而不會互相并發覆蓋沖突。
另一個用法是土法建索引。比如User對象,除了id有時還要按name來查詢??梢杂腥缦碌臄祿涗?
(String) user:101 -> {“id”:101,”name”:”calvin”…}(String) user:102 -> {“id”:102,”name”:”kevin”…}(Hash) user:index-> “calvin”->101, “kevin” -> 102底層實現是hash table,一般操作復雜度是O(1),要同時操作多個field時就是O(N),N是field的數量。
List是一個雙向鏈表,支持雙向的Pop/Push,江湖規矩一般從左端Push,右端Pop——LPush/RPop,而且還有Blocking的版本BLPop/BRPop,客戶端可以阻塞在那直到有消息到來,所有操作都是O(1)的好孩子,可以當Message Queue來用。當多個Client并發阻塞等待,有消息入列時誰先被阻塞誰先被服務。任務隊列系統Resque是其典型應用。
還有RPopLPush/ BRPopLPush,彈出來返回給client的同時,把自己又推入另一個list,LLen獲取列表的長度。
還有按值進行的操作:LRem(按值刪除元素)、LInsert(插在某個值的元素的前后),復雜度是O(N),N是List長度,因為List的值不唯一,所以要遍歷全部元素,而Set只要O(log(N))。
按下標進行的操作:下標從0開始,隊列從左到右算,下標為負數時則從右到左。
LSet ,按下標設置元素值。LIndex,按下標返回元素。LRange,不同于POP直接彈走元素,只是返回列表內一段下標的元素,是分頁的最愛。LTrim,限制List的大小,比如只保留最新的20條消息。復雜度也是O(N),其中LSet的N是List長度,LIndex的N是下標的值,LRange的N是start的值+列出元素的個數,因為是鏈表而不是數組,所以按下標訪問其實要遍歷鏈表,除非下標正好是隊頭和隊尾。LTrim的N是移除元素的個數。
在消息隊列中,并沒有JMS的ack機制,如果消費者把job給Pop走了又沒處理完就死機了怎么辦?
解決方法之一是加多一個sorted set,分發的時候同時發到list與sorted set,以分發時間為score,用戶把job做完了之后要用ZREM消掉sorted set里的job,并且定時從sorted set中取出超時沒有完成的任務,重新放回list。另一個做法是為每個worker多加一個的list,彈出任務時改用RPopLPush,將job同時放到worker自己的list中,完成時用LREM消掉。如果集群管理(如zookeeper)發現worker已經掛掉,就將worker的list內容重新放回主list。Set就是Set,可以將重復的元素隨便放入而Set會自動去重,底層實現也是hash table。
SAdd/SRem/SIsMember/SCard/SMove/SMembers,各種標準操作。除了SMembers都是O(1)。SInter/SInterStore/SUnion/SUnionStore/SDiff/SDiffStore,各種集合操作。交集運算可以用來顯示在線好友(在線用戶 交集 好友列表),共同關注(兩個用戶的關注列表的交集)。O(N),并集和差集的N是集合大小之和,交集的N是小的那個集合的大小*2。有序集,元素放入集合時還要提供該元素的分數。
ZRange/ZRevRange,按排名的上下限返回元素,正數與倒數。ZRangeByScore/ZRevRangeByScore,按分數的上下限返回元素,正數與倒數。ZRemRangeByRank/ZRemRangeByScore,按排名/按分數的上下限刪除元素。ZCount,統計分數上下限之間的元素個數。ZRank/ZRevRank ,顯示某個元素的正倒序的排名。ZScore/ZIncrby,顯示元素的分數/增加元素的分數。ZAdd(Add)/ZRem(Remove)/ZCard(Count),ZInsertStore(交集)/ZUnionStore(并集),Set操作,與正牌Set相比,少了IsMember和差集運算。Sorted Set的實現是hash table(element->score, 用于實現ZScore及判斷element是否在集合內),和skip list(score->element,按score排序)的混合體。 skip list有點像平衡二叉樹那樣,不同范圍的score被分成一層一層,每層是一個按score排序的鏈表。
ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是結果/操作元素的個數。可見,原本可能很大的N被很關鍵的Log了一下,1000萬大小的Set,復雜度也只是幾十不到。當然,如果一次命中很多元素M很大那誰也沒辦法了。
用Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)實現。 在事務提交前,不會執行任何指令,只會把它們存到一個隊列里,不影響其他客戶端的操作。在事務提交時,批量執行所有指令?!禦edis設計與實現》中的詳述。
注意,Redis里的事務,與我們平時的事務概念很不一樣:
它僅僅是保證事務里的操作會被連續獨占的執行。因為是單線程架構,在執行完事務內所有指令前是不可能再去同時執行其他客戶端的請求的。它沒有隔離級別的概念,因為事務提交前任何指令都不會被實際執行,也就不存在”事務內的查詢要看到事務里的更新,在事務外查詢不能看到”這個讓人萬分頭痛的問題。它不保證原子性——所有指令同時成功或同時失敗,只有決定是否開始執行全部指令的能力,沒有執行到一半進行回滾的能力。在redis里失敗分兩種,一種是明顯的指令錯誤,比如指令名拼錯,指令參數個數不對,在2.6版中全部指令都不會執行。另一種是隱含的,比如在事務里,第一句是SET foo bar, 第二句是LLEN foo,對第一句產生的String類型的key執行LLEN會失敗,但這種錯誤只有在指令運行后才能發現,這時候第一句成功,第二句失敗。還有,如果事務執行到一半redis被KILL,已經執行的指令同樣也不會被回滾。Watch指令,類似樂觀鎖,事務提交時,如果Key的值已被別的客戶端改變,比如某個list已被別的客戶端push/pop過了,整個事務隊列都不會被執行。
Redis2.6內置的Lua Script支持,可以在Redis的Server端一次過運行大量邏輯,就像存儲過程一樣,避免了海量中間數據在網路上的傳輸。
Lua自稱是在Script語言里關于快的標準,Redis選擇了它而不是流行的javaScript。因為Redis的單線程架構,整個Script默認是在一個事務里的。Script里涉及的所有Key盡量用變量,從外面傳入,使Redis一開始就知道你要改變哪些key。(but why?)Eval每次傳輸一整段Script比較費帶寬,可以先用Script Load載入script,返回哈希值。然后用EvalHash執行。因為就是SHA-1,所以任何時候執行返回的哈希值都是一樣的。內置的Lua庫里還很貼心的帶了CJSON,可以處理json字符串。一段用Redis做Timer的示例代碼,下面的script被定期調用,從以觸發時間為score的sorted set中取出已到期的Job,放到list中給Client們blocking popup。12345678910111213141516 | -- KEYS: [1]job:sleeping, [2]job:ready-- ARGS: [1]currentTime-- Comments: result is the job idlocal jobs=redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1])local count = table.maxn(jobs) if count>0 then -- Comments: remove from Sleeping Job sorted set redis.call('zremrangebyscore', KEYS[1], '-inf', ARGV[1]) -- Comments: add to the Ready Job list -- Comments: can optimize to use lpush id1,id2,... for better performance for i=1,count do redis.call('lpush', KEYS[2], jobs[i]) endend |
1 |
官方文檔 與 《Redis設計與實現》中的詳述,過期數據的清除從來不容易,為每一條key設置一個timer,到點立刻刪除的消耗太大,每秒遍歷所有數據消耗也大,Redis使用了一種相對務實的做法: 當client主動訪問key會先對key進行超時判斷,過時的key會立刻刪除。 如果clien永遠都不再get那條key呢? 它會在Master的后臺,每秒10次的執行如下操作: 隨機選取100個key校驗是否過期,如果有25個以上的key過期了,立刻額外隨機選取下100個key(不計算在10次之內)。可見,如果過期的key不多,它最多每秒回收200條左右,如果有超過25%的key過期了,它就會做得更多,但只要key不被主動get,它占用的內存什么時候最終被清理掉只有天知道。
數據準備: 預加載兩千萬條數據,占用10G內存。
測試工具:自帶的redis-benchmark,默認只是基于一個很小的數據集進行測試,調整命令行參數如下,就可以開100條線程(默認50),SET 1千萬次(key在0-1千萬間隨機),key長21字節,value長256字節的數據。
1 | redis-benchmark-tSET-c100-n10000000-r10000000-d256 |
Redis-Cluster是今年工作重點,支持automatic re-sharding, 采用和Hazelcast類似的算法,總共有N個分區(eg.N=1024),每臺Server負責若干個分區。
在客戶端先hash出key 屬于哪個分區,隨便發給一臺server,server會告訴它真正哪個Server負責這個分區,緩存下來,下次還有該分區的請求就直接發到地兒了。Re-sharding時,會將某些分區的數據移到新的Server上,完成后各Server周知分區<->Server映射的變化,因為分區數量有限,所以通訊量不大。 在遷移過程中,客戶端緩存的依然是舊的分區映射信息,原server對于已經遷移走的數據的get請求,會返回一個臨時轉向的應答,客戶端先不會更新Cache。等遷移完成了,就會像前面那樣返回一條永久轉向信息,客戶端更新Cache,以后就都去新server了。高可用性關乎系統出錯時到底會丟失多少數據,多久不能服務。要綜合考慮持久化,Master-Slave復制及Fail-Over配置,以及具體Crash情形,比如Master死了,但Slave沒死?;蛘咧皇荝edis死了,操作系統沒死等等。
綜上所述,RDB的數據不實時,同時使用兩者時服務器重啟也只會找AOF文件。那要不要只使用AOF呢?作者建議不要,因為RDB更適合用于備份數據庫(AOF在不斷變化不好備份),快速重啟,而且不會有AOF可能潛在的bug,留著作為一個萬一的手段。
因為RDB文件只用作后備用途,建議只在Slave上持久化RDB文件,而且只要15分鐘備份一次就夠了,只保留save 900 1這條規則。
如果Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒數據,啟動腳本較簡單只load自己的AOF文件就可以了。代價一是帶來了持續的IO,二是AOF rewrite的最后將rewrite過程中產生的新數據寫到新文件造成的阻塞幾乎是不可避免的。只要硬盤許可,應該盡量減少AOF rewrite的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到5G以上。默認超過原大小100%大小時重寫可以改到適當的數值,比如之前的benchmark每個小時會產生40G大小的AOF文件,如果硬盤能撐到半夜系統閑時才用cron調度bgaofrewrite就好了。
如果不Enable AOF ,僅靠Master-Slave Replication 實現高可用性也可以。能省掉一大筆IO也減少了rewrite時帶來的系統波動。代價是如果Master/Slave同時倒掉,會丟失十幾分鐘的數據,啟動腳本也要比較兩個Master/Slave中的RDB文件,載入較新的那個。新浪微博就選用了這種架構,見Tim的博客
現象描述:當AOF rewrite 15G大小的內存時,Redis整個死掉的樣子,所有指令甚至包括slave發到master的ping,redis-cli info都不能被執行。
原因分析:
官方文檔,由IO產生的Latency詳細分析, 已經預言了悲劇的發生,但一開始沒留意。Redis為求簡單,采用了單請求處理線程結構。打開AOF持久化功能后, Redis處理完每個事件后會調用write(2)將變化寫入kernel的buffer,如果此時write(2)被阻塞,Redis就不能處理下一個事件。Linux規定執行write(2)時,如果對同一個文件正在執行fdatasync(2)將kernel buffer寫入物理磁盤,或者有system wide sync在執行,write(2)會被block住,整個Redis被block住。如果系統IO繁忙,比如有別的應用在寫盤,或者Redis自己在AOF rewrite或RDB snapshot(雖然此時寫入的是另一個臨時文件,雖然各自都在連續寫,但兩個文件間的切換使得磁盤磁頭的尋道時間加長),就可能導致fdatasync(2)遲遲未能完成從而block住write(2),block住整個Redis。為了更清晰的看到fdatasync(2)的執行時長,可以使用”strace -p (pid of redis server) -T -e -f trace=fdatasync”,但會影響系統性能。Redis提供了一個自救的方式,當發現文件有在執行fdatasync(2)時,就先不調用write(2),只存在cache里,免得被block。但如果已經超過兩秒都還是這個樣子,則會硬著頭皮執行write(2),即使redis會被block住。此時那句要命的log會打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.” 之后用redis-cli INFO可以看到aof_delayed_fsync的值被加1。因此,對于fsync設為everysec時丟失數據的可能性的最嚴謹說法是:如果有fdatasync在長時間的執行,此時redis意外關閉會造成文件里不多于兩秒的數據丟失。如果fdatasync運行正常,redis意外關閉沒有影響,只有當操作系統crash時才會造成少于1秒的數據丟失。解決方法:最后發現,原來是AOF rewrite時一直埋頭的調用write(2),由系統自己去觸發sync。在RedHat Enterprise 6里,默認配置vm.dirty_background_ratio=10,也就是占用了10%的可用內存才會開始后臺flush,而我的服務器有64G內存。很明顯一次flush太多數據會造成阻塞,所以最后果斷設置了sysctl vm.dirty_bytes=33554432(32M),問題解決。
然后提了個issue,AOF rewrite時定時也執行一下fdatasync嘛, antirez三分鐘后就回復了,新版中,AOF rewrite時32M就會重寫主動調用fdatasync。
有時候明明master/slave都活得好好的,突然間就說要重新進行全同步了:
1.Slave顯示:# MASTER time out: no data nor PING received…
slave會每隔repl-ping-slave-period(默認10秒)ping一次master,如果超過repl-timeout(默認60秒)都沒有收到響應,就會認為Master掛了。如果Master明明沒掛但被阻塞住了也會報這個錯??梢赃m當調大repl-timeout。
2.Master顯示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.
當slave沒掛但被阻塞住了,比如正在loading Master發過來的RDB, Master的指令不能立刻發送給slave,就會放在output buffer中(見oll是命令數量,omem是大小),在配置文件中有如下配置:client-output-buffer-limit slave 256mb 64mb 60, 這是說負責發數據給slave的client,如果buffer超過256m或者連續60秒超過64m,就會被立刻強行關閉?。。?Traffic大的話一定要設大一點。否則就會出現一個很悲劇的循環,Master傳輸一個大的RDB給Slave,Slave努力的裝載,但還沒裝載完,Master對client的緩存滿了,再來一次。
平時可以在master執行 redis-cli client list 找那個cmd=sync,flag=S的client,注意OMem的變化。
Redis-sentinel是2.6版開始加入的另一組獨立運行的節點,提供自動Fail Over的支持。
官方文檔 與 Redis核心解讀–集群管理工具(Redis-sentinel)antirez 對 Sentinel的反駁,與下篇master地址在sentinel.conf里, sentinel會每10秒一次向master發送INFO,知道master的slave有哪些。 如果master已經變為slave,sentinel會分析INFO的應答指向新的master。以前,sentinel重啟時,如果master已經切換過了,但sentinel.conf里master的地址并沒有變,很可能有悲劇發生。另外master重啟后如果沒有切換成slave,也可能有悲劇發生。新版好像修復了一點這個問題,待研究。
另外,sentinel會在master上建一個pub/sub channel,名為”sentinel:hello”,通告各種信息,sentinel們也是通過接收pub/sub channel上的+sentinel的信息發現彼此,因為每臺sentinel每5秒會發送一次自己的host信息,宣告自己的存在。
覺得Sentinel至少有兩個可提升的地方:
一是如果master 主動shutdown,比如系統升級,有辦法主動通知sentinel提升新的master,減少服務中斷時間。二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 啟動,也不支持shutdown命令,要自己kill pid。基于Sentinel的方案,client需要執行語句SENTINEL get-master-addr-by-name mymaster 可獲得當前master的地址。 Jedis正在集成sentinel,已經支持了sentinel的一些指令,但還沒發布,但sentinel版的連接池則暫時完全沒有,在公司的項目里我參考網友的項目自己寫了一個。
淘寶的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多寫隨機讀, 一開始就同步寫入到所有節點,讀的話隨便讀一個還活著的節點就行了。但有些節點成功有些節點失敗如何處理? 節點死掉重新起來后怎么重新同步?什么時候可以重新Ready? 所以不是很敢用。
另外如Ruby寫的redis_failover,也是拋開了Redis Sentinel,基于ZooKeeper的臨時方案。
Redis作者也在博客里抱怨怎么沒有人做Dynamo-style 的client。
約30個配置項,全都有默認配置,對redif.conf默認配置的修改見附錄1。
綜述: Redis監控技巧
Info指令將返回非常豐富的信息。 著重監控檢查內存使用,是否已接近上限,used_memory是Redis申請的內存,used_memory_rss是操作系統分配給Redis的物理內存,兩者之間隔著碎片,隔著Swap。 還有重點監控 AOF與RDB文件的保存情況,以及master-slave的關系。Statistic 信息還包括key命中率,所有命令的執行次數,所有client連接數量等, CONFIG RESETSTAT 可重置為0。
Monitor指令可以顯示Server收到的所有指令,主要用于debug,影響性能,生產環境慎用。
SlowLog 檢查慢操作(見2.性能)。
官網列出了如下工具,但暫時沒發現會直接拿來用的:
Redis Live,基于Python的web應用,使用Info和Monitor獲得系統情況和指令統計分析。 因為Monitor指令影響性能,所以建議用cron定期運行,每次偷偷采樣兩分鐘的樣子。phpRedisAdmin,基于php的Web應用,目標是MysqlAdmin那樣的管理工具,可以管理每一條Key的情況,但它的界面應該只適用于Key的數量不太多的情況,Demo。Redis Faina,基于Python的命令行,Instagram出品,用戶自行獲得Monitor的輸出后發給它進行統計分析。由于Monitor輸出的格式在Redis版本間不一樣,要去github下最新版。Redis-rdb-tools 基于Python的命令行,可以分析RDB文件每條Key對應value所占的大小,還可以將RDB dump成普通文本文件然后比較兩個庫是否一致,還可以將RDB輸出成JSON格式,可能是最有用的一個了。Redis Sampler,基于Ruby的命令行,antirez自己寫的,統計數據分布情況。各個Driver好像只有Jedis比較活躍,但也5個月沒提交了,也是Java里唯一的Redis官方推薦。
Spring Data Redis的封裝并不太必要,因為Jedis已足夠簡單,沒有像Spring Data MongoDB對MongoDB java driver的封裝那樣大幅簡化代碼,頂多就是加強了一點點點pipeline和transaction狀態下的coding,禁止了一些此狀態下不能用的命令。而所謂屏蔽各種底層driver的差異并不太吸引人,因為我就沒打算選其他幾種driver。有興趣的可以翻翻它的JedisConnection代碼。
所以,SpringSide直接在Jedis的基礎上,按Spring的風格封裝了一個JedisTemplate,負責從池中獲取與歸還Jedis實例,處理異常。
Jedis基于Apache Commons Pool做的連接池,默認MaxActive最大連接數只有8,必須重新設置。而且MaxIdle也要相應增大,否則所有新建的連接用完即棄,然后會不停的重新連接。
另外Jedis設定了每30秒對所有連接執行一次ping,以發現失效的連接,這樣每30秒會有一個拿不到連接的高峰。但效果如何需要獨立分析。比如系統高峰之后可能有一長段時間很閑,而且Redis Server那邊做了Timeout控制會把連接斷掉,這時候做idle checking是有意義的,但30秒一次也太過頻繁了。否則關掉它更好。
Jedis的blocking pop函數,應用執行ExecutorService.shutdownNow()中斷線程時并不能把它中斷,見討論組。兩個解決方法:
不要用不限時的blocking popup,傳多一個超時時間參數,如5秒。找地方將調用blocking popup的jedis保存起來,shutdown時主動調用它的close。Windows版本方便對應用的本地開發調試,但Redis并沒有提供,好在微軟提供了一個依賴LibUV實現兼容的補丁,https://github.com/MSOpenTech/redis,但redis作者拒絕合并到master中,微軟只好苦憋的時時人工同步。 目前的穩定版是2.6版本,支持Lua腳本。
因為github現在已經沒有Download服務了,所以編譯好的可執行文件藏在這里:
https://github.com/MSOpenTech/redis/tree/2.6/bin/release注:下文中的鏈接都是網站的架構描述文檔。
Twitter和新浪微博, 都屬于將Redis各種數據結構用得出神入化的那種,如何發布大V如奧巴馬的消息是它們最頭痛的問題。
Tumblr: 11億美刀賣給Yahoo的圖片日志網站,22 臺Redis server,每臺運行8 – 32個實例,總共100多個Redis實例在跑。有著Redis has been completely problem free and the community is great的崇高評價。Redis在里面扮演了八爪魚多面手的角色:
Dashboard的海量通知的存儲。Dashboard的二級索引。存儲海量短鏈接的HBase前面的緩存。Gearman Job Queue的存儲。正在替換另外30臺memcached。Instagram ,曾經,Redis powers their main feed, activity feed, sessions system, and other services。但可惜目前已遷往Cassandra,說新架構只需1/4的硬件費用,是的,就是那個導致Digg CTO辭職的Canssandra。
Flickr , 依然是asynchronous task system and rudimentary queueing system。之前Task system放在mysql innodb,根本,撐不住。
The Others:
Pinterest,混合使用MySQL、Membase與Redis作為存儲。Youporn.com,100%的Redis,MySQL只用于創建新需求用到的sorted set,300K QPS的大壓力。日本微信 ,Redis在前負責異步Job Queue和O(n)的數據,且作為O(n*t)數據的cache,HBase在后,負責O(n*t)數據, n是用戶,t是時間。StackOverflow ,2 Redis servers for distribute caching,好窮好輕量。Github,任務系統Resque的存儲。Discourge,號稱是為下一個十年打造的論壇系統, We use Redis for our job queue, rate limiting, as a cache and for transient data,剛好和我司的用法一樣。extension modules項目封裝了常用的函數與場景,showcase example的src/demo/redis目錄里有各場景的benchmark測試。
典型的Spring Template風格,和JdbcTemplate,HibernateTemplate一樣,封裝從JedisPool獲取與歸還Connecton的代碼,有帶返回值與無返回值兩種返回接口。同時,對最常用的Jedis調用,直接封裝了一系列方法。
Scheduler實現了基于Redis的高并發單次定時任務分發。具體選型見Scheduler章節。
Master Elector基于redis setNx()與expire()兩個api實現,與基于Zookeeper,Hazelcast實現的效果類似。
計有Session,Counter,Scheduler 與 Master Elector四款。
新聞熱點
疑難解答