關於Redis的一些常識

http://www.searchdatabase.com.cn/showcontent_70423.htm html

開源項目SpringSide發起人肖樺(@江南白衣Calvin)在動手試用了Redis數據庫一週以後,總結了一些關於Redis的常識,在這裏分享給你們。 java

  1. Overview mysql

  1.1 資料 git

關於Redis的一些常識

  1.2 優缺點 github

  • 很是很是的快, 有測評說比Memcached還快。
  • 豐富的數據結構,超越了通常的Key-Value數據庫,組合使用各類結構,限制Redis用途的只會是你本身的想象力, Redis在互聯網上的11種常見用例
  • 由於是我的做品,Redis的代碼量也就1萬行左右,大多選擇了比較Simple的作法,使得普通公司而不是文藝公司也能夠吃透它。
  • 做者認爲Redis 2.6版已經成熟,2013年的精力會放在HA和Scalable上,也就是Redis-Sentinel和Redis-Cluster了。

  1.3 八卦 web

  • 做者是意大利的Salvatore Sanfilippo(antirez),又是VMWare大善人聘請了他專心寫Redis。
  • antirez和我同樣不喜歡搞什麼諮詢服務,因此Commercial Support是不會有了。
  • 默認端口6379,是手機按鍵上MERZ對應的號碼,意大利歌女Alessia Merz是antirez和朋友們認爲愚蠢的代名詞。

  2. 數據結構 redis

  2.1 Key 算法

  • Key 能夠是任意類型,最後都存成byte[]
  • Key 不能太長,好比1024字節,但做者也不追求過短,要表達清楚意思纔好,做者建議用":"分隔表名,用"."做爲單詞間的鏈接。
  • KEYS顯示全部的key,支持通配符 "KEYS a*" , "keys a?c",但不建議在生產環境大數據量下使用。
  • SORT,對集合按數字或字母順序排序後返回,或者存到另外一個List,還能夠關聯到外部Key等。由於會耗用CPU,有時會安排到slave上執行。
  • EXPIRE/EXPREAT/PERSIST/TTL/,關於Key超時的操做,默認以秒爲單位,也有p字頭的以毫秒爲單位的版本。
  • 其餘命令: EXISTS,DEL,RENAME/RENAMENX(僅當new key不存在時),MOVE/MIGRATE(實例內今後db到彼db/今後實例到彼實例),RANDOMKEY,TYPE/Object(Key的類型/對象編碼類型,空置時間),DUMP/RESTORE(value值的持久化)

  2.2 String spring

  最普通的key-value,除了支持最基本的get/set, Redis也很喜歡添加一些簡便的指令,在服務端作起來是舉手之勞,客戶端便方便不少。 sql

  • incr/decr/incrby/incrbyfloat, 若是key還不存在時建立key並設原值爲0。
  • setEx/pSetEx, Set + Expire 的簡便寫法,p字頭以毫秒爲單位。
  • setNx, key不存在時才put進去。
  • getset, 設置新值,返回舊值。
  • mget/mset/msetex, 一次get/set多個key。
  • getbit/setbit/bitop/bitcount bitmap玩法,好比統計今天的訪問用戶,每一個用戶有一個offset,今天進來的話就把那個位爲1。
  • append/setrange/getrange,只對特定的數據格式好比字段定長的有用,json格式就沒用。

  2.3 Hash

  Key-HashMap結構,相比2.2中的JSON格式Value,能夠只讀取/更新對象的某些屬性,有些屬性超長就讓它一邊呆着不動。。

  另外一個用法是用來建索引。好比User對象,除了id有時還要按name來查詢,能夠建一個Key爲user:index:name的Hash,在插入User對象時(set user:101 {"id":101,"name":"calvin"}), 順便往這個hash插入一條(hset user:index:name calvin 101),這時calvin做爲hash裏的一個key,值爲101。按name查詢的時候,用hget user:index:name calvin 就能從名爲calvin的key裏取出id。

  2.4 List

  Redis裏能夠當雙向鏈表來用,還提供blocking版本的pop函數,能夠當Message Queue來用。

  不過List並無JMS的ack機制,若是消費者把job給Pop走了又沒處理完就死機了怎麼辦? 解決方法之一是加多一個sorted set,以分發時間爲score,用戶把job作完了以後要去消掉它。

  除了List標準的雙向POP/PUSH外,還支持對隊列內容的直接操做,好比LREM/LSET/LINSERT/LINDEX

  另外常常用LTRIM限制List的大小,好比只保留最新的20條消息。LRANGE不一樣於POP直接彈走元素,只是返回列表內一段下標的元素。LLEN獲取列表的長度。

  2.5 Set

  Set就是Set,還提供一些交集,並集,差集的集合操做。

  2.6 Sorted Set

  有序集,元素放入集合的時候要同時提供該元素的分數。

  2.7 事務

  用Multi/Exec/Discard實現, 隔離級別是這邊事務一天不提交,那邊另外一個事務仍是看到舊的值。 還有個Watch指令,起到CAS的效果,若是事務提交時,Key的值已被別的事務改變,事務會被打斷。

  2.8 Lua Script

  Redis2.6內置的Lua Script支持,能夠在Redis的Server端一次過運行大量邏輯。

  • 整個Script默認是在一個事務裏的。
  • Script裏涉及的全部Key儘可能用變量,從外面傳入,使Redis一開始就知道你要改變哪些key。
  • EVAL每次傳輸一整段Script比較費帶寬,能夠先用SCRIPT LOAD載入script,返回哈希值。而後用EVALHASH執行。
  • 內置的LUA庫裏還很貼心的帶了CJSON,能夠處理JSON字符串。

  3. 性能

  • 速度太快了,用光了帶寬也測不出極限。若是是本地socket直連,incr能夠去到很嚇人的幾十萬TPS。
  • 普通的get/set操做,通過了LAN,延時也只有1毫秒左右,能夠放心使用,不用像調用REST接口和訪問數據庫那樣,多一次外部訪問都心痛。
  • 自帶的redis-benchmark默認只是基於一個很小的數據集進行測試,但可調整命令行參數如 redis-benchmark -r 10000000 -n 10000000 -d 128 -t SET,GET 就能夠默認開50條線程,SET 6M條左右(random)key是21字節長,value是128字節長的數據進去, 再GET出來。
  • 若是要一次發送多條指令,PipeLining模式能讓性能更快,由於它在設計上正視了網絡往返的時間。
  • 更快的是Lua Script模式,還能夠包含邏輯,直接在服務端又get又set的 (見2.8 Lua Script)。
  • 單線程單CPU架構,但做者認爲CPU不是瓶頸,內存與網絡帶寬纔是。
  • 發現執行緩慢的命令,可配置執行超過多少時間的指令算是緩慢指令(默認10毫秒,不含IO時間),能夠用slowlog get 指令查看(默認只保留最後的128條)。單線程的模型下,某個請求佔掉10毫秒是件大事情。

  4. 容量

  最大內存:

  • 必定要設置最大內存,不然物理內存用爆了就會大量使用Swap,寫RDB文件時的速度慢得你想死。
  • 多留一倍內存是最安全的。重寫AOF文件和RDB文件的進程(即便不作持久化,複製到Slave的時候也要寫RDB)會fork出一條新進程來,採用了操做系統的Copy-On-Write策略(若是父進程的內存沒被修改,子進程與父進程共享Page。若是父進程的Page被修改, 會複製一份改動前的內容給新進程),留意Console打出來的報告,如"RDB: 1215 MB of memory used by copy-on-write"。在系統極度繁忙時,若是父進程的全部Page在子進程寫RDB過程當中都被修改過了,就須要兩倍內存。
  • 按照Redis啓動時的提醒,設置 vm.overcommit_memory = 1 ,使得fork()一條10G的進程時,由於COW策略而不必定須要有10G的free memory.
  • 當最大內存到達時,按照配置的Policy進行處理, 默認policy爲volatile-lru, 對設置了expire time的key進行LRU清除(不是按實際expire time)。若是沒有數據設置了expire time或者policy爲noeviction,則直接報錯,但此時系統仍支持get之類的讀操做。 另外還有幾種policy,好比volatile-ttl按最接近expire time的,allkeys-lru對全部key都作LRU。
  • 原來2.0版的VM(將Value放到磁盤,Key仍然放在內存),2.4版後又不支持了。

  內存佔用:

  • 測試代表,stirng類型須要90字節的額外代價,就是說key1個字節,value一個字節時,仍是須要佔用92字節的長度,而上述的benchmark的記錄就佔用了239個字節。
  • 用make 32bit能夠編譯出32位的版本,每一個指針佔用的內存更小,但只支持最大4GB內存。

  Sharding:

  • Jedis支持在客戶端作分區,侷限是不能動態re-sharding, 有分區的master倒了,必須用slave頂上。要增長分區的話,呃.....
  • Redis-Cluster是今年工做重點,支持automatic re-sharding, 採用和Hazelcast相似的算法,總共有N個分區,每臺Server負責若干個分區。客戶端先hash出key 屬於哪一個分區,而後發給負責這個分區的Server。Re-sharding時,會將某些分區的數據移到新的Server上,而後各Server周知分區<->Server映射的變化,由於分區數量有限,因此通信量不大。 在遷移過程當中,原server對於已經遷移走的數據的get請求,會回一個臨時轉向的應答。

  5. 高可用性

  5.1 持久化

  綜述: 解密Redis持久化(中文歸納版)英文原版

  RDB文件:

  • 整個內存的壓縮過的Snapshot,RDB的數據結構, 能夠配置寫Snapshot的複合觸發條件,默認是60秒內改了1萬次或300秒內改了10次或900秒內改了1次。
  • RDB在寫入過程當中,會連內存一塊兒Fork出一個新進程,遍歷新進程內存中的數據寫RDB。
  • 先寫到臨時文件再Rename,這樣外部程序對RDB文件的備份和傳輸過程是安全的。並且即便寫新快照的過程當中Server被強制關掉了,舊的RDB文件還在。
  • 可配置是否進行壓縮,方法是是字符串的LZF算法 和String形式的數字變回int形式存儲。

  AOF文件:

  • append only的操做日誌,等於mysql的binlog,記錄全部有效的寫操做,格式是明文的Redis協議的純文本文件。
  • 通常配置成每秒調用一次fsync將數據寫到磁盤,壞處是操做系統非正常關機時,可能會丟1秒的數據。 若是設爲fsync always,性能只剩幾百TPS,不用考慮。
  • 若是使用了AOF,重啓時只會從AOF文件載入數據,不會管RDB文件。
  • AOF文件過大時,會fork出一條新進程來將文件重寫(也是先寫臨時文件再rename), 遍歷新進程的內存中數據,每條記錄有一條的Set語句。默認配置是當AOF文件大小是上次rewrite後的大小的一倍時觸發。
  • Redis協議的內容,如set mykey hello, 將持久化成*3 $3 set $5 mykey $5 hello, 第一個數字表明這條語句有多少元,其餘的數字表明後面字符串的長度。這樣的設計,使得即便在寫文件過程當中忽然關機致使文件不完整,也能自我修復,執行redis-check-aof便可。

    RDB不會實時寫入數據,並且若是同時使用二者,但服務器重啓只會找AOF文件。那要不要只使用AOF呢?做者建議不要,由於RDB更適合用於備份數據庫,快速重啓,並且不會有AOF可能潛在的bug,留着做爲一個萬一的手段。

  讀寫性能:

  • AOF重寫和RDB寫入都是在fork出進程後,遍歷新進程內存順序寫的,既不影響主進程,順序寫的速度也比隨機寫快,在普通PC服務器上把剛纔的1.5G數據寫成一個200M的RDB文件大約8秒, 啓動時載入一個1.4G的AOF文件大約13秒。
  • 2.4版之後,lpush能夠一次push多個值了,使得AOF重寫時能夠將舊版本中的多個lpush指令合成一個。
  • 有人建議設置no-appendfsync-on-rewrite 爲 yes,aof rewrite時就不執行fsync了,先都存在內存裏,減小IO資源爭用。 固然這樣會丟數據。
  • Fork一個使用了大量內存的進程也要時間,大約10ms per GB的樣子,各類系統的對比

  其餘:

  • 正確關閉服務器:redis-cli shutdown 或者 kill,都會graceful shutdown,保證寫RDB文件以及將AOF文件fsync到磁盤,不會丟失數據。 若是是Ctrl+C,或者kill -9 就會丟失數據。
  • 執行指令bgsave 可觸發rdb存盤,bgrewriteaof可觸發aof重寫。

  5.2 Master-Slave複製

  • 能夠在配置文件、命令行參數、以及執行SLAVEOF指令的來設置。
  • 當前版本,一旦執行SlaveOF, slave會清掉本身的全部數據,執行一次全同步:Master要bgsave出本身的一個RDB文件,發給Slave。而後再增量同步: Master做爲一個普通的client連入slave,將全部寫操做轉發給slave,沒有特殊的同步協議。
  • 做者在2.8版本中將支持PSYNC部分同步
  • 測試代表同步延時很是小。
  • 有建議只在Slave上寫RDB和AOF文件,但這樣master啓動時就須要從slave copy文件,fail-over腳本也更復雜。只要有足夠內存,master平時IO也不高的話,仍是簡化架構更好。

  5.3 Fail-Over

  5.3.1 概述

  Redis-sentinel是2.6版開始加入的另外一組獨立運行的節點, 提供自動Fail Over的支持。

  • 每秒鐘對全部master,slave和其餘sentinel執行ping,redis-server節點要應答+PONG或-LOADING或-MASTERDOWN.
  • 若是某一臺Sentinel沒有在30秒內(可配置得短一些哦)收到上述正確應答,它就會認爲master處於sdown狀態(主觀Down)
  • 它向其餘sentinel詢問是否也認爲master倒了(SENTINEL is-master-down-by-addr ), 若是quonum臺(默認是2)sentinels在5秒鐘內都這樣認爲,就會認爲master真是odown了(客觀Down)。
  • 此時會選出一臺sentinel做爲Leader執行fail-over, Leader會從slave中選出一個提高爲master(執行slaveof none),這臺slave必須狀態正常,並且INFO顯示它與master的複製鏈接並無斷開過久。而後讓其餘slave指向它(執行slaveof new master)。

    5.3.2 master/slave 及 其餘sentinels的發現

  master地址在sentinel的配置文件裏, sentinel會每10秒一次向master發送INFO,知道master的slave有哪些。 若是master已經變爲slave,sentiel會分析INFO的應答指向新的master。因此當sentiel重啓時,它的配置文件裏master的地址並沒變,那它仍然會去找old master,而後被redirect到新的master。但若是old master還沒起來,或者old master沒把本身變成slave,悲劇就可能發生。

  另外,sentinel會在master上建一個pub/sub channel,通告各類信息,好比+sdown/-sdown, 並且sentinel們也是經過接收pub/sub channel上的+sentinel的信息發現彼此,由於每臺sentinel每5秒會發送一次__sentinel__:hello,宣告本身的存在。

  5.3.3 自定義腳本

  • sentinel在failover時還會執行配置文件裏指定的用戶reconfig腳本,讓master變爲slave並指向新的master。
  • 腳本在以下時機被調用: 1. slave開始提高成master,2.全部slave都已指向新master,3.各類緣由提高被終止。
  • 腳本的將會在命令行按順序傳入以下參數: <master-name> <role(leader/observer)> <state(上述三種狀況)> <from-ip> <from-port> <to-ip> <to-port>
  • 腳本返回0是正常,若是返回1會被從新執行,若是返回2或以上不會。 若是超過60秒沒返回會被強制終止。

  另外一種notify腳本在收到任何pub/sub信息時都會調用,讓你去通知O&M系統。

  5.3.4 Client集成

  client中執行語句SENTINEL get-master-addr-by-name mymaster 可得到當前master的地址。 可是Jedis還沒集成sentinel,只有一個熱心網友提交了pull request

  淘寶的Tedis driver,使用了徹底不一樣的思路,不基於Sentinel,而是多寫隨機讀,學術名詞是ReadOne-WriteAll-tx(see NoSQL數據庫的分佈式算法), 一開始就同步寫入到全部節點,讀的話隨便讀一個還活着的節點就好了。(但節點死掉從新起來後怎麼從新同步?何時能夠從新做爲一個可選的master?)

  Redis做者也在博客裏抱怨怎麼沒有人作Dynamo-style 的client

  5.4 Geographic-Redundancy

  一個方法是用rsync等工具同步RDB文件,但RDB文件是很是不實時的。

  若是要求更高,能夠本身寫程序讀取AOF文件或MONITOR,把指令轉發給遠處的站點,反正裏頭的內容就是Redis協議。

  6. 維護

  配置

  能夠在配置文件中編寫, 也能夠在啓動時的命令行配置, 如redis-server --port 7777 --slaveof 127.0.0.1 8888,還能夠在cli中執行CONFIG GET, 能夠達到不重啓服務的修改配置參數的效果。

  監控

  Redis監控技巧 有詳細的介紹。

  INFO指令將返回很是豐富的信息。

  • 着重監控檢查內存使用,好比是否已接近上限,好比有無大量使用swap(used_memory - used_memory_rss)
  • 還有AOF與RDB文件的保存狀況,master-slave的關係也要重點監控。
  • STAT中信息還包括key命中率,全部命令的執行次數,全部client鏈接數量等,CONFIG RESETSTAT 可重置。

  其餘

  • SlowLog 檢查慢操做(見2.性能)
  • 配置sentinel的notify.sh腳本對全部事件告警或本身用PING/INFO監控節點狀態(見5.3.3)
  • MONITOR能夠顯示Server收到的全部指令,能夠用於debug。
  • Redis live, 基於Python的DashBoard,使用INFO和MONITOR得到系統狀況和指令統計分析。
  • Instagram的Redis Faina,基於Python,使用MONITOR對指令進行統計分析.
  • Redis-rdb-tools 基於Python,能夠分析RDB文件,好比每條Key對應value所佔的大小,還能夠將RDB dump成文本文件而後進行比較,還能夠將RDB輸出成JSON格式。
  • Redis做者本身寫的Redis Sampler,基於Ruby,統計數據分佈狀況。

  維護

  • 用redis-check-aof/redis-check-dump 修復爛掉的文件。
  • 在系統閒時調用 bgrewriteaof 重寫AOF文件。

  7. 其餘

  過時數據清除 ,過時數據的清除歷來不容易,Redis使用了一種相對務實的作法

  • 當client主動get的時候會先進行判斷。
  • 若是clien永遠都再也不get那條key呢? 它會在後臺,每秒10次的執行以下操做: 隨機選取100個key校驗是否過時,若是有25個以上的key過時了,馬上隨機選取下100個key,可見若是數據不被主動get,它何時最終被清理掉是未知的。
  • 上述主動清理只會在master進行,slave們會收到從master發過來的DEL指令,master的AOF文件也會插入一條DEL。

  Redis如何處理客戶端鏈接

  8. Java Driver

  各個Driver好像只有Jedis比較活躍。

  不須要Spring Data Redis的封裝,由於Jedis已足夠簡單,因此它沒有像對MongoDB java driver的封裝那樣能簡化代碼,所謂屏蔽各類底層driver的差別並不太吸引人,由於我就沒打算選其餘幾種driver。

  Jedis基於Apache Commons Pool作的鏈接池,默認最大鏈接數只有8,通常須要從新設置。

  9. Windows的版本

  Windows版本方便單機調測,但Redis並無提供,好在微軟提供了一個,暫時基於Redis 2.4版本。 https://github.com/MSOpenTech/redis 由於github如今已經沒有Download服務了,因此編譯好的可執行文件藏在這裏 https://github.com/MSOpenTech/redis/tree/2.4/msvs/bin/release

原文出處:https://github.com/springside/springside4/wiki/redis
相關文章
相關標籤/搜索