文章目錄
- 1. Schema
- 2. 容量及頻寬規劃
- 3. 效能規劃(QPS)
- 4. 可擴充性
- 小結
- 1. Redis是什麼
- 2. Redis不可能比Memcache快
- 3. 單台Redis的存放資料必須比實體記憶體小
- 4. Redis的VM實現是重複造輪子
- 5. 用get/set方式使用Redis
- 6. 使用aof代替snapshot
- 小結
Redis作者antirez是一個非常勤奮的開發人員,在Redis效能已經非常驚人的情況下持續不斷開發新的特性,比如從新的cluster原始碼看到,作者已經把Dynamo及Paxos一些核心的思想考慮進去並進行了一些簡潔的實現。相比其它產品如Memcached則幾年沒什麼大變化,在Web 2.0時代,Memcached已經非常不夠用,技術人員需要考慮做很多額外工作才能讓Memcached適應新的變化和需求。
antirez在1月5日Google Groups發表了一篇Redis diskstore文章,對Redis VM方式進行了反省,思考是否有更好的方式來大資料的Redis訪問。
a few months after VM started to work my feeling about it started to be not very good… that VM was not the way to go for the future of Redis
適合Web 2.0資料訪問最佳的方式就是完全基於記憶體,比如用Memcached或者Redis snapshot方式。但是更多的業務情境是資料規模會超過RAM容量,因此有幾種不同的設計模式。
1. VM方式。將資料分頁存放,由應用(如Redis)或者作業系統(如Varnish)將訪問量較少的頁即冷資料swap到磁碟上,訪問多的頁面由磁碟自動換出到記憶體中。應用實現VM缺點是代碼邏輯複雜,如果業務上冷熱資料邊界並不分明,則換入換出代價太高,系統整體效能低。不少搶鮮的網友在微博上也反饋過使用VM種種不穩定情況。作業系統實現VM的缺點前文Redis幾個認識誤區已經有介紹。
2. 磁碟方式,所有的資料讀寫訪問都是基於磁碟,由作業系統來只能的緩衝訪問的資料。由於現代作業系統都非常聰明,會將頻繁訪問的資料加入到記憶體中,因此應用並不需要過多特殊邏輯。MongoDB就是這種設計方式。這種方式也有一些已知的缺點,比如操作MMap寫入磁碟由作業系統控制,作業系統先寫哪裡後寫哪裡應用並不知情,如果寫入過程中發生了crash則資料一致性會存在問題。這個也是MongoDB飽受爭議的單機Durability問題,
MongoDB is not designed around single-server durability, but rather multi-server durability.
不過MongoDB自己並不覺得這是一個問題,他們的意見是,在目前時代有必要考慮單機完全可靠嗎?有必要嗎?
3. 硬碟儲存 + cache方式。實際原理和mysql+memcache方式類似,只不過將兩者功能合二為一到一個底層服務中,簡化了調用。
在上面幾種方式中,除去VM,antirez覺得MongoDB方式也不太適合,因此選擇了disktore方式來實現新的磁碟儲存,具體細節是
1) 讀操作,使用read through以及LRU方式。記憶體中不存在的資料從磁碟拉取並放入記憶體,記憶體中放不下的資料採用LRU淘汰。
2) 寫操作,採用另外spawn一個線程單獨處理,寫線程通常是非同步,當然也可以把cache-flush-delay配置設成0,Redis盡量保證即時寫入。但是在很多場合延遲寫會有更好的效能,比如一些計數器用Redis儲存,在短時間如果某個計數反覆被修改,Redis只需要將最終的結果寫入磁碟。這種做法作者叫per key persistence。由於寫入會按key合并,因此和snapshot還是有差異,disk store並不能保證時間一致性。
由於寫操作是單線程,即使cache-flush-delay設成0,多個client同時寫則需要排隊等待,如果隊列容量超過cache-max-memory Redis設計會進入等待狀態,造成調用方卡住。
Google Group上有熱心網友迅速完成了壓力測試,當記憶體用完之後,set每秒處理速度從25k下降到10k再到後來幾乎卡住。 雖然通過增加cache-flush-delay可以提高相同key重複寫入效能;通過增加cache-max-memory可以應對臨時峰值寫入。但是diskstore寫入瓶頸最終還是在IO。
3) rdb 和新 diskstore 格式關係
rdb是傳統Redis記憶體方式的儲存格式,diskstore是另外一種格式,那兩者關係如何?
- 通過BGSAVE可以隨時將diskstore格式另存新檔rdb格式,而且rdb格式還用於Redis複製以及不同儲存方式之間的中間格式。
- 通過工具可以將rdb格式轉換成diskstore格式。
當然,diskstore原理很美好,但是目前還處於alpha版本,也只是一個簡單demo,diskstore.c加上注釋只有300行,實現的方法就是將每個value作為一個獨立檔案儲存,檔案名稱是key的hash值。因此diskstore需要將來有一個更高效穩定的實現才能用於生產環境。但由於有清晰的介面設計,diskstore.c也很容易換成一種B-Tree的實現。很多開發人員也在積極探討使用bdb或者innodb來替換預設diskstore.c的可行性。
在Redis幾個認識誤區中也介紹過,Redis優勢是豐富的記憶體資料結構,這個特性和資料持久儲存天生是矛盾的,如用diskstore儲存大list/set(如熱門排行榜)效能會很差,每修改一個list元素則需要將整個大list重新儲存,開銷比使用傳統RDBMS高很多。
用MongoDB的一句設計哲學結尾
Databases are specializing – the “one size fits all” approach no longer applies.
Redis容量及使用規劃Wednesday, Jan 5th, 2011 by Tim |
14 Comments
Filed under: data | Tags: memcache, memcached, mysql, redis
在使用Redis過程中,我們發現了不少Redis不同於Memcached,也不同於MySQL的特徵。
(本文主要討論Redis未啟用VM支援情況)
1. Schema
MySQL: 需事先設計
Memcached: 無需設計
Redis: 小型系統可以不用,但是如果要合理的規劃及使用Redis,需要事先進行類似如下一些規劃
- 資料項目: value儲存的內容是什麼,如使用者資料
- Redis資料類型: 如String, List
- 資料大小: 如100位元組
- 記錄數: 如100萬條(決定是否需要拆分)
- ⋯⋯
上面的規劃就是一種schema,為什麼Redis在大型項目需要事先設計schema?因為Redis伺服器有容量限制,資料容量不能超出實體記憶體大小,同時考慮到業務資料的可擴充性,記錄數會持續增多、單條記錄的內容也都會增長,因此需要提前規劃好容量,資料架構師就是通過schema來判斷當前業務的Redis是否需要“分庫分表”以滿足可擴充需求。
2. 容量及頻寬規劃
容量規劃
MySQL: < 硬碟大小
Memcached: < RAM
Redis: < RAM
頻寬規劃
由於Redis比MySQL快10倍以上,因此頻寬也是需要事先規劃,避免頻寬跑滿而出現瓶頸。
3. 效能規劃(QPS)
當系統讀寫出現瓶頸,通常如何解決?
MySQL
寫: 拆分到多伺服器
讀: (1) 拆分 (2) 寫少也可以通過增加Slave來解決
Memcached
讀寫: 都通過hash拆分到更多節點。
Redis:
寫:拆分
讀: (1) 拆分 (2) 寫少也可以通過增加Slave來解決
4. 可擴充性
MySQL: 分庫分表
Memcached: hash分布
Redis:也可以分庫,也可以hash分布
小結
通過以上分析,Redis在很多方面同時具備MySQL及Memcached使用特徵,在某些方面則更像MySQL。
由於Redis資料不能超過記憶體大小,一方面需要進行事先容量規劃,保證容量足夠;另外一方面設計上需要防止資料規模無限制增加,進而導致Redis不可擴充。
Redis需要象MySQL一樣預先設計好拆分方案。
小問題
在MySQL中,通過預先建立多表或者庫可以在業務增長時候將這些表或庫一分為二部署到更多伺服器上。
在Redis中,“分庫分表”應當如何??有什麼好的設計模式?
Redis幾個認識誤區Saturday, Dec 4th, 2010 by Tim |
42 Comments
Filed under: data | Tags: key value store, redis
前幾天微博發生了一起大的系統故障,很多技術的朋友都比較關心,其中的原因不會超出James Hamilton在On Designing and Deploying Internet-Scale Service(1)概括的那幾個範圍,James第一條經驗“Design for failure”是所有互連網架構成功的一個關鍵。互連網系統的工程理論其實非常簡單,James paper中內容幾乎稱不上理論,而是多條實踐經驗分享,每個公司對這些經驗的理解及執行力決定了架構成敗。
題外話說完,最近又研究了Redis。去年曾做過一個MemcacheDB, Tokyo Tyrant, Redis performance test,到目前為止,這個benchmark結果依然有效。這1年我們經曆了很多眼花繚亂的key value儲存產品的誘惑,從Cassandra的淡出(Twitter暫停在主業務使用)到HBase的興起(Facebook新的郵箱業務選用HBase(2)),當再回頭再去看Redis,發現這個只有1萬多行原始碼的程式充滿了神奇及大量未經挖掘的特性。Redis效能驚人,國內前十大網站的子產品估計用1台Redis就可以滿足儲存及Cache的需求。除了效能印象之外,業界其實普遍對Redis的認識存在一定誤區。本文提出一些觀點供大家探討。
1. Redis是什麼
這個問題的結果影響了我們怎麼用Redis。如果你認為Redis是一個key value store, 那可能會用它來代替MySQL;如果認為它是一個可以持久化的cache, 可能只是它儲存一些頻繁訪問的臨時資料。Redis是REmote DIctionary Server的縮寫,在Redis在官方網站的的副標題是A persistent key-value database with built-in net interface written in ANSI-C for Posix systems,這個定義偏向key value store。還有一些看法則認為Redis是一個memory database,因為它的高效能都是基於記憶體操作的基礎。另外一些人則認為Redis是一個data structure server,因為Redis支援複雜的資料特性,比如List, Set等。對Redis的作用的不同解讀決定了你對Redis的使用方式。
互連網資料目前基本使用兩種方式來儲存,關聯式資料庫或者key value。但是這些互連網業務本身並不屬於這兩種資料類型,比如使用者在社會化平台中的關係,它是一個list,如果要用關聯式資料庫儲存就需要轉換成一種多行記錄的形式,這種形式存在很多冗餘資料,每一行需要儲存一些重複資訊。如果用key value儲存則修改和刪除比較麻煩,需要將全部資料讀出再寫入。Redis在記憶體中設計了各種資料類型,讓業務能夠高速原子的訪問這些資料結構,並且不需要關心持久儲存的問題,從架構上解決了前面兩種儲存需要走一些彎路的問題。
2. Redis不可能比Memcache快
很多開發人員都認為Redis不可能比Memcached快,Memcached完全基於記憶體,而Redis具有持久化儲存特性,即使是非同步,Redis也不可能比Memcached快。但是測試結果基本是Redis占絕對優勢。一直在思考這個原因,目前想到的原因有這幾方面。
- Libevent。和Memcached不同,Redis並沒有選擇libevent。Libevent為了迎合通用性造成代碼龐大(目前Redis代碼還不到libevent的1/3)及犧牲了在特定平台的不少效能。Redis用libevent中兩個檔案修改實現了自己的epoll event loop(4)。業界不少開發人員也建議Redis使用另外一個libevent高效能替代libev,但是作者還是堅持Redis應該小巧並去依賴的思路。一個印象深刻的細節是編譯Redis之前並不需要執行./configure。
- CAS問題。CAS是Memcached中比較方便的一種防止競爭修改資源的方法。CAS實現需要為每個cache key設定一個隱藏的cas token,cas相當value版本號碼,每次set會token需要遞增,因此帶來CPU和記憶體的雙重開銷,雖然這些開銷很小,但是到單機10G+ cache以及QPS上萬之後這些開銷就會給雙方相對帶來一些細微效能差別(5)。
3. 單台Redis的存放資料必須比實體記憶體小
Redis的資料全部放在記憶體帶來了高速的效能,但是也帶來一些不合理之處。比如一個中型網站有100萬註冊使用者,如果這些資料要用Redis來儲存,記憶體的容量必須能夠容納這100萬使用者。但是業務實際情況是100萬使用者只有5萬活躍使用者,1周來訪問過1次的也只有15萬使用者,因此全部100萬使用者的資料都放在記憶體有不合理之處,RAM需要為冷資料買單。
這跟作業系統非常相似,作業系統所有應用訪問的資料都在記憶體,但是如果實體記憶體容納不下新的資料,作業系統會智能將部分長期沒有訪問的資料交換到磁碟,為新的應用留出空間。現代作業系統給應用提供的並不是實體記憶體,而是虛擬記憶體(Virtual Memory)的概念。
基於相同的考慮,Redis 2.0也增加了VM特性。讓Redis資料容量突破了實體記憶體的限制。並實現了資料冷熱分離。
4. Redis的VM實現是重複造輪子
Redis的VM依照之前的epoll實現思路依舊是自己實現。但是在前面作業系統的介紹提到OS也可以自動幫程式實現冷熱資料分離,Redis只需要OS申請一塊大記憶體,OS會自動將熱資料放入實體記憶體,冷資料交換到硬碟,另外一個知名的“理解了現代作業系統(3)”的Varnish就是這樣實現,也取得了非常成功的效果。
作者antirez在解釋為什麼要自己實現VM中提到幾個原因(6)。主要OS的VM換入換出是基於Page概念,比如OS VM1個Page是4K, 4K中只要還有一個元素即使只有1個位元組被訪問,這個頁也不會被SWAP, 換入也同樣道理,讀到一個位元組可能會換入4K無用的記憶體。而Redis自己實現則可以達到控制換入的粒度。另外訪問作業系統SWAP記憶體地區時block進程,也是導致Redis要自己實現VM原因之一。
5. 用get/set方式使用Redis
作為一個key value存在,很多開發人員自然的使用set/get方式來使用Redis,實際上這並不是最佳化的使用方法。尤其在未啟用VM情況下,Redis全部資料需要放入記憶體,節約記憶體尤其重要。
假如一個key-value單元需要最小佔用512位元組,即使只存一個位元組也佔了512位元組。這時候就有一個設計模式,可以把key複用,幾個key-value放入一個key中,value再作為一個set存入,這樣同樣512位元組就會存放10-100倍的容量。
這就是為了節約記憶體,建議使用hashset而不是set/get的方式來使用Redis,詳細方法見參考文獻(7)。
6. 使用aof代替snapshot
Redis有兩種儲存方式,預設是snapshot方式,實現方法是定時將記憶體的快照(snapshot)持久化到硬碟,這種方法缺點是持久化之後如果出現crash則會丟失一段資料。因此在完美主義者的推動下作者增加了aof方式。aof即append only mode,在寫入記憶體資料的同時將操作命令儲存到記錄檔,在一個並發更改上萬的系統中,命令日誌是一個非常龐大的資料,管理維護成本非常高,恢複重建時間會非常長,這樣導致失去aof高可用性本意。另外更重要的是Redis是一個記憶體資料結構模型,所有的優勢都是建立在對記憶體複雜資料結構高效的原子操作上,這樣就看出aof是一個非常不協調的部分。
其實aof目的主要是資料可靠性及高可用性,在Redis中有另外一種方法來達到目的:Replication。由於Redis的高效能,複製基本沒有延遲。這樣達到了防止單點故障及實現了高可用。
小結
要想成功使用一種產品,我們需要深入瞭解它的特性。Redis效能突出,如果能夠熟練的駕馭,對國內很多大型應用具有很大協助。希望更多同行加入到Redis使用及代碼研究行列。
參考文獻
- On Designing and Deploying Internet-Scale Service(PDF)
- Facebook’s New Real-Time Messaging System: HBase To Store 135+ Billion Messages A Month
- What’s wrong with 1975 programming
- Linux epoll is now supported(Google Groups)
- CAS and why I don’t want to add it to Redis(Google Groups)
- Plans for Virtual Memory(Google Groups)
- Full of keys(Salvatore antirez Sanfilippo)
from: http://timyang.net/tag/redis/