關(guān)于Redis一些常識(shí)

      網(wǎng)友投稿 976 2022-05-29

      1.1 資料

      ?,最好的入門(mén)小冊(cè)子,可以先于一切文檔之前看,免費(fèi)。

      Redis 命令中文版,?huangz同學(xué)的翻譯。

      Redis設(shè)計(jì)與實(shí)現(xiàn)?,又是huangz同學(xué)的巨作,深入了解內(nèi)部實(shí)現(xiàn)機(jī)制。

      Redis 2.6源碼中文注釋版?,繼續(xù)是huangz同學(xué)的大功德。

      NoSQL Fan里的Redis分類(lèi)

      《Redis in Action》?(Manning, 2013) MEAP版,看目錄挺實(shí)戰(zhàn),亞馬遜中國(guó)預(yù)售250元人民幣。

      1.2 優(yōu)缺點(diǎn)

      非常非常的快,有測(cè)評(píng)說(shuō)比Memcached還快(當(dāng)大家都是單CPU的時(shí)候),而且是無(wú)短板的快,讀寫(xiě)都一般的快,所有API都差不多快,也沒(méi)有MySQL Cluster、MongoDB那樣更新同一條記錄如Counter時(shí)慢下去的毛病。

      讓人又愛(ài)又恨的單線(xiàn)程架構(gòu),使得代碼不用處理平時(shí)最讓人頭痛的并發(fā)而大幅簡(jiǎn)化,但也帶來(lái)CPU的瓶頸,而且單線(xiàn)程被慢操作所阻塞時(shí),其他請(qǐng)求的延時(shí)變得不確定。

      那Redis不是什么?

      Redis 不是Big Data,數(shù)據(jù)都在內(nèi)存中,無(wú)法以T為單位。

      在Redis-Cluster發(fā)布并被穩(wěn)定使用之前,Redis沒(méi)有真正的平滑水平擴(kuò)展能力。

      Redis 不支持Ad-Hoc Query,提供的只是數(shù)據(jù)結(jié)構(gòu)的API,沒(méi)有SQL一樣的查詢(xún)能力。

      1.3 Feature速覽

      所有數(shù)據(jù)都在內(nèi)存中。

      五種數(shù)據(jù)結(jié)構(gòu):String / Hash / List / Set / Ordered Set。

      數(shù)據(jù)過(guò)期時(shí)間支持。

      不完全的事務(wù)支持。

      服務(wù)端腳本:使用Lua Script編寫(xiě),類(lèi)似存儲(chǔ)過(guò)程的作用。

      PubSub:撈過(guò)界的消息一對(duì)多發(fā)布訂閱功能,起碼Redis-Sentinel使用了它。

      持久化:支持定期導(dǎo)出內(nèi)存的Snapshot 與 記錄寫(xiě)操作日志的Append Only File兩種模式。

      Replication:Master-Slave模式,Master可連接多個(gè)只讀Slave,暫無(wú)專(zhuān)門(mén)的Geographic Replication支持。

      Fail-Over:Redis-Sentinel節(jié)點(diǎn)負(fù)責(zé)監(jiān)控Master節(jié)點(diǎn),在master失效時(shí)提升slave,獨(dú)立的仲裁節(jié)點(diǎn)模式有效防止腦裂。

      Sharding:開(kāi)發(fā)中的Redis-Cluser。

      動(dòng)態(tài)配置:所有參數(shù)可用命令行動(dòng)態(tài)配置不需重啟,并重新寫(xiě)回配置文件中,對(duì)云上的大規(guī)模部署非常合適。

      1.4 八卦

      antirez和我一樣不喜歡搞什么咨詢(xún)服務(wù),不過(guò)最近VMWare旗下的Pivotal公司開(kāi)始招聘Redis Commericial Engineer。

      默認(rèn)端口6379,是手機(jī)按鍵上MERZ對(duì)應(yīng)的號(hào)碼,意大利歌女Alessia Merz是antirez和朋友們認(rèn)為愚蠢的代名詞。

      2. 數(shù)據(jù)結(jié)構(gòu)

      2.1 Key

      Key 不能太長(zhǎng),比如1024字節(jié),但antirez也不喜歡太短如”u:1000:pwd”,要表達(dá)清楚意思才好。他私人建議用”:”分隔域,用”.”作為單詞間的連接,如”comment:1234:reply.to”。

      Keys,返回匹配的key,支持通配符如 “keys a*” 、 “keys a?c”,但不建議在生產(chǎn)環(huán)境大數(shù)據(jù)量下使用。

      Sort,對(duì)集合按數(shù)字或字母順序排序后返回或另存為list,還可以關(guān)聯(lián)到外部key等。因?yàn)閺?fù)雜度是最高的O(N+M*log(M))(N是集合大小,M 為返回元素的數(shù)量),有時(shí)會(huì)安排到slave上執(zhí)行。

      Expire/ExpireAt/Persist/TTL,關(guān)于Key超時(shí)的操作。默認(rèn)以秒為單位,也有p字頭的以毫秒為單位的版本, Redis的內(nèi)部實(shí)現(xiàn)見(jiàn)2.9 過(guò)期數(shù)據(jù)清除。

      2.2 String

      最普通的key-value類(lèi)型,說(shuō)是String,其實(shí)是任意的byte[],比如圖片,最大512M。 所有常用命令的復(fù)雜度都是O(1),普通的Get/Set方法,可以用來(lái)做Cache,存Session,為了簡(jiǎn)化架構(gòu)甚至可以替換掉Memcached。

      Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用來(lái)做計(jì)數(shù)器,做自增序列。key不存在時(shí)會(huì)創(chuàng)建并貼心的設(shè)原值為0。IncrByFloat專(zhuān)門(mén)針對(duì)float,沒(méi)有對(duì)應(yīng)的decrByFloat版本?用負(fù)數(shù)啊。

      SetNx, 僅當(dāng)key不存在時(shí)才Set。可以用來(lái)選舉Master或做分布式鎖:所有Client不斷嘗試使用SetNx master myName搶注Master,成功的那位不斷使用Expire刷新它的過(guò)期時(shí)間。如果Master倒掉了key就會(huì)失效,剩下的節(jié)點(diǎn)又會(huì)發(fā)生新一輪搶奪。

      其他Set指令:

      SetEx, Set + Expire 的簡(jiǎn)便寫(xiě)法,p字頭版本以毫秒為單位。

      GetSet, 設(shè)置新值,返回舊值。比如一個(gè)按小時(shí)計(jì)算的計(jì)數(shù)器,可以用GetSet獲取計(jì)數(shù)并重置為0。這種指令在服務(wù)端做起來(lái)是舉手之勞,客戶(hù)端便方便很多。

      MGet/MSet/MSetNx, 一次get/set多個(gè)key。

      2.6.12版開(kāi)始,Set命令已融合了Set/SetNx/SetEx三者,SetNx與SetEx可能會(huì)被廢棄。

      GetBit/SetBit/BitOp,與或非/BitCount,?BitMap的玩法,比如統(tǒng)計(jì)今天的獨(dú)立訪(fǎng)問(wèn)用戶(hù)數(shù)時(shí),每個(gè)注冊(cè)用戶(hù)都有一個(gè)offset,他今天進(jìn)來(lái)的話(huà)就把他那個(gè)位設(shè)為1,用BitCount就可以得出今天的總?cè)藬?shù)。

      Append/SetRange/GetRange/StrLen,對(duì)文本進(jìn)行擴(kuò)展、替換、截取和求長(zhǎng)度,只對(duì)特定數(shù)據(jù)格式如字段定長(zhǎng)的有用,json就沒(méi)什么用。

      2.3 Hash

      關(guān)于Redis的一些常識(shí)

      Key-HashMap結(jié)構(gòu),相比String類(lèi)型將這整個(gè)對(duì)象持久化成JSON格式,Hash將對(duì)象的各個(gè)屬性存入Map里,可以只讀取/更新對(duì)象的某些屬性。這樣有些屬性超長(zhǎng)就讓它一邊呆著不動(dòng),另外不同的模塊可以只更新自己關(guān)心的屬性而不會(huì)互相并發(fā)覆蓋沖突。

      另一個(gè)用法是土法建索引。比如User對(duì)象,除了id有時(shí)還要按name來(lái)查詢(xún)。可以有如下的數(shù)據(jù)記錄:

      (String) user:101 -> {“id”:101,”name”:”calvin”…}

      (String) user:102 -> {“id”:102,”name”:”kevin”…}

      (Hash) user:index-> “calvin”->101, “kevin” -> 102

      底層實(shí)現(xiàn)是hash table,一般操作復(fù)雜度是O(1),要同時(shí)操作多個(gè)field時(shí)就是O(N),N是field的數(shù)量。

      2.4 List

      List是一個(gè)雙向鏈表,支持雙向的Pop/Push,江湖規(guī)矩一般從左端Push,右端Pop——LPush/RPop,而且還有Blocking的版本BLPop/BRPop,客戶(hù)端可以阻塞在那直到有消息到來(lái),所有操作都是O(1)的好孩子,可以當(dāng)Message Queue來(lái)用。當(dāng)多個(gè)Client并發(fā)阻塞等待,有消息入列時(shí)誰(shuí)先被阻塞誰(shuí)先被服務(wù)。任務(wù)隊(duì)列系統(tǒng)Resque是其典型應(yīng)用。

      還有RPopLPush/?BRPopLPush,彈出來(lái)返回給client的同時(shí),把自己又推入另一個(gè)list,LLen獲取列表的長(zhǎng)度。

      還有按值進(jìn)行的操作:LRem(按值刪除元素)、LInsert(插在某個(gè)值的元素的前后),復(fù)雜度是O(N),N是List長(zhǎng)度,因?yàn)長(zhǎng)ist的值不唯一,所以要遍歷全部元素,而Set只要O(log(N))。

      按下標(biāo)進(jìn)行的操作:下標(biāo)從0開(kāi)始,隊(duì)列從左到右算,下標(biāo)為負(fù)數(shù)時(shí)則從右到左。

      LSet?,按下標(biāo)設(shè)置元素值。

      LIndex,按下標(biāo)返回元素。

      LRange,不同于POP直接彈走元素,只是返回列表內(nèi)一段下標(biāo)的元素,是分頁(yè)的最?lèi)?ài)。

      LTrim,限制List的大小,比如只保留最新的20條消息。

      復(fù)雜度也是O(N),其中LSet的N是List長(zhǎng)度,LIndex的N是下標(biāo)的值,LRange的N是start的值+列出元素的個(gè)數(shù),因?yàn)槭擎湵矶皇菙?shù)組,所以按下標(biāo)訪(fǎng)問(wèn)其實(shí)要遍歷鏈表,除非下標(biāo)正好是隊(duì)頭和隊(duì)尾。LTrim的N是移除元素的個(gè)數(shù)。

      在消息隊(duì)列中,并沒(méi)有JMS的ack機(jī)制,如果消費(fèi)者把job給Pop走了又沒(méi)處理完就死機(jī)了怎么辦?

      解決方法之一是加多一個(gè)sorted set,分發(fā)的時(shí)候同時(shí)發(fā)到list與sorted set,以分發(fā)時(shí)間為score,用戶(hù)把job做完了之后要用ZREM消掉sorted set里的job,并且定時(shí)從sorted set中取出超時(shí)沒(méi)有完成的任務(wù),重新放回list。

      另一個(gè)做法是為每個(gè)worker多加一個(gè)的list,彈出任務(wù)時(shí)改用RPopLPush,將job同時(shí)放到worker自己的list中,完成時(shí)用LREM消掉。如果集群管理(如zookeeper)發(fā)現(xiàn)worker已經(jīng)掛掉,就將worker的list內(nèi)容重新放回主list。

      2.5 Set

      Set就是Set,可以將重復(fù)的元素隨便放入而Set會(huì)自動(dòng)去重,底層實(shí)現(xiàn)也是hash table。

      SAdd/SRem/SIsMember/SCard/SMove/SMembers,各種標(biāo)準(zhǔn)操作。除了SMembers都是O(1)。

      SInter/SInterStore/SUnion/SUnionStore/SDiff/SDiffStore,各種集合操作。交集運(yùn)算可以用來(lái)顯示在線(xiàn)好友(在線(xiàn)用戶(hù) 交集 好友列表),共同關(guān)注(兩個(gè)用戶(hù)的關(guān)注列表的交集)。O(N),并集和差集的N是集合大小之和,交集的N是小的那個(gè)集合的大小*2。

      2.6 Sorted Set

      有序集,元素放入集合時(shí)還要提供該元素的分?jǐn)?shù)。

      ZRange/ZRevRange,按排名的上下限返回元素,正數(shù)與倒數(shù)。

      ZRangeByScore/ZRevRangeByScore,按分?jǐn)?shù)的上下限返回元素,正數(shù)與倒數(shù)。

      ZRemRangeByRank/ZRemRangeByScore,按排名/按分?jǐn)?shù)的上下限刪除元素。

      ZCount,統(tǒng)計(jì)分?jǐn)?shù)上下限之間的元素個(gè)數(shù)。

      ZRank/ZRevRank?,顯示某個(gè)元素的正倒序的排名。

      ZScore/ZIncrby,顯示元素的分?jǐn)?shù)/增加元素的分?jǐn)?shù)。

      ZAdd(Add)/ZRem(Remove)/ZCard(Count),ZInsertStore(交集)/ZUnionStore(并集),Set操作,與正牌Set相比,少了IsMember和差集運(yùn)算。

      Sorted Set的實(shí)現(xiàn)是hash table(element->score, 用于實(shí)現(xiàn)ZScore及判斷element是否在集合內(nèi)),和skip list(score->element,按score排序)的混合體。?skip list有點(diǎn)像平衡二叉樹(shù)那樣,不同范圍的score被分成一層一層,每層是一個(gè)按score排序的鏈表。

      ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是結(jié)果/操作元素的個(gè)數(shù)。可見(jiàn),原本可能很大的N被很關(guān)鍵的Log了一下,1000萬(wàn)大小的Set,復(fù)雜度也只是幾十不到。當(dāng)然,如果一次命中很多元素M很大那誰(shuí)也沒(méi)辦法了。

      2.7 事務(wù)

      用Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)實(shí)現(xiàn)。 在事務(wù)提交前,不會(huì)執(zhí)行任何指令,只會(huì)把它們存到一個(gè)隊(duì)列里,不影響其他客戶(hù)端的操作。在事務(wù)提交時(shí),批量執(zhí)行所有指令。《Redis設(shè)計(jì)與實(shí)現(xiàn)》中的詳述。

      注意,Redis里的事務(wù),與我們平時(shí)的事務(wù)概念很不一樣:

      它僅僅是保證事務(wù)里的操作會(huì)被連續(xù)獨(dú)占的執(zhí)行。因?yàn)槭菃尉€(xiàn)程架構(gòu),在執(zhí)行完事務(wù)內(nèi)所有指令前是不可能再去同時(shí)執(zhí)行其他客戶(hù)端的請(qǐng)求的。

      它沒(méi)有隔離級(jí)別的概念,因?yàn)槭聞?wù)提交前任何指令都不會(huì)被實(shí)際執(zhí)行,也就不存在”事務(wù)內(nèi)的查詢(xún)要看到事務(wù)里的更新,在事務(wù)外查詢(xún)不能看到”這個(gè)讓人萬(wàn)分頭痛的問(wèn)題。

      它不保證原子性——所有指令同時(shí)成功或同時(shí)失敗,只有決定是否開(kāi)始執(zhí)行全部指令的能力,沒(méi)有執(zhí)行到一半進(jìn)行回滾的能力。在redis里失敗分兩種,一種是明顯的指令錯(cuò)誤,比如指令名拼錯(cuò),指令參數(shù)個(gè)數(shù)不對(duì),在2.6版中全部指令都不會(huì)執(zhí)行。另一種是隱含的,比如在事務(wù)里,第一句是SET foo bar, 第二句是LLEN foo,對(duì)第一句產(chǎn)生的String類(lèi)型的key執(zhí)行LLEN會(huì)失敗,但這種錯(cuò)誤只有在指令運(yùn)行后才能發(fā)現(xiàn),這時(shí)候第一句成功,第二句失敗。還有,如果事務(wù)執(zhí)行到一半redis被KILL,已經(jīng)執(zhí)行的指令同樣也不會(huì)被回滾。

      Watch指令,類(lèi)似樂(lè)觀鎖,事務(wù)提交時(shí),如果Key的值已被別的客戶(hù)端改變,比如某個(gè)list已被別的客戶(hù)端push/pop過(guò)了,整個(gè)事務(wù)隊(duì)列都不會(huì)被執(zhí)行。

      2.8 Lua Script

      Redis2.6內(nèi)置的Lua Script支持,可以在Redis的Server端一次過(guò)運(yùn)行大量邏輯,就像存儲(chǔ)過(guò)程一樣,避免了海量中間數(shù)據(jù)在網(wǎng)路上的傳輸。

      Lua自稱(chēng)是在Script語(yǔ)言里關(guān)于快的標(biāo)準(zhǔn),Redis選擇了它而不是流行的JavaScript。

      因?yàn)镽edis的單線(xiàn)程架構(gòu),整個(gè)Script默認(rèn)是在一個(gè)事務(wù)里的。

      Script里涉及的所有Key盡量用變量,從外面?zhèn)魅耄筊edis一開(kāi)始就知道你要改變哪些key。(but why?)

      Eval每次傳輸一整段Script比較費(fèi)帶寬,可以先用Script Load載入script,返回哈希值。然后用EvalHash執(zhí)行。因?yàn)榫褪荢HA-1,所以任何時(shí)候執(zhí)行返回的哈希值都是一樣的。

      內(nèi)置的Lua庫(kù)里還很貼心的帶了CJSON,可以處理json字符串。

      一段用Redis做Timer的示例代碼,下面的script被定期調(diào)用,從以觸發(fā)時(shí)間為score的sorted set中取出已到期的Job,放到list中給Client們blocking popup。

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      -- KEYS: [ 1 ]job:sleeping, [ 2 ]job:ready

      -- ARGS: [ 1 ]currentTime

      -- Comments: result is the? job id

      local 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])

      end

      end

      2.9 過(guò)期數(shù)據(jù)清除

      官方文檔?與?《Redis設(shè)計(jì)與實(shí)現(xiàn)》中的詳述,過(guò)期數(shù)據(jù)的清除從來(lái)不容易,為每一條key設(shè)置一個(gè)timer,到點(diǎn)立刻刪除的消耗太大,每秒遍歷所有數(shù)據(jù)消耗也大,Redis使用了一種相對(duì)務(wù)實(shí)的做法: 當(dāng)client主動(dòng)訪(fǎng)問(wèn)key會(huì)先對(duì)key進(jìn)行超時(shí)判斷,過(guò)時(shí)的key會(huì)立刻刪除。 如果clien永遠(yuǎn)都不再get那條key呢? 它會(huì)在Master的后臺(tái),每秒10次的執(zhí)行如下操作: 隨機(jī)選取100個(gè)key校驗(yàn)是否過(guò)期,如果有25個(gè)以上的key過(guò)期了,立刻額外隨機(jī)選取下100個(gè)key(不計(jì)算在10次之內(nèi))。可見(jiàn),如果過(guò)期的key不多,它最多每秒回收200條左右,如果有超過(guò)25%的key過(guò)期了,它就會(huì)做得更多,但只要key不被主動(dòng)get,它占用的內(nèi)存什么時(shí)候最終被清理掉只有天知道。

      3. 性能

      3.1 測(cè)試結(jié)果

      測(cè)試環(huán)境: RHEL 6.3 / HP Gen8 Server/ 2 * Intel Xeon 2.00GHz(6 core) / 64G DDR3 memory / 300G RAID-1 SATA / 1 master(writ AOF), 1 slave(write AOF & RDB)

      數(shù)據(jù)準(zhǔn)備: 預(yù)加載兩千萬(wàn)條數(shù)據(jù),占用10G內(nèi)存。

      測(cè)試工具:自帶的redis-benchmark,默認(rèn)只是基于一個(gè)很小的數(shù)據(jù)集進(jìn)行測(cè)試,調(diào)整命令行參數(shù)如下,就可以開(kāi)100條線(xiàn)程(默認(rèn)50),SET 1千萬(wàn)次(key在0-1千萬(wàn)間隨機(jī)),key長(zhǎng)21字節(jié),value長(zhǎng)256字節(jié)的數(shù)據(jù)。

      1

      redis-benchmark -t SET -c 100 -n 10000000 -r 10000000 -d 256

      測(cè)試結(jié)果(TPS): 1.SET:4.5萬(wàn), 2.GET:6萬(wàn) ,3.INCR:6萬(wàn),4.真實(shí)混合場(chǎng)景: 2.5萬(wàn)SET & 3萬(wàn)GET

      單條客戶(hù)端線(xiàn)程時(shí)6千TPS,50與100條客戶(hù)端線(xiàn)程差別不大,200條時(shí)會(huì)略多。

      Get/Set操作,經(jīng)過(guò)了LAN,延時(shí)也只有1毫秒左右,可以反復(fù)放心調(diào)用,不用像調(diào)用REST接口和訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)那樣,每多一次外部訪(fǎng)問(wèn)都心痛。

      資源監(jiān)控:

      1.CPU: 占了一個(gè)處理器的100%,總CPU是4%(因?yàn)榭偣灿?CPU*6核*超線(xiàn)程 = 24個(gè)處理器),可見(jiàn)單線(xiàn)程下單處理器的能力是瓶頸。 AOF rewrite時(shí)另一個(gè)處理器占用50-70%。

      2.網(wǎng)卡:15-20 MB/s receive, 3Mb/s send(no slave) or 15-20 MB/s send (with slave) 。當(dāng)把value長(zhǎng)度加到4K時(shí),receive 99MB/s,已經(jīng)到達(dá)千兆網(wǎng)卡的瓶頸,TPS降到2萬(wàn)。

      3.硬盤(pán):15MB/s(AOF append), 100MB/s(AOF rewrite/AOF load,普通硬盤(pán)的瓶頸),

      3.2 為什么快

      純ANSI C編寫(xiě)。

      快,原因之一是Redis多樣的數(shù)據(jù)結(jié)構(gòu),每種結(jié)構(gòu)只做自己愛(ài)做的事,當(dāng)然比數(shù)據(jù)庫(kù)只有Table,MongogoDB只有JSON一種結(jié)構(gòu)快了。

      3.3 性能調(diào)優(yōu)

      官方文檔關(guān)于各種產(chǎn)生Latency的原因的詳細(xì)分析,?中文版

      正視網(wǎng)絡(luò)往返時(shí)間:

      1.MSet/LPush/ZAdd等都支持一次輸入多個(gè)Key。

      2.PipeLining模式?可以一次輸入多個(gè)指令。

      3.更快的是Lua Script模式,還可以包含邏輯,直接在服務(wù)端又get又set的,見(jiàn)2.8 Lua Script。

      發(fā)現(xiàn)執(zhí)行緩慢的命令,可配置執(zhí)行超過(guò)多少時(shí)間的指令算是緩慢指令(默認(rèn)10毫秒,不含IO時(shí)間),可以用slowlog get 指令查看(默認(rèn)只保留最后的128條)。單線(xiàn)程的模型下,一個(gè)請(qǐng)求占掉10毫秒是件大事情,注意設(shè)置和顯示的單位為微秒。

      CPU永遠(yuǎn)是瓶頸,但top看到單個(gè)CPU 100%時(shí),就是垂直擴(kuò)展的時(shí)候了。

      持久化對(duì)性能的影響很大,見(jiàn)5.1持久化。

      要熟悉各指令的復(fù)雜度,不過(guò)只要不是O(N)一個(gè)超大集合,都不用太擔(dān)心。

      4. 容量

      4.1 最大內(nèi)存

      所有的數(shù)據(jù)都必須在內(nèi)存中,原來(lái)2.0版的VM策略(將Value放到磁盤(pán),Key仍然放在內(nèi)存),2.4版后嫌麻煩又不支持了。

      一定要設(shè)置最大內(nèi)存,否則物理內(nèi)存用爆了就會(huì)大量使用Swap,寫(xiě)RDB文件時(shí)的速度慢得你想死。

      多留一倍內(nèi)存是最安全的。重寫(xiě)AOF文件和RDB文件的進(jìn)程(即使不做持久化,復(fù)制到Slave的時(shí)候也要寫(xiě)RDB)會(huì)fork出一條新進(jìn)程來(lái),采用了操作系統(tǒng)的Copy-On-Write策略(子進(jìn)程與父進(jìn)程共享Page。如果父進(jìn)程的Page-每頁(yè)4K有修改,父進(jìn)程自己創(chuàng)建那個(gè)Page的副本,不會(huì)影響到子進(jìn)程,父愛(ài)如山)。留意Console打出來(lái)的報(bào)告,如”RDB: 1215 MB of memory used by copy-on-write”。在系統(tǒng)極度繁忙時(shí),如果父進(jìn)程的所有Page在子進(jìn)程寫(xiě)RDB過(guò)程中都被修改過(guò)了,就需要兩倍內(nèi)存。

      按照Redis啟動(dòng)時(shí)的提醒,設(shè)置 vm.overcommit_memory = 1 ,使得fork()一條10G的進(jìn)程時(shí),因?yàn)镃OW策略而不一定需要有10G的free memory。

      其他需要考慮的內(nèi)存包括:

      1.AOF rewrite過(guò)程中對(duì)新寫(xiě)入命令的緩存(rewrite結(jié)束后會(huì)merge到新的aof文件),留意”Background AOF buffer size: 80 MB”的字樣。

      2.負(fù)責(zé)與Slave同步的Client的緩存,默認(rèn)設(shè)置master需要為每個(gè)slave預(yù)留不高于256M的緩存(見(jiàn)5.1持久化)。

      當(dāng)最大內(nèi)存到達(dá)時(shí),按照配置的Policy進(jìn)行處理, 默認(rèn)策略為volatile-lru,對(duì)設(shè)置了expire time的key進(jìn)行LRU清除(不是按實(shí)際expire time)。如果沒(méi)有數(shù)據(jù)設(shè)置了expire time或者policy為noeviction,則直接報(bào)錯(cuò),但此時(shí)系統(tǒng)仍支持get之類(lèi)的讀操作。 另外還有幾種policy,比如volatile-ttl按最接近expire time的,allkeys-lru對(duì)所有key都做LRU。

      4.2 內(nèi)存占用

      測(cè)試表明,string類(lèi)型需要90字節(jié)的額外代價(jià),就是說(shuō)key 1個(gè)字節(jié),value 1個(gè)字節(jié)時(shí),還是需要占用92字節(jié)的長(zhǎng)度,而上面的benchmark的記錄就占用了367個(gè)字節(jié)。其他類(lèi)型可根據(jù)文檔自行計(jì)算或?qū)嶋H測(cè)試一下。

      使用jemalloc分配內(nèi)存,刪除數(shù)據(jù)后,內(nèi)存并不會(huì)乖乖還給操作系統(tǒng)而是被Redis截留下來(lái)重用到新的數(shù)據(jù)上,直到Redis重啟。因此進(jìn)程實(shí)際占用內(nèi)存是看INFO里返回的used_memory_peak_human。

      Redis內(nèi)部用了ziplist/intset這樣的壓縮結(jié)構(gòu)來(lái)減少hash/list/set/zset的存儲(chǔ),默認(rèn)當(dāng)集合的元素少于512個(gè)且最長(zhǎng)那個(gè)值不超過(guò)64字節(jié)時(shí)使用,可配置。

      用make 32bit可以編譯出32位的版本,每個(gè)指針占用的內(nèi)存更小,但只支持最大4GB內(nèi)存。

      4.4 水平分區(qū),Sharding

      其實(shí),大內(nèi)存加上垂直分區(qū)也夠了,不一定非要沙丁一把。

      Jedis支持在客戶(hù)端做分區(qū),局限是不能動(dòng)態(tài)re-sharding, 有分區(qū)的master倒了,不能減少分區(qū)必須用slave頂上。要增加分區(qū)的話(huà),呃…..

      antire在博客里提到了Twemproxy,一個(gè)Twitter寫(xiě)的Proxy,但它在發(fā)現(xiàn)節(jié)點(diǎn)倒掉后,只會(huì)重新計(jì)算一致性哈希環(huán),把數(shù)據(jù)存到別的master去,而不是集成Sentinel指向新由slave升級(jí)的master,像Memcached一樣的做法也只適合做Cache的場(chǎng)景。

      Redis-Cluster是今年工作重點(diǎn),支持automatic re-sharding, 采用和Hazelcast類(lèi)似的算法,總共有N個(gè)分區(qū)(eg.N=1024),每臺(tái)Server負(fù)責(zé)若干個(gè)分區(qū)。

      在客戶(hù)端先hash出key 屬于哪個(gè)分區(qū),隨便發(fā)給一臺(tái)server,server會(huì)告訴它真正哪個(gè)Server負(fù)責(zé)這個(gè)分區(qū),緩存下來(lái),下次還有該分區(qū)的請(qǐng)求就直接發(fā)到地兒了。

      Re-sharding時(shí),會(huì)將某些分區(qū)的數(shù)據(jù)移到新的Server上,完成后各Server周知分區(qū)<->Server映射的變化,因?yàn)榉謪^(qū)數(shù)量有限,所以通訊量不大。 在遷移過(guò)程中,客戶(hù)端緩存的依然是舊的分區(qū)映射信息,原server對(duì)于已經(jīng)遷移走的數(shù)據(jù)的get請(qǐng)求,會(huì)返回一個(gè)臨時(shí)轉(zhuǎn)向的應(yīng)答,客戶(hù)端先不會(huì)更新Cache。等遷移完成了,就會(huì)像前面那樣返回一條永久轉(zhuǎn)向信息,客戶(hù)端更新Cache,以后就都去新server了。

      5. 高可用性

      高可用性關(guān)乎系統(tǒng)出錯(cuò)時(shí)到底會(huì)丟失多少數(shù)據(jù),多久不能服務(wù)。要綜合考慮持久化,Master-Slave復(fù)制及Fail-Over配置,以及具體Crash情形,比如Master死了,但Slave沒(méi)死。或者只是Redis死了,操作系統(tǒng)沒(méi)死等等。

      5.1 持久化

      綜述:?解密Redis持久化(中文概括版),?英文原版,《Redis設(shè)計(jì)與實(shí)現(xiàn)》:?RDB?與?AOF。

      正確關(guān)閉服務(wù)器:redis-cli shutdown 或者 kill,都會(huì)graceful shutdown,保證寫(xiě)RDB文件以及將AOF文件fsync到磁盤(pán),不會(huì)丟失數(shù)據(jù)。 如果是粗暴的Ctrl+C,或者kill -9 就可能丟失。

      RDB是整個(gè)內(nèi)存的壓縮過(guò)的Snapshot,RDB的數(shù)據(jù)結(jié)構(gòu),可以配置復(fù)合的快照觸發(fā)條件,默認(rèn)是1分鐘內(nèi)改了1萬(wàn)次,或5分鐘內(nèi)改了10次,或15分鐘內(nèi)改了1次。

      RDB寫(xiě)入時(shí),會(huì)連內(nèi)存一起Fork出一個(gè)新進(jìn)程,遍歷新進(jìn)程內(nèi)存中的數(shù)據(jù)寫(xiě)文件,這樣就解決了些Snapshot過(guò)程中又有新的寫(xiě)入請(qǐng)求進(jìn)來(lái)的問(wèn)題。 Fork的細(xì)節(jié)見(jiàn)4.1最大內(nèi)存。

      RDB會(huì)先寫(xiě)到臨時(shí)文件,完了再Rename成,這樣外部程序?qū)DB文件的備份和傳輸過(guò)程是安全的。而且即使寫(xiě)新快照的過(guò)程中Server被強(qiáng)制關(guān)掉了,舊的RDB文件還在。

      可配置是否進(jìn)行壓縮,壓縮方法是字符串的LZF算法,以及將string形式的數(shù)字變回int形式存儲(chǔ)。

      動(dòng)態(tài)所有停止RDB保存規(guī)則的方法:redis-cli config set save “”

      操作日志,記錄所有有效的寫(xiě)操作,等于mysql的binlog,格式就是明文的Redis協(xié)議的純文本文件。

      一般配置成每秒調(diào)用一次fdatasync將kernel的文件緩存刷到磁盤(pán)。當(dāng)操作系統(tǒng)非正常關(guān)機(jī)時(shí),文件可能會(huì)丟失不超過(guò)2秒的數(shù)據(jù)(更嚴(yán)謹(jǐn)?shù)亩x見(jiàn)后)。 如果設(shè)為fsync always,性能只剩幾百TPS,不用考慮。如果設(shè)為no,靠操作系統(tǒng)自己的sync,Linux系統(tǒng)一般30秒一次。

      AOF文件持續(xù)增長(zhǎng)而過(guò)大時(shí),會(huì)fork出一條新進(jìn)程來(lái)將文件重寫(xiě)(也是先寫(xiě)臨時(shí)文件,最后再rename,), 遍歷新進(jìn)程的內(nèi)存中數(shù)據(jù),每條記錄有一條的Set語(yǔ)句。默認(rèn)配置是當(dāng)AOF文件大小是上次rewrite后大小的一倍,且文件大于64M時(shí)觸發(fā)。

      Redis協(xié)議,如set mykey hello, 將持久化成*3 $3 set $5 mykey $5 hello, 第一個(gè)數(shù)字代表這條語(yǔ)句有多少元,其他的數(shù)字代表后面字符串的長(zhǎng)度。這樣的設(shè)計(jì),使得即使在寫(xiě)文件過(guò)程中突然關(guān)機(jī)導(dǎo)致文件不完整,也能自我修復(fù),執(zhí)行redis-check-aof即可。

      AOF重寫(xiě)和RDB寫(xiě)入都是在fork出新進(jìn)程后,遍歷新進(jìn)程的內(nèi)存順序?qū)懙模炔蛔枞鬟M(jìn)程繼續(xù)處理客戶(hù)端請(qǐng)求,順序?qū)懙乃俣纫脖入S機(jī)寫(xiě)快。

      測(cè)試把剛才benchmark的11G數(shù)據(jù)寫(xiě)成一個(gè)1.3的RDB文件,或者等大的AOF文件rewrite,需要80秒,在redis-cli info中可查看。啟動(dòng)時(shí)載入一個(gè)AOF或RDB文件的速度與上面寫(xiě)入時(shí)相同,在log中可查看。

      Fork一個(gè)使用了大量?jī)?nèi)存的進(jìn)程也要時(shí)間,大約10ms per GB的樣子,但Xen在EC2上是讓人郁悶的239ms (KVM和VMWare貌似沒(méi)有這個(gè)毛病),各種系統(tǒng)的對(duì)比,Info指令里的latest_fork_usec顯示上次花費(fèi)的時(shí)間。

      在bgrewriteaof過(guò)程中,所有新來(lái)的寫(xiě)入請(qǐng)求依然會(huì)被寫(xiě)入舊的AOF文件,同時(shí)放到buffer中,當(dāng)rewrite完成后,會(huì)在主線(xiàn)程把這部分內(nèi)容合并到臨時(shí)文件中之后才rename成新的AOF文件,所以rewrite過(guò)程中會(huì)不斷打印”Background AOF buffer size: 80 MB, Background AOF buffer size: 180 MB”,計(jì)算系統(tǒng)容量時(shí)要留意這部分的內(nèi)存消耗。注意,這個(gè)合并的過(guò)程是阻塞的,如果你產(chǎn)生了280MB的buffer,在100MB/s的傳統(tǒng)硬盤(pán)上,Redis就要阻塞2.8秒!!!

      NFS或者Amazon上的EBS都不推薦,因?yàn)樗鼈円惨膸挕?/p>

      bgsave和bgaofrewrite不會(huì)被同時(shí)執(zhí)行,如果bgsave正在執(zhí)行,bgaofrewrite會(huì)自動(dòng)延后。

      2.4版以后,寫(xiě)入AOF時(shí)的fdatasync由另一條線(xiàn)程來(lái)執(zhí)行,不會(huì)再阻塞主線(xiàn)程。

      2.4版以后,lpush/zadd可以輸入一次多個(gè)值了,使得AOF重寫(xiě)時(shí)可以將舊版本中的多個(gè)lpush/zadd指令合成一個(gè),每64個(gè)key串一串。

      因?yàn)镽DB文件只用作后備用途,建議只在Slave上持久化RDB文件,而且只要15分鐘備份一次就夠了,只保留save 900 1這條規(guī)則。

      如果Enalbe AOF,好處是在最?lèi)毫忧闆r下也只會(huì)丟失不超過(guò)兩秒數(shù)據(jù),啟動(dòng)腳本較簡(jiǎn)單只load自己的AOF文件就可以了。代價(jià)一是帶來(lái)了持續(xù)的IO,二是AOF rewrite的最后將rewrite過(guò)程中產(chǎn)生的新數(shù)據(jù)寫(xiě)到新文件造成的阻塞幾乎是不可避免的。只要硬盤(pán)許可,應(yīng)該盡量減少AOF rewrite的頻率,AOF重寫(xiě)的基礎(chǔ)大小默認(rèn)值64M太小了,可以設(shè)到5G以上。默認(rèn)超過(guò)原大小100%大小時(shí)重寫(xiě)可以改到適當(dāng)?shù)臄?shù)值,比如之前的benchmark每個(gè)小時(shí)會(huì)產(chǎn)生40G大小的AOF文件,如果硬盤(pán)能撐到半夜系統(tǒng)閑時(shí)才用cron調(diào)度bgaofrewrite就好了。

      如果不Enable AOF ,僅靠Master-Slave Replication 實(shí)現(xiàn)高可用性也可以。能省掉一大筆IO也減少了rewrite時(shí)帶來(lái)的系統(tǒng)波動(dòng)。代價(jià)是如果Master/Slave同時(shí)倒掉,會(huì)丟失十幾分鐘的數(shù)據(jù),啟動(dòng)腳本也要比較兩個(gè)Master/Slave中的RDB文件,載入較新的那個(gè)。新浪微博就選用了這種架構(gòu),見(jiàn)Tim的博客

      現(xiàn)象描述:當(dāng)AOF rewrite 15G大小的內(nèi)存時(shí),Redis整個(gè)死掉的樣子,所有指令甚至包括slave發(fā)到master的ping,redis-cli info都不能被執(zhí)行。

      原因分析:

      官方文檔,由IO產(chǎn)生的Latency詳細(xì)分析, 已經(jīng)預(yù)言了悲劇的發(fā)生,但一開(kāi)始沒(méi)留意。

      Redis為求簡(jiǎn)單,采用了單請(qǐng)求處理線(xiàn)程結(jié)構(gòu)。

      打開(kāi)AOF持久化功能后, Redis處理完每個(gè)事件后會(huì)調(diào)用write(2)將變化寫(xiě)入kernel的buffer,如果此時(shí)write(2)被阻塞,Redis就不能處理下一個(gè)事件。

      Linux規(guī)定執(zhí)行write(2)時(shí),如果對(duì)同一個(gè)文件正在執(zhí)行fdatasync(2)將kernel buffer寫(xiě)入物理磁盤(pán),或者有system wide sync在執(zhí)行,write(2)會(huì)被block住,整個(gè)Redis被block住。

      如果系統(tǒng)IO繁忙,比如有別的應(yīng)用在寫(xiě)盤(pán),或者Redis自己在AOF rewrite或RDB snapshot(雖然此時(shí)寫(xiě)入的是另一個(gè)臨時(shí)文件,雖然各自都在連續(xù)寫(xiě),但兩個(gè)文件間的切換使得磁盤(pán)磁頭的尋道時(shí)間加長(zhǎng)),就可能導(dǎo)致fdatasync(2)遲遲未能完成從而block住write(2),block住整個(gè)Redis。

      為了更清晰的看到fdatasync(2)的執(zhí)行時(shí)長(zhǎng),可以使用”strace -p (pid of redis server) -T -e -f trace=fdatasync”,但會(huì)影響系統(tǒng)性能。

      Redis提供了一個(gè)自救的方式,當(dāng)發(fā)現(xiàn)文件有在執(zhí)行fdatasync(2)時(shí),就先不調(diào)用write(2),只存在cache里,免得被block。但如果已經(jīng)超過(guò)兩秒都還是這個(gè)樣子,則會(huì)硬著頭皮執(zhí)行write(2),即使redis會(huì)被block住。此時(shí)那句要命的log會(huì)打印:“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。

      因此,對(duì)于fsync設(shè)為everysec時(shí)丟失數(shù)據(jù)的可能性的最嚴(yán)謹(jǐn)說(shuō)法是:如果有fdatasync在長(zhǎng)時(shí)間的執(zhí)行,此時(shí)redis意外關(guān)閉會(huì)造成文件里不多于兩秒的數(shù)據(jù)丟失。如果fdatasync運(yùn)行正常,redis意外關(guān)閉沒(méi)有影響,只有當(dāng)操作系統(tǒng)crash時(shí)才會(huì)造成少于1秒的數(shù)據(jù)丟失。

      解決方法:

      最后發(fā)現(xiàn),原來(lái)是AOF rewrite時(shí)一直埋頭的調(diào)用write(2),由系統(tǒng)自己去觸發(fā)sync。在RedHat Enterprise 6里,默認(rèn)配置vm.dirty_background_ratio=10,也就是占用了10%的可用內(nèi)存才會(huì)開(kāi)始后臺(tái)flush,而我的服務(wù)器有64G內(nèi)存。很明顯一次flush太多數(shù)據(jù)會(huì)造成阻塞,所以最后果斷設(shè)置了sysctl vm.dirty_bytes=33554432(32M),問(wèn)題解決。

      然后提了個(gè)issue,AOF rewrite時(shí)定時(shí)也執(zhí)行一下fdatasync嘛, antirez三分鐘后就回復(fù)了,新版中,AOF rewrite時(shí)32M就會(huì)重寫(xiě)主動(dòng)調(diào)用fdatasync。

      5.2 Master-Slave復(fù)制

      slave可以在配置文件、啟動(dòng)命令行參數(shù)、以及redis-cli執(zhí)行SlaveOf指令來(lái)設(shè)置自己是奴隸。

      測(cè)試表明同步延時(shí)非常小,指令一旦執(zhí)行完畢就會(huì)立刻寫(xiě)AOF文件和向Slave轉(zhuǎn)發(fā),除非Slave自己被阻塞住了。

      比較蠢的是,即使在配置文件里設(shè)了slavof,slave啟動(dòng)時(shí)依然會(huì)先從數(shù)據(jù)文件載入一堆沒(méi)用的數(shù)據(jù),再去執(zhí)行slaveof。

      “Slaveof no one”,立馬變身master。

      2.8版本將支持PSYNC部分同步,master會(huì)撥出一小段內(nèi)存來(lái)存放要發(fā)給slave的指令,如果slave短暫的斷開(kāi)了,重連時(shí)會(huì)從內(nèi)存中讀取需要補(bǔ)讀的指令,這樣就不需要斷開(kāi)兩秒也搞一次全同步了。但如果斷開(kāi)時(shí)間較長(zhǎng),已經(jīng)超過(guò)了內(nèi)存中保存的數(shù)據(jù),就還是要全同步。

      Slave也可以接收Read-Only的請(qǐng)求。

      先執(zhí)行一次全同步 — 請(qǐng)求master BgSave出自己的一個(gè)RDB Snapshot文件發(fā)給slave,slave接收完畢后,清除掉自己的舊數(shù)據(jù),然后將RDB載入內(nèi)存。

      再進(jìn)行增量同步 — master作為一個(gè)普通的client連入slave,將所有寫(xiě)操作轉(zhuǎn)發(fā)給slave,沒(méi)有特殊的同步協(xié)議。

      有時(shí)候明明master/slave都活得好好的,突然間就說(shuō)要重新進(jìn)行全同步了:

      1.Slave顯示:# MASTER time out: no data nor PING received…

      slave會(huì)每隔repl-ping-slave-period(默認(rèn)10秒)ping一次master,如果超過(guò)repl-timeout(默認(rèn)60秒)都沒(méi)有收到響應(yīng),就會(huì)認(rèn)為Master掛了。如果Master明明沒(méi)掛但被阻塞住了也會(huì)報(bào)這個(gè)錯(cuò)。可以適當(dāng)調(diào)大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.

      當(dāng)slave沒(méi)掛但被阻塞住了,比如正在loading Master發(fā)過(guò)來(lái)的RDB, Master的指令不能立刻發(fā)送給slave,就會(huì)放在output buffer中(見(jiàn)oll是命令數(shù)量,omem是大小),在配置文件中有如下配置:client-output-buffer-limit slave 256mb 64mb 60, 這是說(shuō)負(fù)責(zé)發(fā)數(shù)據(jù)給slave的client,如果buffer超過(guò)256m或者連續(xù)60秒超過(guò)64m,就會(huì)被立刻強(qiáng)行關(guān)閉!!! Traffic大的話(huà)一定要設(shè)大一點(diǎn)。否則就會(huì)出現(xiàn)一個(gè)很悲劇的循環(huán),Master傳輸一個(gè)大的RDB給Slave,Slave努力的裝載,但還沒(méi)裝載完,Master對(duì)client的緩存滿(mǎn)了,再來(lái)一次。

      平時(shí)可以在master執(zhí)行?redis-cli client list?找那個(gè)cmd=sync,flag=S的client,注意OMem的變化。

      5.3 Fail-Over

      Redis-sentinel是2.6版開(kāi)始加入的另一組獨(dú)立運(yùn)行的節(jié)點(diǎn),提供自動(dòng)Fail Over的支持。

      官方文檔?與?Redis核心解讀–集群管理工具(Redis-sentinel)

      antirez 對(duì) Sentinel的反駁,與下篇

      Sentinel每秒鐘對(duì)所有master,slave和其他sentinel執(zhí)行Ping,redis-server節(jié)點(diǎn)要應(yīng)答+PONG或-LOADING或-MASTERDOWN.

      如果某一臺(tái)Sentinel沒(méi)有在30秒內(nèi)(可配置得短一些哦)收到上述正確應(yīng)答,它就會(huì)認(rèn)為master處于sdown狀態(tài)(主觀Down)

      它向其他sentinel詢(xún)問(wèn)是否也認(rèn)為該master倒了(SENTINEL is-master-down-by-addr ), 如果quonum臺(tái)(默認(rèn)是2)sentinel在5秒鐘內(nèi)都這樣認(rèn)為,就會(huì)認(rèn)為master真是odown了(客觀Down)。

      此時(shí)會(huì)選出一臺(tái)sentinel作為L(zhǎng)eader執(zhí)行fail-over, Leader會(huì)從slave中選出一個(gè)提升為master(執(zhí)行slaveof no one),然后讓其他slave指向它(執(zhí)行slaveof new master)。

      master地址在sentinel.conf里, sentinel會(huì)每10秒一次向master發(fā)送INFO,知道m(xù)aster的slave有哪些。 如果master已經(jīng)變?yōu)閟lave,sentinel會(huì)分析INFO的應(yīng)答指向新的master。以前,sentinel重啟時(shí),如果master已經(jīng)切換過(guò)了,但sentinel.conf里master的地址并沒(méi)有變,很可能有悲劇發(fā)生。另外master重啟后如果沒(méi)有切換成slave,也可能有悲劇發(fā)生。新版好像修復(fù)了一點(diǎn)這個(gè)問(wèn)題,待研究。

      另外,sentinel會(huì)在master上建一個(gè)pub/sub channel,名為”sentinel:hello”,通告各種信息,sentinel們也是通過(guò)接收pub/sub channel上的+sentinel的信息發(fā)現(xiàn)彼此,因?yàn)槊颗_(tái)sentinel每5秒會(huì)發(fā)送一次自己的host信息,宣告自己的存在。

      sentinel在failover時(shí)還會(huì)執(zhí)行配置文件里指定的用戶(hù)自定義reconfig腳本,做用戶(hù)自己想做的事情,比如讓master變?yōu)閟lave并指向新的master。

      腳本的將會(huì)在命令行按順序傳入如下參數(shù):

      腳本返回0是正常,如果返回1會(huì)被重新執(zhí)行,如果返回2或以上不會(huì)。 如果超過(guò)60秒沒(méi)返回會(huì)被強(qiáng)制終止。

      覺(jué)得Sentinel至少有兩個(gè)可提升的地方:

      一是如果master 主動(dòng)shutdown,比如系統(tǒng)升級(jí),有辦法主動(dòng)通知sentinel提升新的master,減少服務(wù)中斷時(shí)間。

      二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 啟動(dòng),也不支持shutdown命令,要自己kill pid。

      5.4 Client的高可用性

      基于Sentinel的方案,client需要執(zhí)行語(yǔ)句SENTINEL get-master-addr-by-name mymaster 可獲得當(dāng)前master的地址。 Jedis正在集成sentinel,已經(jīng)支持了sentinel的一些指令,但還沒(méi)發(fā)布,但sentinel版的連接池則暫時(shí)完全沒(méi)有,在公司的項(xiàng)目里我參考網(wǎng)友的項(xiàng)目自己寫(xiě)了一個(gè)。

      淘寶的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多寫(xiě)隨機(jī)讀, 一開(kāi)始就同步寫(xiě)入到所有節(jié)點(diǎn),讀的話(huà)隨便讀一個(gè)還活著的節(jié)點(diǎn)就行了。但有些節(jié)點(diǎn)成功有些節(jié)點(diǎn)失敗如何處理? 節(jié)點(diǎn)死掉重新起來(lái)后怎么重新同步?什么時(shí)候可以重新Ready? 所以不是很敢用。

      另外如Ruby寫(xiě)的redis_failover,也是拋開(kāi)了Redis Sentinel,基于ZooKeeper的臨時(shí)方案。

      6. 運(yùn)維

      6.1 安裝

      安裝包制作:沒(méi)有現(xiàn)成,需要自己編譯,自己寫(xiě)rpm包的腳本,可參考utils中的install_server.sh與redis_init_script。

      但RHEL下設(shè)定script runlevel的方式不一樣,redis_init_script中要增加一句 “# chkconfig: 345 90 10″ ,而install_server.sh可以刪掉后面的那句“chkconfig –level 345 reis”

      云服務(wù):Redis Cloud,在Amazon、Heroku、Windows Azure、App Frog上提供云服務(wù),供同樣部署在這些云上的應(yīng)用使用。其他的云服務(wù)有GarantiaData,已被redis-cloud收購(gòu)。另外還有Redis To Go,?OpenRedis,?RedisGreen。

      CopperEgg統(tǒng)計(jì)自己的用戶(hù)在AWS上的數(shù)據(jù)庫(kù)部署:mysqld占了50%半壁江山, redis占了18%排第二, mongodb也有11%, cassandra是3%,Oracle只有可憐的2%。

      Chef Recipes:brianbianco/redisio,活躍,同步更新版本。

      6.2 部署模型

      Redis只能使用單線(xiàn)程,為了提高CPU利用率,有提議在同一臺(tái)服務(wù)器上啟動(dòng)多個(gè)Redis實(shí)例,但這會(huì)帶來(lái)嚴(yán)重的IO爭(zhēng)用,除非Redis不需要持久化,或者有某種方式保證多個(gè)實(shí)例不會(huì)在同一個(gè)時(shí)間重寫(xiě)AOF。

      一組sentinel能同時(shí)監(jiān)控多個(gè)Master。

      有提議說(shuō)環(huán)形的slave結(jié)構(gòu),即master只連一個(gè)slave,然后slave再連slave,此部署有兩個(gè)前提,一是有大量的只讀需求需要在slave完成,二是對(duì)slave傳遞時(shí)的數(shù)據(jù)不一致性不敏感。

      6.3 配置

      約30個(gè)配置項(xiàng),全都有默認(rèn)配置,對(duì)redif.conf默認(rèn)配置的修改見(jiàn)附錄1。

      可以配置文件中編寫(xiě)。

      可以在啟動(dòng)時(shí)的命令行配置,redis-server –port 7777 –slaveof 127.0.0.1 8888。

      云時(shí)代大規(guī)模部署,把配置文件滿(mǎn)街傳顯然不是好的做法, 可以用redis-cli執(zhí)行Config Set指令, 修改所有的參數(shù),達(dá)到維護(hù)人員最?lèi)?ài)的不重啟服務(wù)而修改參數(shù)的效果,而且在新版本里還可以執(zhí)行?Config Rewrite?將改動(dòng)寫(xiě)回到文件中,不過(guò)全部默認(rèn)值都會(huì)打印出來(lái),可能會(huì)破壞掉原來(lái)的文件的排版,注釋。

      在配置文件里設(shè)置密碼:requirepass foobar。

      禁止某些危險(xiǎn)命令,比如殘暴的FlushDB,將它rename成”":rename-command FLUSHDB “”。

      6.4 監(jiān)控與維護(hù)

      綜述:?Redis監(jiān)控技巧

      Info指令將返回非常豐富的信息。 著重監(jiān)控檢查內(nèi)存使用,是否已接近上限,used_memory是Redis申請(qǐng)的內(nèi)存,used_memory_rss是操作系統(tǒng)分配給Redis的物理內(nèi)存,兩者之間隔著碎片,隔著Swap。 還有重點(diǎn)監(jiān)控 AOF與RDB文件的保存情況,以及master-slave的關(guān)系。Statistic 信息還包括key命中率,所有命令的執(zhí)行次數(shù),所有client連接數(shù)量等,?CONFIG RESETSTAT?可重置為0。

      Monitor指令可以顯示Server收到的所有指令,主要用于debug,影響性能,生產(chǎn)環(huán)境慎用。

      SlowLog 檢查慢操作(見(jiàn)2.性能)。

      日志可以動(dòng)態(tài)的設(shè)置成verbose/debug模式,但不見(jiàn)得有更多有用的log可看,verbose還會(huì)很煩的每5秒打印當(dāng)前的key情況和client情況。指令為config set loglevel verbose。

      最?lèi)?ài)Redis的地方是代碼只有2.3萬(wàn)行,而且編碼優(yōu)美,而且huangz同學(xué)還在原來(lái)的注釋上再加上了中文注釋——Redis 2.6源碼中文注釋版?,所以雖然是C寫(xiě)的代碼,雖然有十年沒(méi)看過(guò)C代碼,但這幾天trouble shooting毫無(wú)難度,一看就懂。

      Trobule shotting的經(jīng)歷證明antirez處理issue的速度非常快(如果你的issue言之有物的話(huà)),比Weblogic之類(lèi)的商業(yè)支持還好。

      如果AOF文件在寫(xiě)入過(guò)程中crash,可以用redis-check-aof修復(fù),見(jiàn)5.1.2

      如果AOF rewrite和 RDB snapshot的過(guò)程中crash,會(huì)留下無(wú)用的臨時(shí)文件,需要定期掃描刪除。

      官網(wǎng)列出了如下工具,但暫時(shí)沒(méi)發(fā)現(xiàn)會(huì)直接拿來(lái)用的:

      Redis Live,基于Python的web應(yīng)用,使用Info和Monitor獲得系統(tǒng)情況和指令統(tǒng)計(jì)分析。 因?yàn)镸onitor指令影響性能,所以建議用cron定期運(yùn)行,每次偷偷采樣兩分鐘的樣子。

      phpRedisAdmin,基于php的Web應(yīng)用,目標(biāo)是MysqlAdmin那樣的管理工具,可以管理每一條Key的情況,但它的界面應(yīng)該只適用于Key的數(shù)量不太多的情況,Demo。

      Redis Faina,基于Python的命令行,Instagram出品,用戶(hù)自行獲得Monitor的輸出后發(fā)給它進(jìn)行統(tǒng)計(jì)分析。由于Monitor輸出的格式在Redis版本間不一樣,要去github下最新版。

      Redis-rdb-tools?基于Python的命令行,可以分析RDB文件每條Key對(duì)應(yīng)value所占的大小,還可以將RDB dump成普通文本文件然后比較兩個(gè)庫(kù)是否一致,還可以將RDB輸出成JSON格式,可能是最有用的一個(gè)了。

      Redis Sampler,基于Ruby的命令行,antirez自己寫(xiě)的,統(tǒng)計(jì)數(shù)據(jù)分布情況。

      7. Java Driver

      7.1 Driver選擇

      各個(gè)Driver好像只有Jedis比較活躍,但也5個(gè)月沒(méi)提交了,也是Java里唯一的Redis官方推薦。

      Spring Data Redis的封裝并不太必要,因?yàn)镴edis已足夠簡(jiǎn)單,沒(méi)有像Spring Data MongoDB對(duì)MongoDB java driver的封裝那樣大幅簡(jiǎn)化代碼,頂多就是加強(qiáng)了一點(diǎn)點(diǎn)點(diǎn)pipeline和transaction狀態(tài)下的coding,禁止了一些此狀態(tài)下不能用的命令。而所謂屏蔽各種底層driver的差異并不太吸引人,因?yàn)槲揖蜎](méi)打算選其他幾種driver。有興趣的可以翻翻它的JedisConnection代碼。

      所以,SpringSide直接在Jedis的基礎(chǔ)上,按Spring的風(fēng)格封裝了一個(gè)JedisTemplate,負(fù)責(zé)從池中獲取與歸還Jedis實(shí)例,處理異常。

      7.2 Jedis的細(xì)節(jié)

      Jedis基于Apache Commons Pool做的連接池,默認(rèn)MaxActive最大連接數(shù)只有8,必須重新設(shè)置。而且MaxIdle也要相應(yīng)增大,否則所有新建的連接用完即棄,然后會(huì)不停的重新連接。

      另外Jedis設(shè)定了每30秒對(duì)所有連接執(zhí)行一次ping,以發(fā)現(xiàn)失效的連接,這樣每30秒會(huì)有一個(gè)拿不到連接的高峰。但效果如何需要獨(dú)立分析。比如系統(tǒng)高峰之后可能有一長(zhǎng)段時(shí)間很閑,而且Redis Server那邊做了Timeout控制會(huì)把連接斷掉,這時(shí)候做idle checking是有意義的,但30秒一次也太過(guò)頻繁了。否則關(guān)掉它更好。

      Jedis的blocking pop函數(shù),應(yīng)用執(zhí)行ExecutorService.shutdownNow()中斷線(xiàn)程時(shí)并不能把它中斷,見(jiàn)討論組。兩個(gè)解決方法:

      不要用不限時(shí)的blocking popup,傳多一個(gè)超時(shí)時(shí)間參數(shù),如5秒。

      找地方將調(diào)用blocking popup的jedis保存起來(lái),shutdown時(shí)主動(dòng)調(diào)用它的close。

      7.3 Redis對(duì)Client端連接的處理

      Redis默認(rèn)最大連接數(shù)是一萬(wàn)。

      Redis默認(rèn)不對(duì)Client做Timeout處理,可以用timeout 項(xiàng)配置,但即使配了也不會(huì)非常精確。

      8. Windows的版本

      因?yàn)間ithub現(xiàn)在已經(jīng)沒(méi)有Download服務(wù)了,所以編譯好的可執(zhí)行文件藏在這里:

      https://github.com/MSOpenTech/redis/tree/2.6/bin/release

      9. 成功案例

      注:下文中的鏈接都是網(wǎng)站的架構(gòu)描述文檔。

      Twitter和新浪微博, 都屬于將Redis各種數(shù)據(jù)結(jié)構(gòu)用得出神入化的那種,如何發(fā)布大V如奧巴馬的消息是它們最頭痛的問(wèn)題。

      Tumblr: 11億美刀賣(mài)給Yahoo的圖片日志網(wǎng)站,22 臺(tái)Redis server,每臺(tái)運(yùn)行8 – 32個(gè)實(shí)例,總共100多個(gè)Redis實(shí)例在跑。有著Redis has been completely problem free and the community is great的崇高評(píng)價(jià)。Redis在里面扮演了八爪魚(yú)多面手的角色:

      Dashboard的海量通知的存儲(chǔ)。

      Dashboard的二級(jí)索引。

      存儲(chǔ)海量短鏈接的HBase前面的緩存。

      Gearman Job Queue的存儲(chǔ)。

      正在替換另外30臺(tái)memcached。

      Instagram?,曾經(jīng),Redis powers their main feed, activity feed, sessions system, and?other services。但可惜目前已遷往Cassandra,說(shuō)新架構(gòu)只需1/4的硬件費(fèi)用,是的,就是那個(gè)導(dǎo)致Digg CTO辭職的Canssandra。

      Flickr?, 依然是asynchronous task system and rudimentary queueing system。之前Task system放在mysql innodb,根本,撐不住。

      The Others:

      Pinterest,混合使用MySQL、Membase與Redis作為存儲(chǔ)。

      Youporn.com,100%的Redis,MySQL只用于創(chuàng)建新需求用到的sorted set,300K QPS的大壓力。

      日本微信?,Redis在前負(fù)責(zé)異步Job Queue和O(n)的數(shù)據(jù),且作為O(n*t)數(shù)據(jù)的cache,HBase在后,負(fù)責(zé)O(n*t)數(shù)據(jù), n是用戶(hù),t是時(shí)間。

      StackOverflow?,2 Redis servers for distribute caching,好窮好輕量。

      Github,任務(wù)系統(tǒng)Resque的存儲(chǔ)。

      Discourge,號(hào)稱(chēng)是為下一個(gè)十年打造的論壇系統(tǒng), We use Redis for our job queue, rate limiting, as a cache and for transient data,剛好和我司的用法一樣。

      10. In SpringSide

      extension modules項(xiàng)目封裝了常用的函數(shù)與場(chǎng)景,showcase example的src/demo/redis目錄里有各場(chǎng)景的benchmark測(cè)試。

      10.1 Jedis Template

      典型的Spring Template風(fēng)格,和JdbcTemplate,HibernateTemplate一樣,封裝從JedisPool獲取與歸還Connecton的代碼,有帶返回值與無(wú)返回值兩種返回接口。同時(shí),對(duì)最常用的Jedis調(diào)用,直接封裝了一系列方法。

      10.2 Scheduler與Master Elector

      Scheduler實(shí)現(xiàn)了基于Redis的高并發(fā)單次定時(shí)任務(wù)分發(fā)。具體選型見(jiàn)Scheduler章節(jié)。

      Master Elector基于redis setNx()與expire()兩個(gè)api實(shí)現(xiàn),與基于Zookeeper,Hazelcast實(shí)現(xiàn)的效果類(lèi)似。

      10.3 Showcase中的Demo

      計(jì)有Session,Counter,Scheduler 與 Master Elector四款。

      附錄

      附錄1: 對(duì)redis.conf默認(rèn)配置的修改

      daemonize no -> yes ,啟動(dòng)daemonize模式,注意如果用daemon工具啟動(dòng)redis-server時(shí)設(shè)回false。

      logfile stdout -> /var/log/redis/redis.log ,指定日志文件

      注釋掉RDB的所有觸發(fā)規(guī)則,在Master不保存RDB文件。

      dir ./ -> /var/data/redis,指定持久化文件及臨時(shí)文件目錄.

      maxmemory,設(shè)置為可用內(nèi)存/2.

      (可選)appendonly no->yes,打開(kāi)AOF文件.

      auto-aof-rewrite-percentage 100, 綜合考慮硬盤(pán)大小,可接受重啟加載延時(shí)等盡量的大,減少AOF rewrite頻率.

      auto-aof-rewrite-min-size 64mb,同上,起碼設(shè)為5G.

      client-output-buffer-limit slave 256mb 64mb 60. 考慮Traffic及Slave同步是RDB加載所需時(shí)間,正確設(shè)置避免buffer撐爆client被關(guān)掉后又要重新進(jìn)行全同步。

      安全配置,可選。

      設(shè)置RDB保存頻率,因?yàn)镽DB只作為Backup工具,只保留15分鐘的規(guī)則,設(shè)置為15分鐘保存一次就夠了save 900 1。

      (可選)slaveof 設(shè)置master地址,也可動(dòng)態(tài)設(shè)定。

      repl-timeout 60, 適當(dāng)加大比如120,避免master實(shí)際還沒(méi)倒掉就認(rèn)為master倒了。

      附錄2:版本變更歷史

      3.0.1版 2013-7-10,在微博發(fā)布后反應(yīng)良好,持續(xù)修改。

      3.0版 2013-6-29,在公司W(wǎng)orkshop后修訂,提高wiki的可讀性而不只是簡(jiǎn)單的記錄知識(shí)點(diǎn)。 ### 附錄3:其他參考資料

      Redis的幾個(gè)認(rèn)識(shí)誤區(qū)?by Tim yang。

      Redis

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:HTML簡(jiǎn)介與基本結(jié)構(gòu)
      下一篇:【最佳實(shí)踐】使用DLI將CSV數(shù)據(jù)轉(zhuǎn)換為Parquet數(shù)據(jù)
      相關(guān)文章
      欧洲 亚洲 国产图片综合| 亚洲AV无码XXX麻豆艾秋| 最新亚洲卡一卡二卡三新区| 亚洲精品私拍国产福利在线| 亚洲国产精品碰碰| 久久亚洲精品无码gv| 亚洲精华国产精华精华液好用| 日韩亚洲产在线观看| 亚洲午夜无码毛片av久久京东热| 国产亚洲精品bv在线观看| 亚洲精品人成网在线播放影院| 国产婷婷综合丁香亚洲欧洲| 亚洲首页国产精品丝袜| 久久久久久亚洲精品影院| 在线观看亚洲AV每日更新无码| 日本亚洲免费无线码 | 亚洲av午夜福利精品一区| 国产成人无码综合亚洲日韩| 久久亚洲国产精品一区二区| 亚洲AV无码久久精品狠狠爱浪潮 | 亚洲高清一区二区三区| 久久精品国产亚洲AV久 | 亚洲色WWW成人永久网址| 亚洲理论电影在线观看| 久久噜噜噜久久亚洲va久| 亚洲美女精品视频| 67194在线午夜亚洲| 亚洲色欲色欱wwW在线| heyzo亚洲精品日韩| 国产亚洲精品线观看动态图| 国产AV无码专区亚洲Av| 亚洲尹人香蕉网在线视颅| 亚洲午夜在线一区| 亚洲av乱码中文一区二区三区| 国产亚洲视频在线观看| 中文字幕第一页亚洲| 亚洲AV日韩AV天堂一区二区三区 | 亚洲日本在线观看视频| 亚洲精品亚洲人成人网| 99久久精品国产亚洲| 亚洲中文字幕无码一去台湾 |