http://www.searchdatabase.com.cn/showcontent_70423.htm html
開源項目SpringSide發起人肖樺(@江南白衣Calvin)在動手試用了Redis數據庫一週以後,總結了一些關於Redis的常識,在這裏分享給你們。 java
1. Overview mysql
1.1 資料 git
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