Nov 21, 2011

Memcached vs Redis vs MongoDB vs MySQL 效能比較

"記憶體是新一代的硬碟"在高流量網站內已是稀鬆平常的事情,像是採用 Memcached 做分散式快取服務即是典型的應用模式,Memcached 使用概念簡單且相關應用情境、文件、工具、演算法都已相當成熟,許多知名網站都運用 Memcached 提升scalability,像是GAE、Heroku 等PaaS 環境也有提供 Memcached 的收費服務,這些市場訊息已說明大規模的使用記憶體是可以合乎成本效益的。

Redis 是近年隨著 NoSQL 熱潮出現的 Key-Value store,其功能涵蓋了 Memcached 並且提供許多高階功能,在許多應用情境下相較 Memcached,能降低開發與營運複雜度,比如:
  • 豐富的資料結構(strings, hashes, lists, sets and sorted sets) 更易於設計適當的資料粒度增進資料重用的機會,且可依不同的資料結構特性在redis server上做組合與計算,降低開發上的複雜度與資料傳輸
  • 永續保存功能可充當簡易的資料庫降低重覆資料的一致性問題或一些特殊應用,像是當系統當機後能快速回復快取資料,避免大量重建的衝擊
  • 資料覆寫功能 提升讀取效能,提升 availability
  • 2.6可以寫script (lua)
  • 3.0版本預期還會加上Cluster做HA與Balancing有些data grid的樣子

但效能是否能與 Memcached 匹敵呢?以下透過大量的 get/set 操作做簡單的比較,並且加入MySQL與MonogoDB 做為參考




一、測試環境

Hardware Overview:

  Model Name: MacBook Pro
  Model Identifier: MacBookPro8,2
  Processor Name: Intel Core i7
  Processor Speed: 2 GHz
  Number of Processors: 1
  Total Number of Cores: 4
  L2 Cache (per Core): 256 KB
  L3 Cache: 6 MB
  Memory: 8 GB
  HDD: 500G(5400RPM)

System Software Overview:

  System Version: Mac OS X 10.7.2 (11C74)
  Kernel Version: Darwin 11.2.0
  64-bit Kernel and Extensions: Yes

Software Overview:
  Java: "1.6.0_29" HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)

  Memcached Version: 1.4.5
  Memcached Client: xmemcache 1.3.5

  Redis Version: 2.4.2
  Redis Client: jedis 2.0

  MongoDB Version: mongodb-osx-x86_64-2.0.0
  MongoDb Client: mongo-java-driver 2.7.2

  MySQL Version: 5.5.11-log Community Server
  MySQL Client: mysql-connector-java 5.1.6 + tomcat-jdbc 7.0.22 (connection-pool)
  
環境設定:
Server
  ulimit -n 10000

Client
  java -server -Xms512m -Xmx512m


二、測試方法


  • 每組測式含三種方法 Set/Get/Set-Get(50/50),各別產生500,000個請求
  • 不同的壓力測試Thread(50/100/150/200) * 資料量(64/128/256/512/1024) bytes
  • 壓力測試工具 https://github.com/parkghost/NoSQLBenchmark


三、測試案例

1.Memcached 
memcached -m 2048

2.Redis(without persistence)
disable RDB and AOF

3.Redis(RDB)
save 900 1
save 300 10
save 60 10000
rdbcompression yes

4.Redis(AOF - no)
appendonly yes
appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

5.Redis(AOF - every second)
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

6.Redis(AOF - always)
appendonly yes
appendfsync always
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb


7.MongoDB
8.MySQL - MyISAM
max_connections = 300
table_open_cache = 400
thread_cache_size = 4


9.MySQL - Innodb(innodb_flush_log_at_trx_commit=0)
max_connections = 300
table_open_cache = 400
thread_cache_size = 4
innodb_log_file_size = 512m
innodb_flush_log_at_trx_commit = 0
innodb_buffer_pool_size = 2g
innodb_log_buffer_size = 16m


10.MySQL - Innodb(innodb_flush_log_at_trx_commit=1)
max_connections = 300
table_open_cache = 400
thread_cache_size = 4

innodb_log_file_size = 512m
innodb_flush_log_at_trx_commit = 1
innodb_buffer_pool_size = 2g
innodb_log_buffer_size = 16m



11.MySQL - Innodb(innodb_flush_log_at_trx_commit=2)
max_connections = 300
table_open_cache = 400
thread_cache_size = 4

innodb_log_file_size = 512m
innodb_flush_log_at_trx_commit = 2
innodb_buffer_pool_size = 2g
innodb_log_buffer_size = 16m


四、測試結果


完整測試結果可從Github下載,以下僅顯示壓力在 200 threads 與資料大小在 1024 bytes 的比較圖表

1.Memcached 

xmemcached(client library) 內部會將多組 get 操作換成 mget 來優化傳送指令數量

2.Redis(without persistence)
3.Redis(RDB)
4.Redis(AOF - no)




AOF寫入效能依數據量大量增加,效能降幅較大

5.Redis(AOF - every second)
6.Redis(AOF - always)

磁碟寫入 70M/s 的時候,開始不能寫入資料,停頓一陣子又能繼續運作,但已發生資料寫入失敗,所以沒通過測試

[8112] 23 Nov 20:43:03 * Background AOF rewrite successful
[8112] 23 Nov 20:43:03 * 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.
[8112] 23 Nov 20:43:18 * Starting automatic rewriting of AOF on 101% growth
[8112] 23 Nov 20:43:18 * Background append only file rewriting started by pid 8253
[8253] 23 Nov 20:43:32 * SYNC append only file rewrite performed
[8112] 23 Nov 20:43:32 * Background AOF rewrite terminated with success
[8112] 23 Nov 20:43:35 * Parent diff successfully flushed to the rewritten AOF (239673418 bytes)
[8112] 23 Nov 20:43:35 * Background AOF rewrite successful






7.MongoDB


同時讀取與寫入的 throughput 降到低點


8.MySQL - MyISAM
9.MySQL - Innodb(innodb_flush_log_at_trx_commit=0)
10.MySQL - Innodb(innodb_flush_log_at_trx_commit=1)
11.MySQL - Innodb(innodb_flush_log_at_trx_commit=2)



MyISAM表現還算平穩,但 Innodb 在讀資料的反應很奇怪

匯總比較




五、結論

  • Memcached 吞吐量約為 Redis 約1.3倍
  • Memcached 979M 記憶體用量比Redis(RDB) 960.43M 多了一些些
  • Memcached 的client library較Redis先進
  • Redis(AOF) 寫入的TPS比Redis(RDB)少了約55%
  • Redis(AOF) 記憶體用量比Redis(RDB)多很多
  • Redis(AOF) 的檔案很大,重寫後較同資料量的RDB檔案大一些
  • Redis 在 bgsave/bgrewriteaof 會fork process來寫檔案會使用到兩倍原有的記憶體大小
  • 以get/set來看MongoDB的效能跟MySQL MyIsam 接近
  • MongoDB 在同時讀寫時TPS下降非常多
  • mongo-java-driver GC佔掉整體時間的1.85%,創建過多物件(可能是為了asynchronous write 的實作)

  • MySQL Innodb 的 select 結果不知道是怎麼回事,飄移很大
Memcached 的Get/Set 操作吞吐量較 Redis 高,但實際應用上Redis在伺服器與應用端還有許多優化策略(e.g. rich data structure、scripting、自動化的指令選擇),理論上能大幅提升效能,若再結合其它特性還能降低開發維護的複雜度。


做這份測試,前前後後因為效能與測試方法共實作了三個版本
第一版用Python來開發,找到的連線模組都不確定是否穩定,內建Threading模組效能差,測不出資料庫效能,像Memcached的吞吐量不到20k,其它資料庫的測試結果也不好

第二版用JMeter 與 java sampler,在設置與執行這些參數化測試案例有些麻煩,在GUI即時顯示數據也影響了效能

第三版用 Java 來開發,整體表現比前二版優秀,得到的測試數據能夠比較出各資料庫的效能差異

No comments: