《Redis官方教程》-基準測試

ali清英發表於2016-04-01

原文連線  譯者:looyup

Redis有多快?

Redis 包含了工具程式redis-benchmark,它可以模擬執行命令,相當於模擬N個客戶端同時傳送總數M個查詢(和apache的ab工具程式類似)。下面是在linux系統上執行benchemark後的完整輸出,支援的選項如下:

Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]

-h <hostname> Server hostname (default 127.0.0.1)
-p <port> Server port (default 6379)
-s <socket> Server socket (overrides host and port)
-a <password> Password for Redis Auth
-c <clients> Number of parallel connections (default 50)
-n <requests> Total number of requests (default 100000)
-d <size> Data size of SET/GET value in bytes (default 2)
-dbnum <db> SELECT the specified db number (default 0)
-k <boolean> 1=keep alive 0=reconnect (default 1)
-r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD
Using this option the benchmark will expand the string __rand_int__
inside an argument with a 12 digits number in the specified range
from 0 to keyspacelen-1. The substitution changes every time a command
is executed. Default tests use this to hit random keys in the
specified range.
-P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline).
-q Quiet. Just show query/sec values
–csv Output in CSV format
-l Loop. Run the tests forever
-t <tests> Only run the comma separated list of tests. The test
names are the same as the ones produced as output.
-I Idle mode. Just open N idle connections and wait.

 

啟動benchmark之前你得有個正在執行的Redis例項。執行benchmark典型示例如下:

redis-benchmark -q -n 100000

使用這個工具比較簡單,你也可以實現你自己的benchmark,但是有些坑不要踩。

只執行一部分測試

不是每次執行redis-benchmark時,都要執行所有預設的測試。選擇一部分來測試,只要簡單地使用-t選項,如下:

$ redis-benchmark -t set,lpush -n 100000 -q
SET: 74239.05 requests per second
LPUSH: 79239.30 requests per second

上面的例子中,在安靜模式下(參見-q選項),我們只測試了SET和LPUSH命令。也可以直接給benchmark指定命令,如下:

$ redis-benchmark -n 100000 -q script load “redis.call(‘set’,’foo’,’bar’)”
script load redis.call(‘set’,’foo’,’bar’): 69881.20 requests per second

 

選擇key空間的大小

預設情況下,benchmark只針對單一個key測試。在人為測試環境和真實使用環境下,Redis表現出來的差別不是很大,因為它是一個記憶體系統。但如果使用更大範圍的key,則可以測試快取命中,並模擬一個更真實的工作負載。

使用-r選項就可以達到此目的,比如我想執行一百萬次SET操作,每次操作從10萬個key裡面隨機選一個,可以使用下面的命令:

$ redis-cli flushall
OK

$ redis-benchmark -t set -r 100000 -n 1000000
====== SET ======
1000000 requests completed in 13.86 seconds
50 parallel clients
3 bytes payload
keep alive: 1

99.76% `<=` 1 milliseconds
99.98% `<=` 2 milliseconds
100.00% `<=` 3 milliseconds
100.00% `<=` 3 milliseconds
72144.87 requests per second

$ redis-cli dbsize
(integer) 99993

管道化

預設情況下,每個模擬的客戶端(如果不指定選項-c,benchmark模擬50個客戶端)需要等待,只有收到前一條命令的響應之後才會傳送下一條命令。這意味著,客戶端傳送的每條命令,伺服器都需要一次read呼叫才能獲得。當然,等待時間也得算上RTT的時間。

Redis支援/topics/pipelining即管道化,因此是可以一次傳送多條命令的。現實中的應用經常用到這個特性。Redis在管道化模式下能夠大幅提高伺服器的每秒運算元

下面的例子中,使用由16條命令的組成的管道,在一臺MacBook Air 11″上執行benchmark:

$ redis-benchmark -n 1000000 -t set,get -P 16 -q
SET: 403063.28 requests per second
GET: 508388.41 requests per second

使用管道化可以大大地提高效能。

一些坑和誤區

第一點很明顯: benmark測試的黃金法則是隻比較相互有可比性的東西。例如基於相同的工作負載比較不同版本的Redis。或者使用相同版本的Redis,但是使用不同的選項執行。如果你打算把Redis和其他什麼東西進行比較,評估和考慮他們功能上和技術上的差異非常重要。

  • Redis是個伺服器:所有的命令在網路和IPC的之間都需要經過一個來回的過程。把redis和內嵌資料儲存(embedded data stores)比如SQLite,Berkeley DB,Tokyo/Kyoto Cabinet等等進行比較沒有意義,因為redis大多數操作的代價花在網路/協議管理上了。
  • redis對所有常用命令都會返回一個確認。其他的資料儲存未必這樣,比如MongoDB就不確認寫操作。把redis和單向查詢的儲存比較用處不大。
  • 簡單地迭代同步的Redis命令並不是測試redis,而是測算你的網路(或者IPC)延遲。要想真正測試redis,你需要多個連線(像redis-benchmark那樣),使用多執行緒或多程式,使用管道化以便一次發多條命令。
  • redis是一個記憶體資料儲存(in-memory data store),也帶一些持久化功能。如果打算把它和事務類伺服器(MySQL, PostgreSQL等)進行比較,你得啟用AOF,並且確定合適的fsync策略。
  • redis是個單執行緒伺服器,沒有設計成利用多核cpu獲得更多效能。如果需要的話,應該啟動多個redis例項來利用多核。將單個redis例項和多執行緒的資料儲存(multi-threaded data store)不大公平。

 

一種常見的誤區是redis-benchmark被設計成使得redis效能看上去更好,redis-benchmark的吞吐量似乎有點假,而不是由真實應用輸出的。這種認識實際是不對的。

要獲得和評估一個redis例項在給定硬體上的效能,使用redis-benchmark程式是一種快速且有用的方式。然而,預設情況下,redis-benmark並不能測出一個redis例項所能承受的最大吞吐量。實際上,通過使用管道化和快速客戶端(hiredis),寫一個吞吐量比redis-benchmark更高的程式是相當容易的。redis-benchmark的預設行為僅僅是利用併發性來達成吞吐量(即建立了好幾個連線到伺服器),並沒有使用管道化或者任何並行操作(每個連線上最多一條等待處理的查詢,也沒有多執行緒)。

要使用管道化執行一個benchmark獲得更大吞吐量,你需要加上-P選項。注意,這樣比較切實,因為很多基於redis的應用積極使用管道化來提高效能。

最後,進行多個資料儲存比較時,benchmark應該處在相同的工作模式下,使用相同的操作。把redis-benchmark的測試結果和其他benchmark程式的結果比較和推斷,沒什麼意義。

例如,可以對redis和單執行緒模式的memcached基於GET/SET操作進行比較。兩個都是記憶體資料儲存,協議層幾乎是一樣的工作方式。如果各自的benchmark程式以同樣的方式(管道化)彙集多條查詢,並使用差不多的連線數,這樣的比較才有意義。

Redis (antirez) 和memcached (dormando) 的開發人員之間的對話就是最好的例子。

antirez 1 – On Redis, Memcached, Speed, Benchmarks and The Toilet  (校對注:歡迎在ifeve.com翻譯發表此文

dormando – Redis VS Memcached (slightly better bench)   (校對注:不能訪問)

antirez 2 – An update on the Memcached/Redis benchmark

從最後的結果可以看到,考慮所有技術相關的方面,這兩種解決方案之間的差異並沒有大得讓人出乎意料。注意,在這些效能測試之後,redis和memcached已經又進一步優化了。

 

最後,在測試那些高效的伺服器時(redis和memcache肯定屬於這一類),其實很難讓服務達到滿負荷。有時性瓶頸在客戶端這邊,而不是伺服器那邊。這種情況下,要讓伺服器達到最大吞吐量,客戶端(也就是benchmark程式本身)必須得修復或擴充套件。

 

影響redis效能的因素

有多種因素會直接影響redis的效能。這裡,我們分析一些,因為它們可以改變所有benchmark測試的結果。但請注意,一個典型的redis例項,執行在低速、未調優過的系統上,提供的效能對大多數應用來說也是夠好的。

  • 網路頻寬和延遲常常是直接影響效能。啟動benchmark程式前,使用ping程式快速檢查客戶端和伺服器之間的延遲是一種好習慣。關於頻寬,一般用Gbit/s來評估吞吐量,並和網路的理論頻寬比較。例如,一個benchmark以每秒100000次設定4KB的string到redis中,將消耗2Gbit/s的頻寬,一般需要頻寬為10Gbit/s的連結,而不是1Gbit/s的連結。在很多現實的場景中,redis吞吐量先受到網路限制,然後才是CPU。要在一臺機器上整合多個高效能的redis例項,要考慮裝一塊10Gbit/s或者多塊1Gbit/s的TCP/IP網路卡。
  • CPU是另一個重要的因素。作為單執行緒的,redis喜歡高速有大快取而不是有多個核的cpu。這場較量中,intel cpu是贏家。redis使用AMD Operon CPU時的效能只達到使用Nehalem EP/Westmere EP/Sandy Bridge Intel CPUs時的一半,這並不稀奇。當客戶端和伺服器執行在同一個系統上,cpu就是redis-benchmark的限制因素。
  • ram的速度和記憶體頻寬對全域性效能的影響則次要多了,尤其是小資料物件時。對大物件(大於10KB)而言,影響會明顯一些。一般來說,買昂貴的快速記憶體來優化redis並不是很經濟的做法。
  • 相同的硬體上,執行在虛擬機器上的redis會比執行在真實系統上的redis慢。如果可以在物理機上執行redis是最好的。當然,這並不意味著,在虛擬環境下redis就慢,處理效能依然很好,虛擬環境下,可能遇到的大多數效能問題是由於預留空間(over-provisioning),高延遲的非本地磁碟(non-local disks with high latency),或者哪些使用低速fork系統呼叫實現的舊管理程式(hypervisor)。
  • 當伺服器和客戶端benchmark程式執行同一系統上時,TCP/IP迴路和unix域套接字都可使用。取決於平臺,使用unix域套接字可以比使用TCP/IP迴路多大約15%的吞吐量(比如在linux上)。redis-benchmark預設使用TCP/IP迴路。
  • 大量使用管道化(即長管道)時,比起使用TCP/IP,unix域套接字的效能提升會有所下降。
  • 使用乙太網訪問redis,資料大小保持小於乙太網報文大小(大約1500位元組),使用管道化彙集多條命令特別有效。實際上,處理10位元組,100位元組,1000位元組的查詢幾乎是一樣的吞吐量。如下圖。

Data_size

  • 在有多個CPU插口的伺服器上,redis的效能依賴於NUMA配置和程式位置。最明顯的是,redis-benchmark的結果似乎無法確定,因為客戶端和伺服器程式隨機分配在這些核上。要得到確定性的結果,得使用程式安置工具(linux上是taskset或numactl)。最有效的組合是,總是把客戶端和伺服器程式放在cpu不同的核上,充分利用起L3快取。使用4KB的SET命令,在不同的程式安置組合下, benchmark測試三個CPU(AMD Istanbul, Intel Nehalem EX, and Intel Westmere)的結果如下。請注意,這個benchmark測試並不是為了比較CPU型號本身(這裡並未標出CPU的準確型號和頻率)。

NUMA_chart

  • 使用高階配置,客戶端連線數也是很重要的因素。基於epoll/kqueue,redis的事件迴圈伸縮性較好。redis已經測試過超過60000連線,這些條件下,仍然能承受每秒50000次請求。經驗告訴我們,有30000連線的redis,只能處理100連線時一半的吞吐量。下面的例子中顯示各連線數下,redis例項對應的吞吐量:

Connections_chart

  • 在高階配置上,通過調優NIC的配置和對應的中斷,可能會獲得更高的吞吐量。通過設定和CPU核數相近的網路卡佇列數,並啟用RPS(Receive Packet Steering),則可以獲得最大吞吐量。更多內容清參考這裡。傳送大物件時,使用大容量幀(Jumbo frames)也可以提升效能。
  • 看平臺支援情況,redis可以在編譯時指定使用不同的記憶體分配器(libc malloc, jemalloc, tcmalloc),從原始速度(raw speed),內部和外部碎片這些方面看,行為表現會有不同。如果不是自己編譯的redis,可以使用命令INFO檢視記憶體分配器。和生產環境的redis相比,benchmark執行的時間不夠長,無法產生足夠多的外部碎片。

 

其他要考慮的事情

benchmark的一個重要目的是得到可重複的結果,這樣才能和其他測試的結果進行比較。

  • 好的做法是儘可能地在孤立的硬體上執行測試。如果沒條件,則必須監控系統確保benchmark沒有受外界活動影響。
  • 某些機器(桌面和筆記本肯定支援,有些伺服器也支援)支援可變的cpu 核心頻率。控制策略可以在OS層設定。某些CPU型號在改變CPU頻率適應工作負載方面比其他型號更快。要得到可重複的結果,在測試時,最好把所有CPU核的頻率固定設在最高值。
  • 根據benchmark測試,調整系統也是很重要的。系統必須要有足夠的RAM,不使用SWAP。在LINUX上不要忘記正確設定overcommit_memory引數。注意32位和64位的redis使用的記憶體空間是不一樣的。
  • 如果打算使用RDB或AOF,請確保系統中沒有其他I/O活動。避免把RDB或AOF檔案在NAS或NFS上共享,或放在其他影響網路頻寬或延遲的裝置上(比如Amazon EC2的EBS)。
  • 設定redis的日誌級別(引數loglevel)為warning 或notice。避免把日誌放在遠端檔案系統上。
  • 避免使用影響benchmark結果的監控工具。比如,定期使用INFO收集統計資訊是可以的,但MONITOR則會對測試的效能結果有較大影響。

 

不同虛擬機器和裸機上的benchmark結果

  • 同時使用50個客戶端傳送2百萬請求進行測試
  • 所有測試使用版本6.14
  • 使用迴路介面測試
  • 使用1百萬的key空間測試
  • 使用管道化(彙集16條命令)和不使用管道化分別測試

Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (with pipelining)

$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -P 16 -q
SET: 552028.75 requests per second
GET: 707463.75 requests per second
LPUSH: 767459.75 requests per second
LPOP: 770119.38 requests per second

Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (without pipelining)

$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 122556.53 requests per second
GET: 123601.76 requests per second
LPUSH: 136752.14 requests per second
LPOP: 132424.03 requests per second

Linode 2048 instance (with pipelining)

$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q -P 16
SET: 195503.42 requests per second
GET: 250187.64 requests per second
LPUSH: 230547.55 requests per second
LPOP: 250815.16 requests per second

Linode 2048 instance (without pipelining)

$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 35001.75 requests per second
GET: 37481.26 requests per second
LPUSH: 36968.58 requests per second
LPOP: 35186.49 requests per second

 

更多未使用管道化的詳細測試

$ redis-benchmark -n 100000

====== SET ======
100007 requests completed in 0.88 seconds
50 parallel clients
3 bytes payload
keep alive: 1

58.50% <= 0 milliseconds
99.17% <= 1 milliseconds
99.58% <= 2 milliseconds
99.85% <= 3 milliseconds
99.90% <= 6 milliseconds
100.00% <= 9 milliseconds
114293.71 requests per second

====== GET ======
100000 requests completed in 1.23 seconds
50 parallel clients
3 bytes payload
keep alive: 1

43.12% <= 0 milliseconds
96.82% <= 1 milliseconds
98.62% <= 2 milliseconds
100.00% <= 3 milliseconds
81234.77 requests per second

====== INCR ======
100018 requests completed in 1.46 seconds
50 parallel clients
3 bytes payload
keep alive: 1

32.32% <= 0 milliseconds
96.67% <= 1 milliseconds
99.14% <= 2 milliseconds
99.83% <= 3 milliseconds
99.88% <= 4 milliseconds
99.89% <= 5 milliseconds
99.96% <= 9 milliseconds
100.00% <= 18 milliseconds
68458.59 requests per second

====== LPUSH ======
100004 requests completed in 1.14 seconds
50 parallel clients
3 bytes payload
keep alive: 1

62.27% <= 0 milliseconds
99.74% <= 1 milliseconds
99.85% <= 2 milliseconds
99.86% <= 3 milliseconds
99.89% <= 5 milliseconds
99.93% <= 7 milliseconds
99.96% <= 9 milliseconds
100.00% <= 22 milliseconds
100.00% <= 208 milliseconds
88109.25 requests per second

====== LPOP ======
100001 requests completed in 1.39 seconds
50 parallel clients
3 bytes payload
keep alive: 1

54.83% <= 0 milliseconds
97.34% <= 1 milliseconds
99.95% <= 2 milliseconds
99.96% <= 3 milliseconds
99.96% <= 4 milliseconds
100.00% <= 9 milliseconds
100.00% <= 208 milliseconds
71994.96 requests per second

注意:修改包的載荷分別為256,1024,4096位元組,並沒有明顯改變結果數字(但應答包被合在一起直到1024位元組,因此大資料包時GET會慢些)。客戶端數量從50變到256時,結果數字是一樣的。只有10個客戶端時,則慢一點。

不同的系統上會有不同結果。比如一個低配主機,CPU是主頻為1.66GHz 的intel coreT5500,執行著linux 2.6,測試結果如下:

$ ./redis-benchmark -q -n 100000
SET: 53684.38 requests per second
GET: 45497.73 requests per second
INCR: 39370.47 requests per second
LPUSH: 34803.41 requests per second
LPOP: 37367.20 requests per second

 

另一個CPU為2.5GHz Xeon L5420的64位主機上,結果如下:

$ ./redis-benchmark -q -n 100000
PING: 111731.84 requests per second
SET: 108114.59 requests per second
GET: 98717.67 requests per second
INCR: 95241.91 requests per second
LPUSH: 104712.05 requests per second
LPOP: 93722.59 requests per second

 

在優化過的高階服務硬體上的測試結果示例

  • 版本為redis 2.4.2
  • 預設數量的連線數,資料載荷為256位元組
  • linux主機執行著SLES10 SP3 2.6.16.60-0.54.5-smp,配備2 個主頻為93GHz的Intel X5670 CPU
  • benchmark客戶端和redis執行在同一個CPU上,但在不同的核上進行測試

 

使用UNIX域套接字:

$ numactl -C 6 ./redis-benchmark -q -n 100000 -s /tmp/redis.sock -d 256
PING (inline): 200803.22 requests per second
PING: 200803.22 requests per second
MSET (10 keys): 78064.01 requests per second
SET: 198412.69 requests per second
GET: 198019.80 requests per second
INCR: 200400.80 requests per second
LPUSH: 200000.00 requests per second
LPOP: 198019.80 requests per second
SADD: 203665.98 requests per second
SPOP: 200803.22 requests per second
LPUSH (again, in order to bench LRANGE): 200000.00 requests per second
LRANGE (first 100 elements): 42123.00 requests per second
LRANGE (first 300 elements): 15015.02 requests per second
LRANGE (first 450 elements): 10159.50 requests per second
LRANGE (first 600 elements): 7548.31 requests per second

使用TCP迴路:

$ numactl -C 6 ./redis-benchmark -q -n 100000 -d 256
PING (inline): 145137.88 requests per second
PING: 144717.80 requests per second
MSET (10 keys): 65487.89 requests per second
SET: 142653.36 requests per second
GET: 142450.14 requests per second
INCR: 143061.52 requests per second
LPUSH: 144092.22 requests per second
LPOP: 142247.52 requests per second
SADD: 144717.80 requests per second
SPOP: 143678.17 requests per second
LPUSH (again, in order to bench LRANGE): 143061.52 requests per second
LRANGE (first 100 elements): 29577.05 requests per second
LRANGE (first 300 elements): 10431.88 requests per second
LRANGE (first 450 elements): 7010.66 requests per second
LRANGE (first 600 elements): 5296.61 requests per second


相關文章