Apache Cassandra 4.0已經發布了Beta版,這是第一個支援JDK 11及更高JDK版本的Cassandra版本。
時延對於Apache Cassandra™使用者來說是個顯而易見的關注點,所以大家對JDK 11中引入的全新低時延垃圾收集器ZGC(Z Garbage Collector)寄予厚望。
我們將看到Cassandra 4.0帶來了強大的效能改進,而一些新的垃圾收集器(ZGC、尤其是Shenandoah)的上線很大程度上加強了這些提升改進的效果。
經作者測試,Cassandra 4.0在吞吐量和時延方面都有25%到30%的提升。所以在使用相同的垃圾收集器時,Cassandra 4.0可以很輕易地打敗Cassandra 3.11.6。
01 測試方法
下面進行的基準測試是用tlp-cluster在AWS中生成並配置Apache Cassandra叢集的,並用tlp-stress來進行負載生成和指標收集工作的。
所有在本測試中用到的工具都是開源的,並且只要擁有一個AWS賬號,任何人都可以很容易地復現本次測試的過程和結果。
本測試中的叢集中有三個r3.2xlarge例項作為節點,另有一個c3.2xlarge例項作為壓測節點。
除了垃圾收集和堆記憶體設定外,我們使用了Apache Cassandra的預設設定。
叢集的生成和配置是由最新版本的tlp-cluster完成的。除此之外,我們還新增了一些輔助指令碼(helper script),從而可以將Reaper和Medusa的叢集生成和安裝過程自動化。
在根據文件安裝並配置好tlp-cluster工具之後,你就可以隨時建立跟我們一樣的 用來做基準測試的Cassandra叢集了。
# 3.11.6 CMS JDK8 build_cluster.sh -n CMS_3-11-6_jdk8 -v 3.11.6 --heap=16 --gc=CMS -s 1 -i r3.2xlarge --jdk=8 --cores=8 # 3.11.6 G1 JDK8 build_cluster.sh -n G1_3-11-6_jdk8 -v 3.11.6 --heap=31 --gc=G1 -s 1 -i r3.2xlarge --jdk=8 --cores=8 # 4.0 CMS JDK11 build_cluster.sh -n CMS_4-0_jdk11 -v 4.0~alpha4 --heap=16 --gc=CMS -s 1 -i r3.2xlarge --jdk=11 --cores=8 # 4.0 G1 JDK14 build_cluster.sh -n G1_4-0_jdk14 -v 4.0~alpha4 --heap=31 --gc=G1 -s 1 -i r3.2xlarge --jdk=14 --cores=8 # 4.0 ZGC JDK11 build_cluster.sh -n ZGC_4-0_jdk11 -v 4.0~alpha4 --heap=31 --gc=ZGC -s 1 -i r3.2xlarge --jdk=11 --cores=8 # 4.0 ZGC JDK14 build_cluster.sh -n ZGC_4-0_jdk14 -v 4.0~alpha4 --heap=31 --gc=ZGC -s 1 -i r3.2xlarge --jdk=14 --cores=8 # 4.0 Shenandoah JDK11 build_cluster.sh -n Shenandoah_4-0_jdk11 -v 4.0~alpha4 --heap=31 --gc=Shenandoah -s 1 -i r3.2xlarge --jdk=11 --cores=8
注意:為了在基準測試中控制變數,整個測試中我們會同用一組EC2例項進行測試。
適當地使用下面的指令碼,就可以完成Cassandra 3.11.6到Cassandra 4.0~alpha4的升級以及不同版本JDK的置換:
#!/usr/bin/env bash OLD=$1 NEW=$2 curl -sL https://github.com/shyiko/jabba/raw/master/install.sh | bash . ~/.jabba/jabba.sh jabba uninstall $OLD jabba install $NEW jabba alias default $NEW sudo update-alternatives --install /usr/bin/java java ${JAVA_HOME%*/}/bin/java 20000 sudo update-alternatives --install /usr/bin/javac javac ${JAVA_HOME%*/}/bin/java
在呼叫JDK版本管理工具jabba時,可以使用以下JDK值:
-
openjdk@1.11.0-2
-
openjdk@1.14.0
-
openjdk-shenandoah@1.8.0
-
openjdk-shenandoah@1.11.0
OpenJDK 8已經用Ubuntu的apt工具安裝完成。
下面是在基準測試中,在不同的JDK版本下,java -version的輸出結果:
-
jdk8
openjdk version "1.8.0_252" OpenJDK Runtime Environment (build 1.8.0_252-8u252-b09-1~18.04-b09) OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)
-
jdk8 with Shenandoah
openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment 18.9 (build 11.0.2+9) OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
-
jdk11 with Shenandoah
openjdk version "11.0.8-testing" 2020-07-14 OpenJDK Runtime Environment (build 11.0.8-testing+0-builds.shipilev.net-openjdk-shenandoah-jdk11-b277-20200624) OpenJDK 64-Bit Server VM (build 11.0.8-testing+0-builds.shipilev.net-openjdk-shenandoah-jdk11-b277-20200624, mixed mode)
- jdk14
openjdk version "14.0.1" 2020-04-14 OpenJDK Runtime Environment (build 14.0.1+7) OpenJDK 64-Bit Server VM (build 14.0.1+7, mixed mode, sharing)
02 CMS
CMS (Concurrent Mark Sweep)收集器是目前Apache Cassandra預設的垃圾收集器。由於它在JDK 14中被移除了,所以所有的測試都是基於JDK 8或JDK 11進行的。
下面的設定被用於CMS的基準測試:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSWaitDuration=10000 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSEdenChunksRecordAlways -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=8 -XX:ConcGCThreads=8 -Xms16G -Xmx16G -Xmn8G
請注意,-XX:+UseParNewGC引數已經被從JDK 11中移除,在那之後它就變成了一個隱式引數(implicit parameter)。使用這個引數會阻止JVM的啟動。
我們將CMS的最大堆記憶體(max heap)限制在16GB,否則它可能會引發major collection的長時間暫停。
03 G1
相比CMS收集器,G1GC(即Garbage-First Garbage Collector,垃圾優先型垃圾收集器)要容易配置一些,因為它可以動態調整年輕代的大小。
不過G1GC更適用於大型的堆記憶體(>=24GB)——這也就是為什麼它沒有成為Cassandra的預設垃圾收集器。另外,雖然它的吞吐量比CMS更好,但是它的時延比除錯過的CMS要長。
下面的設定被用於G1的基準測試:
-XX:+UseG1GC -XX:G1RSetUpdatingPauseTimePercent=5 -XX:MaxGCPauseMillis=300 -XX:InitiatingHeapOccupancyPercent=70 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=8 -Xms31G -Xmx31G
為了對Cassandra 4.0進行基準測試,我們在執行G1測試時使用了JDK14。
我們使用31GB的堆記憶體大小,從而可以受益於壓縮指標(compressed oops),並可以以最小的堆記憶體大小擁有最多的可定址物件。
04 ZGC
ZGC(Z Garbage Collector)是JDK中最新的垃圾收集器,它主要的關注點是將讓全世界暫停(stop-the-world)的時延縮小至10ms以內。ZGC還應該可以保證堆記憶體大小對暫停時間沒有影響,這使得它的堆記憶體大小可以擴充至16TB。
如果這些令人期待的效果都能被滿足,ZGC就使得使用堆外儲存變得沒有必要,而且還能簡化Apache Cassandra的一些開發任務。
下面的設定被用於ZGC的基準測試:
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:ConcGCThreads=8 -XX:ParallelGCThreads=8 -XX:+UseTransparentHugePages -verbose:gc -Xms31G -Xmx31G
我們需要用-XX:+UseTransparentHugePages作為一種靈活變通的方式來避免在Linux裡啟用大記憶體頁(page)。
儘管官方的ZGC文件稱這可能會造成時延激增,從測試的結果來看似乎並沒出現這種情況。我們也許可以使用大記憶體頁來進行多次吞吐量測試,從而判斷這種方法會對基準測試的結果有什麼影響。
請注意,ZGC不能使用壓縮指標,但是也不受“32GB閾值”的限制。我們在對ZGC的測試中,使用和在G1測試中一樣的31GB的堆記憶體。這樣,兩種情況下系統空閒的記憶體大小就會是一樣的。
05 Shenandoah
Shenandoah是一個由紅帽(RedHat)開發的低時延垃圾收集器。在JDK 8和11中,它作為一個向後移植(backport)的版本存在。從Java 13開始,它就被包含在OpenJDK的主線版本里了。
與ZGC一樣,在大多數情況下Shenandoah是個併發垃圾收集器。它的目標是讓暫停時間不會隨著堆記憶體的增大而線性增加。
下面的設定被用於Shenandoah的基準測試:
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ConcGCThreads=8 -XX:ParallelGCThreads=8 -XX:+UseTransparentHugePages -Xms31G -Xmx31G
Shenandoah應該可以使用壓縮指標,因此可以受益於使用比32GB稍小一些的堆。
06 Cassandra 4.0的JVM配置
Cassandra 4.0版本分別為Java 8和Java 11推出了不同的jvm.options檔案。它們是:
-
conf/jvm-server.options
-
conf/jvm8-server.options
-
conf/jvm11-server.options
如果將Cassandra從3.11版本升級到4.0版本,原先已有的jvm.options檔案依然可以沿用,只要它被重新命名為jvm-server.options,並且將jvm8-server.options和jvm11-server.options檔案移除就好。不過,這並不是值得推薦的方式.
值得推薦的方式是將原來的jvm.options檔案裡的設定,重新應用到新的jvm-server.options和jvm8-server.options檔案上。這些特定的Java選項檔案(option file)大多是和垃圾收集的引數相關的。
一旦jvm-server.options和jvm8-server.options檔案更新完畢並且就位,配置jvm11-server.options檔案以及從JDK 8轉換到JDK 11就容易多了。
07 工作負載
本次基準測試中有8個執行緒,並將讀寫比例限制為80%寫/20%讀。tlp-stress大量使用非同步的查詢語句,這使得它只要有限的幾個壓測執行緒一不小心就可能會讓Cassandra節點過載。在本次的負載測試中,每一個執行緒會一次併發地傳送50個查詢語句。
本次測試中的鍵空間(keyspace)的複製因子(replication factor)為3,並且所有的語句都以一致性級別LOCAL_ONE來執行。
對於所有垃圾收集器和Cassandra版本的測試,操作的數目都以每秒25k、40k、45k、50k的增長幅度進行,因而我們能夠評估它們在不同壓力水平下的效能表現。
下面的tlp-stress語句被使用在本測試中:
tlp-stress run BasicTimeSeries -d 30m -p 100M -c 50 --pg sequence -t 8 -r 0.2 --rate <desired rate> --populate 200000
所有的工作負載都執行30分鐘,將5至16GB的資料裝載到每個節點並考慮合理的壓實負載。
請注意,這個測試的目的並非測評Cassandra的最優效能,因為對於不同的工作負載,可以通過很多方式進行除錯出最優效能。
這個測試的目的也並非除錯這些垃圾收集器,雖然它們其實已經暴露了很多可以針對特定工作負載提高效能的引數和選項。
這些基準測試想要達到的目的是:在使用大部分預設設定並且Cassandra產生同樣的負載的情況下,提供一個針對不同垃圾收集器的公平比較。
08 基準測試結果
3.11.6 25k-40k ops/s
4.0 25k-40k ops/s
4.0 45k-50k ops/s
就吞吐量而言,Cassandra 3.11.6最高可達41k ops/s,而Cassandra 4.0則高達51k ops/s。兩種版本都用了CMS作為垃圾收集器,而升級後的Cassandra 4.0的比Cassandra 3.11.6的提升達到了25%。
4.0版本中的大量效能提升都可以用來解釋這個結果,尤其是關於壓實(compaction)操作引發的堆記憶體壓力問題(可檢視CASSANDRA-14654作為例子)。
在Cassandra 3.11.6叢集中的JDK 8的Shenandoah在40k ops/s的負載測試中,不僅沒能實現最高的吞吐量,還出現了查詢失敗的情況。
而藉助於Cassandra 4.0叢集和JDK 11,Shenandoah的表現就好很多——它在這種情況下的最大吞吐量49.6k ops/s,幾乎可以趕得上4.0在CMS下的吞吐量了。
使用JDK 8和Cassandra 3.11.6,G1和Shenandoah總體上的吞吐量都只能最高達到36k ops/s。
使用JDK 14和JDK 11,前者似乎讓G1的表現也有些微提高——從47k/s提高到50k/s。
無論是使用JDK 14還是JDK 11,ZGC的吞吐量都無法與其它的垃圾回收器匹敵,最高只能到41k ops/s。
在Cassandra 3.11.6叢集中,JDK 8的Shenandoah在中等程度的負載下,展現出了非常令人印象深刻的低時延。不過,它的效能會隨著負載壓力的提升而嚴重下降。
在使用CMS的情況下,Cassandra 4.0的平均p99(99百分位)值介於11ms到31ms之間,而吞吐量則高達50k ops/s。在中等程度的負載下,讀操作的P99平均值在Cassandra 3.11.6中為17ms,而在Cassandra 4.0中則下降到了11.5ms。即相比之下,在時延方面Cassandra 4.0有30%的提升。
Cassandra 4.0在吞吐量和時延方面都有25%到30%的提升,所以在使用相同的垃圾收集器時,Cassandra 4.0可以很輕易地打敗Cassandra 3.11.6。
值得一提的是,在Cassandra 3.11.6叢集中,Shenandoah在中等程度的負載下的延遲非常低,不過它在壓力下的表現使我們擔心其處理突增負載的能力。
雖然ZGC在中等程度的負載下展現出了非常令人印象深刻的低時延,尤其是在使用JDK 14時,但是它的最高吞吐量並不能與Shenandoah相匹敵。
幾乎在所有的負載測試中,Shenandoah的讀操作時延和寫操作時延的平均p99值都是最低的。Shenandoah這樣低的時延再加上它在Cassandra 4.0中能達到的吞吐量,使它成為了在向Cassandra 4.0升級時,一個值得考慮的垃圾收集器。
在中等程度的負載下,讀操作時延的平均p99值只有2.64ms本身已經是相當令人印象深刻的。在此基礎上,如果你知道這些的資料是由客戶端記錄的,你就不得不對Shenandoah刮目相看了。
在大多數情況下,G1的最大p99值符合它所配置的最大暫停時間,即300ms。如果想要降低目標的暫停時間,則可能會在高負載的情況下出現不想看到的效果,甚至可能會觸發更長時間的暫停。
在中等程度的負載下,Shenandoah的平均p99時延可以降低77%,即最高時延只有2.64ms。這對於時延敏感的用例來說會是一項重大的提升——相比使用Cassandra 3.11.6中的CMS,讀操作的p99時延大幅降低了85%!
值得一提的是,JDK 14中的ZGC在中等程度的負載下有著良好的效能,但是遺憾的是它不能在更大的吞吐速率下保持同樣的表現。我們樂觀地認為ZGC會在未來的幾個月內被改進,最終可能會足以與Shenandoah一較高下。
09 反思
G1通過移除對不同代的大小的除錯需求得以提升Cassandra的易用性,但是 是以犧牲一定的效能為代價的。
新發行的Apache Cassandra 4.0帶來了令人印象極為深刻的效能增強,它將會允許使用像是Shenandoah和ZGC這樣的新一代垃圾收集器。這些收集器簡單易用,無需太多細微精妙的除錯,並且在時延問題上更為高效。
如果使用Cassandra 3.11.6,我們很難向你推薦Shenandoah,因為Cassandra節點在高負載情況下的表現並不如意。但是從JDK 11和Cassandra 4.0開始,Shenandoah在延遲方面有著驚人的改進,同時還能支援幾乎是Cassandra資料庫所能提供的最大吞吐量。
由於這次基準測試集中關注於特定的工作負載,你的測試結果可能會依情況而有所不同。但是對於時延敏感的用例來說,這次測試的結果讓我們對Apache Cassandra的未來感到相當的樂觀,因為它將為我們帶來超過Cassandra 3.11.6的大幅改進。
下載最新的Apache 4並且自行嘗試一番吧。如果你有任何反饋意見,記得通過社群郵件或是ASF Slack聯絡我們。點選這裡檢視我們的聯絡方式。