作者:任坤
現居珠海,先後擔任專職 Oracle 和 MySQL DBA,現在主要負責 MySQL、mongoDB 和 Redis 維護工作。
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
1、背景
某業務寫多讀少,上線前預估TPS最高可達4w且可能會隨時增長,去年上線時採用了mongo 4分片叢集架構。
現在業務趨於穩定,日常TPS只有最高值的1/10不到,專案組出於成本考慮想要將其遷移到記憶體KV資料庫,但是 redis純記憶體模式機器成本有點高,經過調研後決定嘗試360開源的pika。
我們採用的是3.3.6版本,機器A配置為8c8G200G,壓測出現大量超時ERROR: redis: connection pool timeout,qps只有3k左右,且磁碟的%util 一直接近100,處於“飽和”狀態。
同樣版本的pika,在機器B上測試,qps能達到40K且沒有1個超時錯誤,這臺機器配置24c48G1.5T。 從多家雲廠商那裡獲悉相同型號的雲主機,硬碟容量越大IO吞吐會越高,因此一開始懷疑是硬體問題。
針對兩臺雲主機進行FIO測試,配置更高的機器讀寫吞吐是會高一些(大概提升20%左右),但並沒有pika qps指標相 差10倍這麼誇張。
將機器B的pika配置檔案複製到機器A上,重啟pika再測進行壓測,這次機器A的qps也能達到40K,說明pika配置 引數導致的效能差異。
2、診斷
兩個配置檔案引數相差的有點多,只能逐個修改並壓測。
測試過程略過,最後確認是max-write-buffer-size設定不合理導致的,該引數預設值14045392(13M),調大為 4294967296(4G)後pika qps就從3k提升到了40K。
以下是對應測試案例的iostat截圖
-- max-write-buffer-size 14045392(13M)
-- max-write-buffer-size 4294967296
兩者最大的差異是w/s和avgrq-sz,其中avgrq-sz描述的是IO請求的平均大小,以扇區(512位元組)為單位。
圖1每秒磁碟寫請求4700,每個請求平均大小為55 * 0.5 ~= 27.5K,出現了大量的小塊寫。
圖2每秒磁碟寫請求200左右,每個請求平均大小為800 * 0.5 ~= 400K,明顯採用了批量落盤的策略。
再看%util 指標,這個不是我們通常理解的磁碟飽和度,而是磁碟使用率,其計算時只關注io請求數量,不理會每 個io請求的大小,即便達到了100,並不意味著磁碟吞吐已達上限。
假設某路段有1w輛私家車(每車只有1個人,avgrq-sz=1)同時通行,即便平均每秒放行10輛車(w/s=10),總體運 力也只有10人/s,若是改成50座大巴車(avgrq-sz=50),即便每秒只放行1輛車(w/s=1),總體運力也會提高到50 人/s。
在這個案例中,%util 記錄的只是平均每秒通行的機動車數量,不關心每輛車坐了多少人,如果私家車的%util 是 100,那大巴車的%util只有10並且吞吐更高,跟上述截圖描述的場景十分吻合。
關於max-write-buffer-size引數,pika官檔原文如下:https://github.com/OpenAtomFo... %E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E
# Pika 底層單個rocksdb單個memtable的大小, 設定越大寫入效能越好但會在buffer刷盤時帶來更大的IO負載, 請 依據使用場景合理配置
[RocksDb‐Tuning‐Guide](https://github.com/facebook/rocksdb/wiki/RocksDB‐Tuning‐Guide)
write‐buffer‐size : 268435456
# pika例項所擁有的rocksdb例項使用的memtable大小上限,如果rocksdb實際使用超過這個數值,下一次寫入會造成 刷盤
[Rocksdb‐Basic‐Tuning](https://github.com/facebook/rocksdb/wiki/Setup‐Options‐and‐Basic‐Tuning)
max‐write‐buffer‐size : 10737418240
RocksDB採用WAL + LSM架構,memtable可以看作是使用者資料落盤的基本單位,memtable越大則落盤時越傾 向於批量寫,更能有效利用磁碟IO吞吐。
最初的引數檔案沒有設定max-write-buffer-size,只有write-buffer-size,奇怪的是調大write-buffer-size並不會 將前者自動增大,兩者不具備聯動關係。
我在壓測時嘗試調大write-buffer-size到1G(max-write-buffer-size保持預設值),效能依然上不去,看來是max- write-buffer-size起到了決定性作用。
經過多次壓測,最終我們的主要引數設定如下:
thread‐num : 8 #和cpu核數相同
thread‐pool‐size : 8
write‐buffer‐size : 268435456
max‐write‐buffer‐size : 4294967296
compression : snappy
max‐background‐flushes : 2
max‐background‐compactions : 2
3、結論
通過這個案例對iostat的輸出指標有了更深一步的瞭解,以後再遇到%util達到100時先不要輕易作出磁碟IO已飽和 的結論,很可能是大量小IO請求導致的,可通過w/s和avgrq-sz進行辨別比較。
使用pika時,一定要設定max-write-buffer-size值,雖然和write-buffer-size引數名字很像,但兩者沒有聯動關係 且max-write-buffer-size起到了決定性作用。
最後,我們的應用成功遷移到pika,相比之前的mongo叢集節省了不少的機器資源開銷,可見沒有最好的DB,只有最適合的。