大容量类Redis存储--Pika介绍
嘉賓介紹
大家好,首先自我介紹一下,我是360 web平臺(tái)-基礎(chǔ)架構(gòu)組的宋昭,負(fù)責(zé)大容量類redis存儲(chǔ)pika的和分布式存儲(chǔ)Bada的開發(fā)工作,這是我的github和博客地址,平時(shí)歡迎指正交流^^
我的github:https://github.com/KernelMaker
我的博客:http://kernelmaker.github.io
下面是pika的github,歡迎關(guān)注
https://github.com/Qihoo360/pika
Pika介紹
pika是360 DBA和基礎(chǔ)架構(gòu)組聯(lián)合開發(fā)的類redis存儲(chǔ)系統(tǒng), 使用Redis協(xié)議,兼容redis絕大多數(shù)命令(String,Hash,List,ZSet,Set),用戶不需要修改任何代碼, 就可以將服務(wù)遷移至pika.
pika主要是使用持久化存儲(chǔ)來解決redis在內(nèi)存占用超過50G,80G時(shí)遇到的如啟動(dòng)恢復(fù)時(shí)間長(zhǎng),主從同步代價(jià)大,硬件成本貴等問題,并且在對(duì)外用法上盡可能做到與redis一致,用戶基本上對(duì)后端是redis或pika無感知
既然pika要做到兼容redis并解決redis在大容量時(shí)的各種問題,那么首先要面對(duì)的問題便是如何從redis遷移到pika,畢竟現(xiàn)在redis的使用非常廣泛,如果從redis遷移到pika很麻煩,那應(yīng)該也不會(huì)有多少人用了
從redis遷移到pika需要經(jīng)過幾個(gè)步驟?
開發(fā)需要做的:
基本不用做任何事情
dba需要做的:
1.dba遷移redis數(shù)據(jù)到pika
2.dba將redis的數(shù)據(jù)實(shí)時(shí)同步到pika,確保redis與pika的數(shù)據(jù)始終一致
3.dba切換lvs后端ip,由pika替換redis
注:pika提供redis_to_pika工具,通過aof來將db和實(shí)時(shí)增量數(shù)據(jù)同步到pika
遷移過程中需要停業(yè)務(wù)/業(yè)務(wù)會(huì)受到影響嗎:
不會(huì)
注
由于pika的數(shù)據(jù)存在硬盤上,故單線程的性能肯定不如redis, 但pika使用多線程來盡可能的彌補(bǔ)數(shù)據(jù)讀寫性能較之redis內(nèi)存讀寫的差異, 線程數(shù)比較多的情況下, 某些數(shù)據(jù)結(jié)構(gòu)的性能會(huì)優(yōu)于redis
pika肯定不會(huì)是一個(gè)完全優(yōu)于redis的方案, 和redis相比也有弊端,只是在某些場(chǎng)景下面更適合. 所以目前公司內(nèi)部redis, pika 是共同存在的方案, DBA會(huì)根據(jù)業(yè)務(wù)的場(chǎng)景挑選合適的方案
本次分享分成6個(gè)部分
背景
為什么做pika(大容量redis遇到的問題)
pika架構(gòu)
pika的創(chuàng)新及優(yōu)化
pika的優(yōu)勢(shì)及不足
總結(jié)
背景
redis提供了豐富的多數(shù)據(jù)結(jié)構(gòu)的接口, 在redis之前, 比如memcache,都認(rèn)為后端只需要存儲(chǔ)kv的結(jié)構(gòu)就可以, 不需要感知這個(gè)value里面的內(nèi)容, 用戶需要使用的話通過json_encode,json_decode等形式進(jìn)行數(shù)據(jù)的讀取就行. 但是其實(shí)redis做了一個(gè)微創(chuàng)新, 提供了多數(shù)據(jù)結(jié)果的支持, 讓服務(wù)端寫代碼起來更加的方便了
因此redis在公司的使用率也是越來越廣泛, 用戶不知不覺把越來越多的數(shù)據(jù)存儲(chǔ)在redis中, 隨著用戶的使用, DBA發(fā)現(xiàn)有些redis實(shí)例的大小也是越來越大, 發(fā)現(xiàn)在redis實(shí)例內(nèi)存使用比較大的情況下, 遇到的問題也會(huì)越來越多, 因此和我們一起實(shí)現(xiàn)了大容量redis的解決方案
最近半年公司每天redis 的訪問情況
Imgur
redis 架構(gòu)方案
Imgur
為什么做pika(大容量redis遇到的問題)
恢復(fù)時(shí)間長(zhǎng)
我們線上的redis一般同時(shí)開啟rdb和aof. 我們知道aof的作用是實(shí)時(shí)的記錄用戶的寫入操作, rdb是redis某一時(shí)刻數(shù)據(jù)的完整快照. 那么恢復(fù)的時(shí)候一般是通過rdb+aof的方式進(jìn)行恢復(fù), 根據(jù)我們線上的情況50G redis恢復(fù)時(shí)間需要差不多40~70分鐘(取決于服務(wù)器性能)
一主多從, 主從切換代價(jià)大
redis在主庫掛掉以后, 從庫升級(jí)為新的主庫. 那么切換主庫以后, 所有的從庫都需要跟新主做一次全同步, 代價(jià)非常大
緩沖區(qū)寫滿問題
為了實(shí)現(xiàn)部分同步,redis使用了repl_backlog來緩存部分同步命令,repl_backlog默認(rèn)1M。 當(dāng)主從之間網(wǎng)絡(luò)有故障, 同步出現(xiàn)延遲了大于1M以后, slave丟失了master的同步點(diǎn),就會(huì)觸發(fā)全同步的過程. 如果多個(gè)從庫同時(shí)觸發(fā)全同步的過程, 在全同步的過程中,redis會(huì)將同步點(diǎn)之后的增量請(qǐng)求給每一個(gè)slave緩存一份,在寫入量大的情況下很容易就將主庫給拖死,當(dāng)然你也可以把repl_backlog調(diào)大來緩解,比如2G,不過對(duì)全內(nèi)存的redis而言,這2G的內(nèi)存代價(jià)也不小
內(nèi)存太貴
我們一般線上使用的redis機(jī)器是64G, 96G. 我們只會(huì)使用80%的空間.
如果一個(gè)redis的實(shí)例是50G, 那么基本一臺(tái)機(jī)器只能運(yùn)行一個(gè)redis實(shí)例. 因此特別的浪費(fèi)資源
總結(jié): 可以看到在redis比較小的情況下, 這些問題都不是問題, 但是當(dāng)redis容量上去以后. 很多操作需要的時(shí)間也就越來越長(zhǎng)了
pika 整體架構(gòu)
Imgur
主要組成:
1. 網(wǎng)絡(luò)模塊 pink
2. 線程模型
3. 存儲(chǔ)引擎 nemo
4. 日志模塊 binlog
5. 主從同步模塊
pink 網(wǎng)絡(luò)模塊
* 基礎(chǔ)架構(gòu)團(tuán)隊(duì)開發(fā)網(wǎng)絡(luò)編程框架, 支持pb, redis等等協(xié)議. 提供了對(duì)thread的封裝, 用戶定義不同thread的行為, 使用更加清晰
* 支持單線程模型, 多線程worker模型
* github 地址: https://github.com/baotiao/pink
線程模型
Imgur
pika使用的是多線程模型,使用多個(gè)工作線程來進(jìn)行讀寫操作,線程分為11種:
PikaServer:主線程
DispatchThread:監(jiān)聽端口1個(gè)端口,接收用戶連接請(qǐng)求
ClientWorker:存在多個(gè)(用戶配置),每個(gè)線程里有若干個(gè)用戶客戶端的連接,負(fù)責(zé)接收處理用戶命令并返回結(jié)果,每個(gè)線程執(zhí)行寫命令后,追加到binlog中
Trysync:嘗試與master建立首次連接,并在以后出現(xiàn)故障后發(fā)起重連
ReplicaSender:存在多個(gè)(動(dòng)態(tài)創(chuàng)建銷毀,本master節(jié)點(diǎn)掛多少個(gè)slave節(jié)點(diǎn)就有多少個(gè)),每個(gè)線程根據(jù)slave節(jié)點(diǎn)發(fā)來的同步偏移量,從binlog指定的偏移開始實(shí)時(shí)同步命令給slave節(jié)點(diǎn)
ReplicaReceiver:存在1個(gè)(動(dòng)態(tài)創(chuàng)建銷毀,一個(gè)slave節(jié)點(diǎn)同時(shí)只能有一個(gè)master),將用戶指定或當(dāng)前的偏移量發(fā)送給master節(jié)點(diǎn)并開始接收master實(shí)時(shí)發(fā)來的同步命令,在本地使用和master完全一致的偏移量來追加binlog,然后分發(fā)給多個(gè)BinlogBGWorker中的一個(gè)來執(zhí)行
BinlogBGWorker:存在多個(gè)(用戶配置),ReplicaReceiver將命令按key取hash分配給其中的一個(gè)BinlogBGWorker,它負(fù)責(zé)真正執(zhí)行命令
SlavePing:slave用來向master發(fā)送心跳進(jìn)行存活檢測(cè)
HeartBeat:master用來接收所有slave發(fā)送來的心跳并回復(fù)進(jìn)行存活檢測(cè)
bgsave:后臺(tái)dump線程
scan:后臺(tái)掃描keyspace線程
purge:后臺(tái)刪除binlog線程
存儲(chǔ)引擎 nemo
pika的存儲(chǔ)引擎是基于Rocksdb實(shí)現(xiàn)的. 封裝了String,Hash, List, ZSet, Set等數(shù)據(jù)結(jié)構(gòu)
我們知道redis是需要支持多數(shù)據(jù)結(jié)構(gòu)的, 而rocksdb只是一個(gè)kv的接口, 那么我們?nèi)绾螌?shí)現(xiàn)的呢?
比如對(duì)于Hash數(shù)據(jù)結(jié)構(gòu):
對(duì)于每一個(gè)Hash存儲(chǔ),它包括hash鍵(key),hash鍵下的域名(field)和存儲(chǔ)的值 (value).
nemo的存儲(chǔ)方式是將key和field組合成為一個(gè)新的key,將這個(gè)新生成的key與所要存儲(chǔ)的value組成最終落盤的kv鍵值對(duì)。同時(shí),對(duì)于每一個(gè)hash鍵,nemo還為它添加了一個(gè)存儲(chǔ)元信息的落盤kv,它保存的是對(duì)應(yīng)hash鍵下的所有域值對(duì)的個(gè)數(shù)。
每個(gè)hash鍵、field、value到落盤kv的映射轉(zhuǎn)換
Imgur
每個(gè)hash鍵的元信息的落盤kv的存儲(chǔ)格式
Imgur
比如對(duì)于List 數(shù)據(jù)結(jié)構(gòu):
顧名思義,每個(gè)List結(jié)構(gòu)的底層存儲(chǔ)也是采用鏈表結(jié)構(gòu)來完成的。對(duì)于每個(gè)List鍵,它的每個(gè)元素都落盤為一個(gè)kv鍵值對(duì),作為一個(gè)鏈表的一個(gè)節(jié)點(diǎn),稱為元素節(jié)點(diǎn)。和hash一樣,每個(gè)List鍵也擁有自己的元信息。
每個(gè)元素節(jié)點(diǎn)對(duì)應(yīng)的落盤kv存儲(chǔ)格式
Imgur
每個(gè)元信息的落盤kv的存儲(chǔ)格式
Imgur
其他的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的方式也類似, 通過將數(shù)據(jù)結(jié)構(gòu)拆分為一個(gè)個(gè)獨(dú)立的KV, 存儲(chǔ)到rocksdb 里面去. 從而實(shí)現(xiàn)多數(shù)據(jù)結(jié)構(gòu)的結(jié)構(gòu)
日志模塊 binlog
pika的主從同步是使用Binlog來完成的.
binlog 本質(zhì)是順序?qū)懳募? 通過Index + offset 進(jìn)行同步點(diǎn)檢查.
解決了同步緩沖區(qū)太小的問題
支持全同步 + 增量同步
master執(zhí)行完一條寫命令就將命令追加到Binlog中,ReplicaSender將這條命令從Binlog中讀出來發(fā)送給slave,slave的ReplicaReceiver收到該命令,執(zhí)行,并追加到自己的Binlog中.
當(dāng)發(fā)生網(wǎng)絡(luò)閃斷或slave掛掉重啟時(shí), slave僅需要將自己當(dāng)前的Binlog Index + offset 發(fā)送給master,master找到后從該偏移量開始同步后續(xù)命令
為了防止讀文件中寫錯(cuò)一個(gè)字節(jié)則導(dǎo)致整個(gè)文件不可用,所以pika采用了類似leveldb log的格式來進(jìn)行存儲(chǔ),具體如下:
Imgur
主從同步模塊
Imgur
上圖是一個(gè)主從同步的一個(gè)過程(即根據(jù)主節(jié)點(diǎn)數(shù)據(jù)庫的操作日志,將主節(jié)點(diǎn)數(shù)據(jù)庫的改動(dòng)同步到從節(jié)點(diǎn)的數(shù)據(jù)庫上),從圖中可以看出,每一個(gè)從節(jié)點(diǎn)在主節(jié)點(diǎn)下都有一個(gè)唯一對(duì)應(yīng)的BinlogSenderThread
主要模塊:
WorkerThread:接受和處理用戶的命令;
BinlogSenderThread:負(fù)責(zé)順序地向?qū)?yīng)的從節(jié)點(diǎn)發(fā)送在需要同步的命令;
BinlogReceiverModule: 負(fù)責(zé)接受主節(jié)點(diǎn)發(fā)送過來的同步命令
Binglog:用于順序的記錄需要同步的命令
主要的工作過程:
1.當(dāng)WorkerThread接收到客戶端的命令,按照?qǐng)?zhí)行順序,添加到Binlog里;
2.BinglogSenderThread判斷它所負(fù)責(zé)的從節(jié)點(diǎn)在主節(jié)點(diǎn)的Binlog里是否有需要同步的命令,若有則發(fā)送給從節(jié)點(diǎn);
3.BinglogReceiverModule模塊則做以下三件事情:
a. 接收主節(jié)點(diǎn)的BinlogSenderThread發(fā)送過來的同步命令;
b. 把接收到的命令應(yīng)用到本地的數(shù)據(jù)上;
c. 把接收到的命令添加到本地Binlog里
至此,一條命令從主節(jié)點(diǎn)到從節(jié)點(diǎn)的同步過程完成
BinLogReceiverModule的工作過程:
Imgur
上圖是BinLogReceiverModule的組成,從圖中可以看出BinlogReceiverModule由一個(gè)BinlogReceiverThread和多個(gè)BinlogBGWorker組成。
BinlogReceiverThread:負(fù)責(zé)接受由主節(jié)點(diǎn)傳送過來的命令,并分發(fā)給各個(gè)BinlogBGWorker,若當(dāng)前的節(jié)點(diǎn)是只讀狀態(tài)(不能接受客戶端的同步命令),則在這個(gè)階段寫B(tài)inlog
BinlogBGWorker:負(fù)責(zé)執(zhí)行同步命令;若該節(jié)點(diǎn)不是只讀狀態(tài)(還能接受客戶端的同步命令),則在這個(gè)階段寫B(tài)inlog(在命令執(zhí)行之前寫)
BinlogReceiverThread接收到一個(gè)同步命令后,它會(huì)給這個(gè)命令賦予一個(gè)唯一的序列號(hào)(這個(gè)序列號(hào)是遞增的),并把它分發(fā)給一個(gè)BinlogBGWorker;而各個(gè)BinlogBGWorker則會(huì)根據(jù)各個(gè)命令的所對(duì)應(yīng)的序列號(hào)的順序來執(zhí)行各個(gè)命令,這樣也就保證了命令執(zhí)行的順序和主節(jié)點(diǎn)執(zhí)行的順序一致了
之所以這么設(shè)計(jì)主要原因是:
配備多個(gè)BinlogBGWorker是可以提高主從同步的效率,減少主從同步的滯后延遲;
讓BinlogBGWorker在執(zhí)行執(zhí)行之前寫B(tài)inlog可以提高命令執(zhí)行的并行度;
在當(dāng)前節(jié)點(diǎn)是非只讀狀態(tài),讓BinglogReceiverThread來寫B(tài)inlog,是為了讓Binglog里保存的命令順序和命令的執(zhí)行順序保持一致;
綜上所述,正是因?yàn)檫@樣的架構(gòu)及實(shí)現(xiàn),pika可以較好的解決上面說到redis在大數(shù)據(jù)量下的不足:
恢復(fù)時(shí)間長(zhǎng)
pika的存儲(chǔ)引擎是nemo, nemo使用的是rocksdb, rocksdb啟動(dòng)不需要加載全部數(shù)據(jù), 只需要加載recover log文件就可以啟動(dòng), 因此恢復(fù)時(shí)間非常快
一主多從, 主從切換代價(jià)大
在主從切換的時(shí)候, 新主確定以后, 從庫會(huì)用當(dāng)前的偏移量嘗試與新主做一次部分同步, 如果部分同步不成功才做全同步. 這樣盡可能的減少全同步次數(shù)
緩沖區(qū)寫滿問題
pika不是用內(nèi)存buffer進(jìn)行同步數(shù)據(jù)的緩存, 而是記錄在本地的binlog上, binlog的大小可配,遠(yuǎn)遠(yuǎn)大于內(nèi)存可以使用的上限,因此不會(huì)出現(xiàn)把緩沖區(qū)寫滿的問題,減少無用的全同步次數(shù)
內(nèi)存昂貴問題
pika的存儲(chǔ)引擎nemo使用的是rocksdb, rocksdb同時(shí)使用內(nèi)存和磁盤減少對(duì)內(nèi)存的依賴. 同時(shí)我們盡可能使用SSD盤來存放數(shù)據(jù), 盡可能跟上redis的性能.
pika的創(chuàng)新及優(yōu)化
多數(shù)據(jù)結(jié)構(gòu)key的快速刪除
以Hash為例,redis一個(gè)Hash key可能包含百萬或者千萬的field,對(duì)于Hash key的刪除,redis首先從db dict中刪除掉這個(gè)key,然后立刻返回,該key對(duì)應(yīng)的hash空間采用惰性的方式來慢慢回收,而我們知道,pika的是將Hash結(jié)構(gòu)轉(zhuǎn)換成一個(gè)個(gè)KV來存儲(chǔ)的,刪除一個(gè)Hash Key就等于要?jiǎng)h除其對(duì)應(yīng)的千萬field,此時(shí)用戶的一個(gè)del操作等價(jià)于引擎千萬次的del操作,當(dāng)初做的時(shí)候我們有如下考量:
Solution 1:阻塞刪除,這也是目前其他類似Pika項(xiàng)目的主要解決方案,直到把Hash key對(duì)應(yīng)的所有field key全部刪除才返回給用戶
優(yōu)點(diǎn):易實(shí)現(xiàn)
缺點(diǎn):阻塞處理,影響服務(wù)
Solution 2:刪除meta key之后立刻返回,其他的field key后臺(tái)起線程慢慢刪除
優(yōu)點(diǎn):速度快
缺點(diǎn):使用場(chǎng)景受限,如果用戶刪除某個(gè)Hash key之后又立刻插入這個(gè)key,則此時(shí)還未刪除的field key會(huì)被無當(dāng)做新key的一部分,出錯(cuò)
上述兩種方案皆不可行,我們最終在rocksdb上做了改動(dòng),使它支持多數(shù)據(jù)結(jié)構(gòu)版本的概念
最終解決方案:
Hash Key的元信息增加版本,表示當(dāng)前key的有效版本;
操作:
Put:查詢?cè)畔ⅲ@得key的最新版本,后綴到val;
Get:查詢?cè)畔ⅲ@得key的最新版本,過濾低版本的數(shù)據(jù);
Del:key的元信息版本號(hào)+1即可;
Iterator: 迭代時(shí),查詢key的版本,過濾舊版本數(shù)據(jù);
Compact:數(shù)據(jù)的實(shí)際刪除是在Compact過程中,根據(jù)版本信息過濾;
通過對(duì)rocksdb的修改,pika實(shí)現(xiàn)了對(duì)多數(shù)據(jù)結(jié)構(gòu)key的秒刪功能,并且將真正的刪除操作交給了compact來減少顯示調(diào)用引擎del造成的多次操作(插入del record及compact)
快照式備份
不同于Redis,Pika的數(shù)據(jù)主要存儲(chǔ)在磁盤中,這就使得其在做數(shù)據(jù)備份時(shí)有天然的優(yōu)勢(shì),可以直接通過文件拷貝實(shí)現(xiàn)
流程:
打快照:阻寫,并在這個(gè)過程中或的快照內(nèi)容
異步線程拷貝文件:通過修改Rocksdb提供的BackupEngine拷貝快照中文件,這個(gè)過程中會(huì)阻止文件的刪除
Imgur
這樣的備份速度基本等同于cp的速度,降低了備份的代價(jià)
后續(xù)優(yōu)化:不過目前pika正在嘗試使用硬鏈建立checkpoint來實(shí)現(xiàn)數(shù)據(jù)的更快備份(秒級(jí)),并且減少備份數(shù)據(jù)的空間占用(從之前的2倍優(yōu)化到不到2倍),更好的支持超大容量存儲(chǔ)
過期支持
redis的過期是通過將需要過期的key在多存一份,記錄它的過期時(shí)間,然后每次讀取時(shí)進(jìn)行比較來完成的,這樣的實(shí)現(xiàn)簡(jiǎn)單,但基于內(nèi)存的讀寫都很快不會(huì)有性能問題,目前其他類似pika的開源項(xiàng)目也采用這樣的方式,將過期key在db多存一份,不過不同于redis,這些項(xiàng)目的db也是落盤,采用這樣簡(jiǎn)單粗暴的方式無形中又多了一次磁盤讀,影響效率,那么pika是如何解決的呢?
pika通過給修改rocksdb Set、Get接口并且新增compact filter,給每個(gè)value增加ttl后綴,并且在Get的時(shí)候來進(jìn)行過濾,將真正的過期刪除交給compact(基于ttl來Drop),在磁盤大容量的前提下,使用額外空間來減少磁盤讀取次數(shù),提高效率
空間回收
rocksdb 默認(rèn)的compact 策略是在寫放大, 讀放大, 空間放大的權(quán)衡. 那么DBA同學(xué)當(dāng)然希望盡可能減少空間的使用, 因此DBA希望能夠隨時(shí)觸發(fā)compact, 而又盡可能的不影響線上的使用, 而rocksdb 默認(rèn)的手動(dòng)compact 策略是最高優(yōu)先級(jí)的, 會(huì)阻塞線上的正常流程的合并, 因此我們修改了rocksdb compact的部分邏輯,低優(yōu)先級(jí)手動(dòng)compact優(yōu)先級(jí),使得自動(dòng)compact可以打斷手動(dòng)compact,來避免level 0文件數(shù)量過多而造成的rocksdb主動(dòng)停寫. pika支持DBA隨時(shí)compact
方便的運(yùn)維
pika較之其他類似開源項(xiàng)目,還有一個(gè)優(yōu)勢(shì)就是它可以方便的運(yùn)維,例如
1. pika的binlog可以配置按個(gè)數(shù)或者按天來刪除,提供工具來支持不用再啟實(shí)例來進(jìn)行指定節(jié)點(diǎn)binlog的實(shí)時(shí)備份,支持binlog恢復(fù)數(shù)據(jù)到指定某一秒(正在做)
2. 支持info命令來查看后臺(tái)任務(wù)的執(zhí)行狀態(tài)(bgsave,purgelogs,keyscan)
3. 支持monitor
4. 支持通過redis aof和monitor來遷移數(shù)據(jù)
5. 支持config set來動(dòng)態(tài)修改配置項(xiàng)
6. 支持多用戶(admin及普通用戶)及命令黑名單,可以禁止掉不想讓普通用戶使用的命令
7. 支持不活躍客戶端的自動(dòng)刪除,支持慢日志,client kill all,readonly開關(guān)
8. 支持快照式備份及手動(dòng)compact
9. 等等...
pika在追求盡可能高的性能及穩(wěn)定性的同時(shí),還注重使用者的使用體驗(yàn),一個(gè)產(chǎn)品即使擁有再給力的性能如果不可運(yùn)維我想也不會(huì)有人想用,所以pika會(huì)不斷發(fā)現(xiàn)并解決使用上的問題,使得它更好用
pika的優(yōu)勢(shì)及不足
pika相對(duì)于redis,最大的不同就是pika是持久化存儲(chǔ),數(shù)據(jù)存在磁盤上,而redis是內(nèi)存存儲(chǔ),由此不同也給pika帶來了相對(duì)于redis的優(yōu)勢(shì)和劣勢(shì)
優(yōu)勢(shì):
容量大:Pika沒有Redis的內(nèi)存限制, 最大使用空間等于磁盤空間的大小
加載db速度快:Pika 在寫入的時(shí)候, 數(shù)據(jù)是落盤的, 所以即使節(jié)點(diǎn)掛了, 不需要rdb或者aof,pika 重啟不用重新加載數(shù)據(jù)到內(nèi)存而是直接使用已經(jīng)持久化在磁盤上的數(shù)據(jù), 不需要任何數(shù)據(jù)回放操作(除去少量rocksdb自身的recover),這大大降低了重啟成本。
備份速度快:Pika備份的速度大致等同于cp的速度(拷貝數(shù)據(jù)文件后還有一個(gè)快照的恢復(fù)過程,會(huì)花費(fèi)一些時(shí)間),目前已經(jīng)開發(fā)完更快更省空間的秒級(jí)備份,即將投入使用,這樣在對(duì)于百G大庫的備份是快捷的,更快的備份速度更好的解決了主從的全同步問題
劣勢(shì):
由于Pika是基于內(nèi)存和文件來存放數(shù)據(jù), 所以性能肯定比Redis低一些, 但是我們一般使用SSD盤來存放數(shù)據(jù), 盡可能跟上Redis的性能。
總結(jié)
如果用戶的業(yè)務(wù)場(chǎng)景數(shù)據(jù)比較大,Redis會(huì)出現(xiàn)上面說到的那些問題,如果這些問題對(duì)用戶來說不可容忍,那么可以考慮使用pika。
我們對(duì)pika整體進(jìn)行了性能測(cè)試,結(jié)果如下:
服務(wù)端配置:
處理器:24核 Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz
內(nèi)存:165157944 kB
操作系統(tǒng):CentOS release 6.2 (Final)
網(wǎng)卡:Intel Corporation I350 Gigabit Network Connection
客戶端配置:
同服務(wù)端
測(cè)試結(jié)果:
pika配置18個(gè)worker,用40個(gè)客戶端;
1. 寫性能:
方法:客戶端依次執(zhí)行set、hset、lpush、zadd、sadd接口寫入數(shù)據(jù),每個(gè)數(shù)據(jù)結(jié)構(gòu)10000個(gè)key;
結(jié)果:qps 110000
2. 讀性能:
方法:客戶端一次執(zhí)行g(shù)et、hget、lindex、zscore、smembers,每個(gè)數(shù)據(jù)結(jié)構(gòu)5000000個(gè)key;
結(jié)果:qps 170000
單數(shù)據(jù)結(jié)構(gòu)性能用戶可以依據(jù)自己的需求來測(cè)試,數(shù)據(jù)結(jié)構(gòu)間的性能比較大致是:
String > Hash = Set > ZSet > List
在實(shí)際使用中,大多數(shù)場(chǎng)景下pika的性能大約是Redis的50%~80%,在某些特定場(chǎng)景下,例如range 500,pika的性能只有redis的20%,針對(duì)這些場(chǎng)景我們?nèi)匀辉诟倪M(jìn)
在360內(nèi)部使用情況:
粗略的統(tǒng)計(jì)如下:
實(shí)例數(shù)160個(gè)
當(dāng)前每天承載的總請(qǐng)求量超過100億
當(dāng)前承載的數(shù)據(jù)總量約3TB
wiki
github 地址:
https://github.com/Qihoo360/pika
github wiki:
https://github.com/Qihoo360/pika/wiki/pika介紹
Q&A
Q1:元信息是跟key一起存儲(chǔ)的嗎?
A1:是的
Q2:快照的生成依賴于什么?會(huì)阻塞讀寫嗎?
A2:快照是后臺(tái)生成的,阻塞的地方只是在最開始取當(dāng)前db狀態(tài)和同步點(diǎn)信息的時(shí)候,非常短
Q3:為什么能做到基本上不阻塞?
A3:因?yàn)閜ika的數(shù)據(jù)本來就是存在磁盤上的,備份就等同于文件拷貝.只要在拷貝前計(jì)算一下該拷貝那些文件,然后就可以后臺(tái)搞了
Q4:看hashset的實(shí)現(xiàn),只記錄那些信息,是怎么處理hgetall的
A4:比如hash有5個(gè)field,那么在pika存儲(chǔ),除了元信息之外,真正數(shù)據(jù)是這么存的:key+field1 -> value1,key+field2-> value2等等,在hgetall的時(shí)候,只需要通過rocksdb的iterator,seek到key,然后迭代便可取出所有的field了
Q5:當(dāng)存儲(chǔ)超過SSD空間后,怎么擴(kuò)容呢?
A5: pika受制于底下引擎的限制,不支持?jǐn)U容,如果連ssd都不夠用了,就只能靠掛lvm來解決了
Q6:那新的數(shù)據(jù)如何融合到db里
A6:只需要給新的實(shí)例啟動(dòng)前,配置文件db路徑為備份的目錄,然后啟動(dòng)新實(shí)例即可
Q7:依賴于rocksdb_backup機(jī)制?
A7:是的,目前是基于rocksdb_backup做的,不過我們最新的秒備份已經(jīng)差不多做完了,可以直接通過硬鏈接省去文件的拷貝,以達(dá)到更快的備份速度及更少的空間占用
Q8:dispatch 和 clientWorker 這種生產(chǎn)者消費(fèi)者模型對(duì)性能的影響。
A8:clientworker之間是互不影響的,dispatch在把某個(gè)連接分配給其中一個(gè)worker時(shí)僅與worker有極小概率的沖突(dispatch已分發(fā)完一輪連接,又回到這個(gè)worker,并且該worker此時(shí)也在操作自己的任務(wù)隊(duì)列),這樣的模型是比較通用的做法(與MC類似),性能瓶頸不會(huì)出現(xiàn)在這里
Q9:對(duì)hash類型的讀取、修改、增加、刪除任意部分field的過程及性能。
A9:性能比string接口稍有下降,因?yàn)槎嗔艘粋€(gè)元信息的讀寫,過程的話無非就是先讀元信息,在讀寫真正的數(shù)據(jù)等等,說起來比較多,感興趣的話可以下來交流^^
Q10:binlog有對(duì)操作做可重入處理嗎
A10:binlog里記錄的就是用戶發(fā)來的redis命令,所以不是冪等的
Q11:存放元數(shù)據(jù)的那個(gè)key如何不成為瓶頸
A11: 這樣的設(shè)計(jì)元信息的key的確會(huì)成為瓶頸,不過對(duì)于比較熱的元數(shù)據(jù)key,頻繁更新會(huì)讓他駐留在rocksdb的memtable中,這樣可以彌補(bǔ)一下讀寫性能
Q12:請(qǐng)問講師使用的是redis cluster嗎?為何只有一個(gè)master并且50g那么大?在截圖那個(gè)qps下,會(huì)遇到什么瓶頸?
A12: 目前在公司redis cluster的應(yīng)用不是很廣泛,所以大的業(yè)務(wù)很容易將redis內(nèi)存撐到50G,截圖的qps,根據(jù)上面的介紹,性能梯度是String>Hash=Set>ZSet>List,之所以可以到達(dá)10w+的qps,也是因?yàn)镾tring,Hash,Set這樣的接口操作和redis相比差不多,在worker數(shù)多的情況下,String甚至還高于redis,所以整體上看qps還不錯(cuò),不過對(duì)于List這樣的數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)中就是基于kv來做的list,所以性能低于前面的數(shù)據(jù)結(jié)構(gòu)
Q13:在多線程情況下,對(duì)事務(wù)的處理是怎么實(shí)現(xiàn)的
A13:pika上層會(huì)對(duì)寫操作加行鎖,來確保對(duì)同一個(gè)key的寫db和寫binlog不被打斷
Q14:hashset每次hset都要讀取count值嗎
A14:是的,因?yàn)榘姹咎?hào)的原因,每一次都需要讀取元信息
Q15:binlog可以理解為redis的aof文件么?存儲(chǔ)的都是命令日志記錄?
A15:是的,和aof差不多,存儲(chǔ)的都是用戶命令,不過除了做aof的功能,在主從同步中它也承擔(dān)了redis中repl_backlog的功能
總結(jié)
以上是生活随笔為你收集整理的大容量类Redis存储--Pika介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CISCO ACL配置全解
- 下一篇: 杨元庆:ChatGPT为例的AIGC将带