leveldb(ssdb)性能、使用場景評估


  最近有個業務場景存儲壓力很大,寫遠遠大於讀,讀也集中在最近寫入,想想這不很適合采用leveldb存儲么。leveldb的話好像用ssdb比較多,花了兩天時間就ssdb簡單做下測試,以下總結。

  ssdb 是對leveldb存儲引擎的redis兼容協議封裝,並且實現了主從同步,源碼不多易讀。對於支持的操作,除了get/set KV存儲,由於 leveldb 是有序的,還可實現很多操作;通過scan遍歷的命令,利用有序性 list、hset也通過key+fileld/seq - val 方式存儲,ttl 會單獨存儲一個hset 保存過期時間,由一個單獨線程定時輪詢刪除。

  leveldb 參考:  

  http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html

  http://blog.csdn.net/houzengjiang/article/details/7718548

測試場景

機器:兩台R720XD E5-2620 2.1G (6核12線程)*2/內存128GB/300GB機械硬盤
數據: key: key 10位順序數字 value: 50字節
並發: 默認情況100連接並發
客戶端:  使用erlang erdis模塊get/set
 
配置:
leveldb:
        cache_size: 500
        block_size: 1
        write_buffer_size: 64
        compression: no
        max_open_files: 1000
  

數據量:2kw

- 文件大小:1.3GB
- 寫速度:7w/s  CPU 250%  mem:30M
- 隨機讀: 5.5w/s  CPU  100%  mem:1GB
- 並發讀寫1:10: 讀 5k/s 寫:5w/s,CPU 250%
總結:總體性能和github給出的相近;leveldb數據存放十分緊湊,因為會對key開啟前綴壓縮,如果開啟snappy后會更小,即使全量緩存到內存,內存消耗也會比redis 少很多。
 

數據量:1.5億

- 文件大小:9.6GB
- 寫速度:7w/s  cpu: 250%  mem:70M
- 隨機讀:  1.6w/s cpu:100% mem:70M
-並發讀寫:1: 10: 4k/s 讀  5w/s 寫 CPU: 250%
總結:讀太隨機的LRU cache 無法有效緩存任何數據,請求都要經過文件系統讀取,性能下降;但寫保性能持不變
 

數據量:10億

- 文件大小:66GB
- 寫速度:7w/s  cpu:250% mem:80M
- 隨機讀:  1.6w/s cpu:180% mem:80M
-並發讀寫:1: 10: 4k/s 讀:5w/s 寫:280%
總結:和1.5億級別效果保持一致
 

page cache

     leveldb默認緩存meta data、和8M的block;本次測試使用了500M block_cache,從測試效果看,因為隨機讀,cache只在2k數據級別上起到作用,且帶來很高性能提升。
     1.5億、10億時,完全依賴kernel 對文件系統的page cache,機器有128GB內存,leveldb 沒有使用direct io,文件都緩存,實際運行中不會有磁盤IO。
     那么使用腳本清理:     
     while true;  do echo 1 > /proc/sys/vm/drop_caches;echo clean cache ok; sleep 1; done
 
- 隨機讀 約160/s  cpu: 5%  mem: 120m  iostat 95%util
- 1並發隨機讀,100並發寫:90/s 讀, 1500/s 寫,隨機讀取影響寫入速度
總結:
     隨機IO在機械硬盤上是完全無解了,只能靠cache扛,相比page cache,block_cache 更有效,應該更加需求增加bock_cache。
     相比增加內存,使用ssd硬盤成本更低。
 
讀取系統調用:
open("./var/data/006090.ldb", O_RDONLY) = 27
stat("./var/data/006090.ldb", {st_mode=S_IFREG|0644, st_size=34349641, ...}) = 0
mmap(NULL, 34349641, PROT_READ, MAP_SHARED, 27, 0) = 0x7f0334f76000
madvise(0x7f040457e000, 737280, MADV_DONTNEED) = 0
munmap(0x7f03dc2ab000, 34349599)        = 0
close(27)                               = 0
 

多線程

ssdb 是多線程的,但上面測試效果看有明顯多核利用率很低問題,從源碼看可以知道:
- 1個主線程,負責網絡io
- 10個讀線程,負責像scan復雜操作讀
- 1個寫線程,負責寫操作磁盤io
- 1個leveldb 的compact線程
 
也就是:一個主線程負責網絡,一個寫線程負責leveldb操作;而讀 get 只主線程在工作。
 
ssdb相關都沒有配置,簡單修改源碼重新編譯:
 
- 10個線程處理讀:2.5/s  CPU 450%
     60%消耗在sys,高並發讀文件對內核瓶頸
- 減小至3個線程處理讀:3.2w/s 280%(相比10線程,更少的CPU消耗,更高的性能)
- 使用LRUCahce 在1kw區間隨機讀  7w/s ,200%CPU
 

可靠性

     leveldb 更新前先寫日志方式,但默認方式日志mmap是不會做msync,也就是完全依賴操作系統刷磁盤,這樣存在機器掉電等意外故障時可能會丟失部分最新消息。支持leveldb:WriteOptions.sync可選參數,但ssdb默認false,改為需要修改代碼。
     修改true后,奇怪性能無變化?代碼上看並不是每個寫都會msync,而是4kbuffer后刷一次。
     那么leveldb 在故障時可能丟時少量數量就是沒辦法的了,如需要強可靠需要注意。
 
     測測msync速度怎么樣呢?
     簡單單線程c程序,每寫100字節做一次msync,效果:
     服務器2w/s, 我的mac pro ssd 3w/s (此時ssd也沒太大優勢)
 
     msync 每次都做的話,肯定是有較大的性能影響的,但是可以做group msync;group  msync 會增加延時,就看可接受都少了,如0.1ms,那就就可以以1w/s 脈沖式、批量sync磁盤,保證所有請求都寫入磁盤再返回。
 

batch

     leveldb 的寫入、修改、刪除都是支持優化的batch操作,使用multi_set命令。
 

最佳實戰

1. 寫性能
     測試中,寫入速度一直維持7w/s, 可滿足到多需求, leveldb 寫入可以到40w/s, 這里受限於ssdb 線程模型無法利用更多的核心。需要的話通過pipline、網卡中斷平衡、提高網絡、leveldb寫線程數 來提高寫入性能。
2. 讀性能
     一般業務都存在數據熱點,可調整cache_size, block_size 提高緩存命中率,block_size 為緩存塊大小1K~4M視具體業務value而定。
     如果業務熱點度不高,那只能上ssd硬盤了。
     注意使用了page cache,不小心清空會讓性能急劇下降,還是盡量配置足夠大的cache_size。還有就是啟動有個預熱過程。
3. compaction
     本次使用的順序key寫入,因為業務上key 都是順序的,然后一段時間從后往前順序刪除。compaction影響會很小,如果業務大量隨機key寫入、修改、刪除會增加compaction量,需要注意另外做壓力測試。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM