光使用內存作存儲很難知足這一點。可是沒有要求斷電不丟失,也就是說:可使用pageCache來作寫入緩存。 因此想到使用pageCache來充當數據和索引的寫入緩衝(二者策略不一樣)。 方案具體能夠參考ES的pageCache方案。數據庫
按照隨機寫,隨機讀,順序讀進行實驗。 隨機寫階段不須要在內存維護索引,能夠直接落盤。 隨機讀和順序讀,磁盤均存在數據,恢復索引能夠採用多線程併發恢復。數組
因爲採用了pageCache,採用腳本方式清空pageCache會比較耗時。 因此不能無節制的使用pageCache,因此準備引入Direct IO。緩存
因爲key能夠均勻分佈,採用數據分區方式,能夠大大減小順序讀寫的鎖衝突,key分佈均勻能夠按照key搞n位來作hash,能夠確保key兩個分區之間總體有序,因而能夠嘗試將數據分紅1024,2048個分區。多線程
隨機寫入key時,能夠根據key進行hash將隨機寫轉換成對應分區的文件順序寫。
架構
內存維護有序的 key[1024][625000] 數組和 offset[1024][625000] 數組。併發
利用數據分佈均勻特性,將全局數據hash爲1024分區,每一個分區存放兩類文件:索引文件和數據文件(對kafka熟悉的同窗,是否是很親切)。性能
在隨機寫入階段,根據key得到該數據對應分區位置,按照時序,順序追加到文件尾部,將全局隨機轉換爲局部順序。優化
利用索引和數據一一對應特性,不須要將數據的邏輯偏移落盤,在恢復階段能夠按照恢復key的測序,反推value的邏輯偏移量。線程
因爲作了分區,在range查詢階段,partition(N)中任何一個數據必定大於partition(N-1)中任何一個數據,因而咱們能夠大塊的讀,將一個partition總體讀進內存,給64個線程消費。架構設計
讀盤線程負責按分區讀盤進入內存,64個消費線程消費內存,按照key順序訪問內存,進行回調。
使用pageCache實現寫入緩衝區
磁盤IO類型的系統,第一步是測量磁盤IOPS及多少個線程一次讀寫多大的緩存可以打滿IO,在固定64線程寫入前提下,16kb,64kb都可達到理想IOPS,因此能夠爲每一個分區分配一個寫入緩存,湊齊4個value落盤。
因爲要求kill -9不丟失數據,不能簡單的在內存中分配一個ByteBuffer.allocate(4096*4);,能夠考慮mmap內存映射一片寫入緩衝,湊齊4個刷盤,這樣kill -9以後,pageCache不會丟失。
索引文件落盤比較簡單,key固定爲8b,因此mmap能夠發揮寫小數據的優點,將pageCache利用起來,mmap相比filechannel寫索引快3s左右。
隨機寫入後不會當即隨機讀,因此不須要在寫入時維護內存索引,只須要在恢復階段恢復索引順序,反推出數據的邏輯偏移,由於key和value在同一個分區的位置是一一對應的。
須要在數據庫引擎啓動時,將索引從數據文件恢復到內存中。
因爲有1024個分區,可使用64個線程併發恢復索引,使用快速排序對 key[1024][62500] 數組和 offset[1024][62500] 進行 sort,以後再 compact,對 key 進行去重。
根據key定位到分區,以後在有序的key數據中進行二分查找key/offset,拿到數據的邏輯偏移和分區編號,能夠隨機讀取了。
順序讀取思路是生產者消費者模型,n個生產者從磁盤讀數據放入內存,64個消費線程消費同時判斷內存數據以驗證數據。
堆外內存的好處是大大減小了一分內存拷貝,而且對gc友好。
- server - Xms2560m - Xmx2560m - XX : MaxDirectMemorySize = 1024m - XX : NewRatio = 4 - XX :+ UseConcMarkSweepGC - XX :+ UseParNewGC - XX :- UseBiasedLocking
對於那些須要反覆new出來的東西,均可以池化,分配內存在回收也是不小的開銷,直接可使用threadlocal緩存搞定。
io線程的切換成本很高,爲了減小io線程的時間片流失能夠考慮使用while(true)輪訓,也能夠採用sleep(1us)避免cpu空轉帶來的總體性能問題。
機器抖動在所不免,避免IO切換不能靠while(true),cpu級別的優化能夠專門騰出4個核心專門給IO線程使用,避免IO線程的時間片徵用(採用Affinity )。