深入理解缓存系统|单机QPS突破千万优化之路
hi,大家好,今天給大家分享一篇,如何設(shè)計(jì)和優(yōu)化緩存(類redis)系統(tǒng),希望大家了解真正落地的架構(gòu)設(shè)計(jì)方案,類別redis和memcached,了解架構(gòu)是如何演進(jìn)的,學(xué)習(xí)落地經(jīng)驗(yàn)。
CKV(也稱為CKV+)是騰訊新一代自研高性能NoSQL KV數(shù)據(jù)庫(kù),兼容Redis、Memcached、ASN協(xié)議,具備高性能、低延時(shí)、低成本的優(yōu)勢(shì),能夠應(yīng)對(duì)海量數(shù)據(jù)訪問(wèn)、存儲(chǔ)成本敏感、延時(shí)敏感等問(wèn)題。
CKV作為公司應(yīng)用最廣泛的NoSQL存儲(chǔ)系統(tǒng),已接入包括廣點(diǎn)通、信鴿、QQ音樂(lè)、財(cái)付通、微視、看點(diǎn)等業(yè)務(wù)。
隨著業(yè)務(wù)規(guī)模的增長(zhǎng),成本和性能是用戶最關(guān)注的問(wèn)題,為了進(jìn)一步降低用戶的成本并提升性能,我們分別從性能和成本兩個(gè)方面對(duì)CKV進(jìn)行了優(yōu)化。
下面先回顧C(jī)KV的架構(gòu)演進(jìn),然后分析當(dāng)前架構(gòu)的性能瓶頸,重點(diǎn)介紹性能方面的優(yōu)化,并在性能上與開(kāi)源Redis和友商競(jìng)品做對(duì)比。
一、CKV的架構(gòu)演進(jìn)
CKV作為公司有歷史的分布式內(nèi)存系統(tǒng),下面羅列了一些重要的發(fā)展經(jīng)歷。
2009年TMEM(tencent memcached)作為高并發(fā)內(nèi)存級(jí)存儲(chǔ)解決方案上線,支撐了當(dāng)時(shí)的QZone/朋友網(wǎng)等業(yè)務(wù)。
2011年更名為CMEM(cloud memcached),開(kāi)始向云計(jì)算靠攏并為海量的第三方游戲用戶提供了解決方案,比如偷菜,搶車位等。
2012年隨著業(yè)務(wù)規(guī)模的增加,內(nèi)存的高成本成為了核心問(wèn)題,通過(guò)引入冷數(shù)據(jù)存儲(chǔ)介質(zhì)降低了業(yè)務(wù)的使用成本。
2014年CKV支持了微信紅包業(yè)務(wù)。
2017年基于seastar框架開(kāi)發(fā)的CKV上線,兼容了REDIS協(xié)議,進(jìn)一步提升了性能。
1.1 CMEM架構(gòu)
1.1.1 架構(gòu)總覽
CMem的模塊眾多,按照模塊功能分為核心模塊和外圍模塊。
核心模塊采用的是傳統(tǒng)的三層架構(gòu),包括接入、存儲(chǔ)和元數(shù)據(jù)(master)模塊三部分。元數(shù)據(jù)(master)模塊負(fù)責(zé)管理路由等元數(shù)據(jù),元數(shù)據(jù)是本地文件存儲(chǔ),無(wú)備份節(jié)點(diǎn)。業(yè)務(wù)直接訪問(wèn)的是接入模塊,接入模塊會(huì)根據(jù)路由轉(zhuǎn)發(fā)到后端的存儲(chǔ)模塊。
外圍模塊包括備份、搬遷、統(tǒng)計(jì)、探測(cè)、恢復(fù)等模塊。
CMem對(duì)業(yè)務(wù)數(shù)據(jù)通過(guò)Presharding的方式進(jìn)行分片,將所有數(shù)據(jù)按照 hash(key) % 10000的方式邏輯上分成10000個(gè)Slot,每個(gè)Slot會(huì)對(duì)應(yīng)存儲(chǔ)節(jié)點(diǎn)的一對(duì)主備分片,存儲(chǔ)端采用主備的方式來(lái)容災(zāi)。
1.1.2 架構(gòu)優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
底層數(shù)據(jù)存儲(chǔ)采用共享內(nèi)存,在進(jìn)程異常的時(shí)候可以快速恢復(fù)。引擎設(shè)計(jì)上對(duì)于用戶數(shù)據(jù)長(zhǎng)度比較一致的業(yè)務(wù)上,可以充分利用內(nèi)存。
支持開(kāi)源memcached協(xié)議,可以直接用memcached客戶端訪問(wèn)。
搬遷對(duì)用戶影響很小,只影響搬遷中的刪除操作。
多租戶方式,可以充分利用資源。
缺點(diǎn)
模塊較多,出現(xiàn)異常定位問(wèn)題鏈路較長(zhǎng)。
存儲(chǔ)模型采用整機(jī)主備的設(shè)計(jì),一個(gè)機(jī)器都是主,一個(gè)機(jī)器都是備,造成主機(jī)壓力較大,而備機(jī)相對(duì)比較閑,而且只支持一主一備。
主備同步用的rsync進(jìn)程,是單進(jìn)程單線程模型。在訪問(wèn)量較大的場(chǎng)景下,容易成為瓶頸。
引擎設(shè)計(jì)對(duì)于隨機(jī)用戶數(shù)據(jù)長(zhǎng)度上不太友好。
存儲(chǔ)模塊采用多進(jìn)程加鎖的方式,不能充分利用資源。進(jìn)程太少,能夠利用的CPU就少,進(jìn)程太多,鎖的開(kāi)銷就增大。
元數(shù)據(jù)管理模塊采用的本地存儲(chǔ),并且是單點(diǎn)。元數(shù)據(jù)推送采用全量推送,效率較低。
1.2 CKV架構(gòu)
1.2.1 架構(gòu)總覽
上圖中灰色部分是從CMem的架構(gòu)中移除的模塊,只保留了一些必要的模塊(藍(lán)色部分)。從功能上也分為核心和外圍模塊。
核心模塊采用的是二層架構(gòu),包括存儲(chǔ)和元數(shù)據(jù)管理兩個(gè)模塊。元數(shù)據(jù)是保存在本地的ETCD服務(wù)中,采用ETCD服務(wù)于元數(shù)據(jù)服務(wù)1對(duì)1模式,至少是三分?jǐn)?shù)據(jù),提升了元數(shù)據(jù)的可靠性。
存儲(chǔ)模塊兼顧接入的能力,業(yè)務(wù)直接訪問(wèn)存儲(chǔ)模塊,如果根據(jù)路由計(jì)算出是本機(jī)處理就直接處理,非本機(jī)請(qǐng)求就轉(zhuǎn)發(fā)到目標(biāo)節(jié)點(diǎn)處理。
外圍模塊主要包括備份和OSS。備份負(fù)責(zé)數(shù)據(jù)的冷備與恢復(fù),OSS負(fù)責(zé)管理整個(gè)集群,包括實(shí)例的創(chuàng)建/刪除/備份/擴(kuò)縮容等。
1.2.2 核心架構(gòu)
CKV從設(shè)計(jì)上為了適應(yīng)新型的多CPU大內(nèi)存機(jī)型,采用多租戶方式,底層引擎采用共享內(nèi)存,為了兼容REDIS協(xié)議,設(shè)計(jì)支持了string/list/hash/set/zset等數(shù)據(jù)結(jié)構(gòu)。
存儲(chǔ)節(jié)點(diǎn)采用單進(jìn)程多線程模型,實(shí)際存儲(chǔ)分片按需分配,一個(gè)節(jié)點(diǎn)上盡量保證主備數(shù)量均衡,同時(shí)存儲(chǔ)兼有轉(zhuǎn)發(fā)的能力,也支持獨(dú)立部署接入層的能力。
元數(shù)據(jù)管理上基于ETCD保證元數(shù)據(jù)的可靠性,路由設(shè)計(jì)上采用增量推送的方式,提高了路由更新的效率。數(shù)據(jù)模型上跟CMem類似,采用Presharding,hash(key) % 16384,Slot數(shù)從10000增加到了16384,跟Redis保持一致,并且支持一主多備。
1.2.3 線程模型
CKV的Cache作為核心的存儲(chǔ)模塊,在設(shè)計(jì)的時(shí)候,性能是我們最看重的,同時(shí)為了充分利用當(dāng)前多CPU大內(nèi)存的機(jī)型資源,我們基于Seastar這個(gè)高性能網(wǎng)絡(luò)通信框架來(lái)開(kāi)發(fā)。
http://seastar.io/
Seastar是一個(gè)面向現(xiàn)代硬件多核架構(gòu)的高性能異步網(wǎng)絡(luò)框架,它是KVM作者Avi Kivity領(lǐng)銜開(kāi)發(fā)的開(kāi)源項(xiàng)目。Seastar采用Share-nothing架構(gòu)、全異步編程、用戶態(tài)任務(wù)調(diào)度器、支持內(nèi)核協(xié)議棧和用戶態(tài)協(xié)議棧、獨(dú)立的內(nèi)存管理等提供了極致的性能。
Cache的設(shè)計(jì)采用對(duì)等模式,每個(gè)CPU可以理解為一個(gè)線程,所有線程的功能可以認(rèn)為是一樣的,不區(qū)分IO線程和worker線程。
CKV的最小存儲(chǔ)單元是分片,每個(gè)分片是獨(dú)立的共享內(nèi)存,采用按需分配的方式。CKV存儲(chǔ)訪問(wèn)采用無(wú)鎖設(shè)計(jì),每個(gè)分片只會(huì)由一個(gè)CPU來(lái)管理,這樣訪問(wèn)就不需要加鎖。
在上圖中可以看到,一共有四個(gè)線程,其中第一個(gè)線程管理兩個(gè)分片,第二個(gè)線程管理三個(gè)分片,后面兩個(gè)線程分別只管理一個(gè)分片。
每個(gè)線程接收到請(qǐng)求之后,會(huì)根據(jù)路由計(jì)算是不是當(dāng)前線程處理,如果不是的話,會(huì)轉(zhuǎn)發(fā)到其他線程來(lái)執(zhí)行請(qǐng)求。
同時(shí)每個(gè)線程都可以處理網(wǎng)絡(luò)IO,一旦接受到數(shù)據(jù)包,在當(dāng)前核完成編解碼的工作,然后會(huì)根據(jù)路由將請(qǐng)求轉(zhuǎn)發(fā)到指定的線程或者其他節(jié)點(diǎn)運(yùn)行。這樣就把網(wǎng)絡(luò)IO和編解碼的工作分?jǐn)偟剿芯€程,可以充分利用CPU資源。
Cache上大部分操作都是基于future/promise實(shí)現(xiàn)的異步,包括邏輯處理、網(wǎng)絡(luò)IO和磁盤(pán)IO等,只有底層存儲(chǔ)引擎是同步操作。這樣的好處是所有的IO不需要等待,可以讓出CPU,提高吞吐量。
2、性能瓶頸及分析
2.1 性能數(shù)據(jù)
基于Seastar框架開(kāi)發(fā)的CKV整機(jī)測(cè)試性能數(shù)據(jù)。
2.2 CKV性能分析
前面已經(jīng)給出了CKV的性能數(shù)據(jù),雖然相對(duì)CMem來(lái)說(shuō),性能提升了不少,為了進(jìn)一步提高性能,我們對(duì)于整機(jī)場(chǎng)景了做了性能分析。
從火焰圖上看沒(méi)有明顯的集中,CPU主要消耗是
seastar::reactor::run_tasks 和 seastar::reactor:poll_once。
其中run_tasks負(fù)責(zé)執(zhí)行命令以及網(wǎng)絡(luò)收包,而poll_once主要是負(fù)責(zé)網(wǎng)絡(luò)包的發(fā)送。
然后通過(guò)mpstat分析CPU消耗。
上圖中,第2列是CPU,第3列是%usr 用戶態(tài)占比,第5列是%sys 內(nèi)核態(tài)占比,第8列是%si 軟中斷占比。可以看出軟中斷已經(jīng)消耗了30%的CPU。
這里的軟中斷主要是網(wǎng)卡收包導(dǎo)致的。網(wǎng)卡收到網(wǎng)絡(luò)包后,會(huì)通過(guò)硬件中斷通知內(nèi)核有新的數(shù)據(jù)到了,內(nèi)核會(huì)調(diào)用對(duì)應(yīng)的中斷處理程序來(lái)響應(yīng)該事件,先將網(wǎng)卡的數(shù)據(jù)讀到內(nèi)存中,然后更新硬件寄存器的狀態(tài),
然后內(nèi)核會(huì)觸發(fā)一個(gè)軟中斷,需要從內(nèi)存中找到網(wǎng)絡(luò)數(shù)據(jù),再按照網(wǎng)絡(luò)協(xié)議棧,對(duì)數(shù)據(jù)逐層解析和處理,最后將數(shù)據(jù)給到應(yīng)用程序。
2.3 REDIS性能分析
在同樣的硬件條件下,測(cè)試機(jī)器包含72個(gè)物理核,在機(jī)器上啟動(dòng)了72個(gè)redis實(shí)例,然后對(duì)72個(gè)實(shí)例進(jìn)行了整機(jī)壓測(cè)。perf統(tǒng)計(jì)如下
從上圖可以看出,redis的CPU消耗主要是網(wǎng)絡(luò)收發(fā)包。通過(guò)mpstat查看CPU消耗。
從上圖可以看出,CPU消耗主要集中也是在軟中斷。在整機(jī)測(cè)試的場(chǎng)景下可以看出網(wǎng)絡(luò)成為了性能的瓶頸。
在整機(jī)大壓力訪問(wèn)場(chǎng)景中,無(wú)論是CKV還是REDIS,最終性能瓶頸都在網(wǎng)絡(luò)上,因此傳統(tǒng)的內(nèi)核協(xié)議棧不能能充分利用最新硬件的能力,DPDK通過(guò)用戶態(tài)協(xié)議棧則可以解決這個(gè)問(wèn)題。
3、DPDK
DPDK全稱Intel Data Plane Development Kit,是intel提供的數(shù)據(jù)平面開(kāi)發(fā)工具集,是一個(gè)用來(lái)進(jìn)行包數(shù)據(jù)處理加速的軟件庫(kù)。DPDK專注于網(wǎng)絡(luò)應(yīng)用中數(shù)據(jù)包的高性能處理,具體體現(xiàn)在DPDK應(yīng)用程序是運(yùn)行在用戶態(tài)上利用自身提供的數(shù)據(jù)平面庫(kù)來(lái)收發(fā)數(shù)據(jù)包,繞過(guò)了Linux內(nèi)核協(xié)議棧對(duì)數(shù)據(jù)包處理過(guò)程。
DPDK通過(guò)UIO、poll-mode網(wǎng)卡驅(qū)動(dòng)、內(nèi)存池、大頁(yè)內(nèi)存管理、無(wú)鎖數(shù)據(jù)結(jié)構(gòu)等數(shù)據(jù)手段來(lái)提升性能。
3.1 初始化SEASTAR
1.seastar初始化的時(shí)候,先在cpu 0調(diào)用smp::configure 初始化配置。
2.在cpu 0調(diào)用rte_eal_init 初始化DPDK的EAL。
3.通過(guò)rte_eal_remote_launch 在非0 cpu上調(diào)用reactor::configure 初始化引擎,在初始化引擎的時(shí)候會(huì)根據(jù)用戶配置初始化協(xié)議棧
4.等待其他核初始化完成后,在cpu 0上初始化引擎和協(xié)議棧。完成框架的初始化。
3.2 初始化協(xié)議棧(DPDK)
協(xié)議棧的初始化分成三大步。其中上圖中設(shè)置成橙色的 設(shè)置隔離模式 和 設(shè)置流規(guī)則 是CKV增加的。
1.網(wǎng)卡初始化。首先通過(guò)rte_eth_dev_info_get 獲取網(wǎng)卡信息,然后通過(guò)rte_flow_isolate設(shè)置網(wǎng)卡成隔離模式,最后調(diào)用rte_eth_dev_configure 配置網(wǎng)卡
2.隊(duì)列初始化。通過(guò)調(diào)用rte_eth_rx_queue_setup 和 rte_eth_tx_queue_setup 分別初始化入隊(duì)列和出隊(duì)列。然后會(huì)輪詢 rte_eth_rx_burst 和 rte_eth_tx_burst分別處理收包和發(fā)包。
3.網(wǎng)卡啟動(dòng)。先調(diào)用rte_eth_dev_start啟動(dòng)網(wǎng)卡,然后調(diào)用rte_flow_create設(shè)置流規(guī)則,最后調(diào)用rte_eth_link_get_nowait檢查網(wǎng)卡是否正確啟動(dòng)。
3.3 數(shù)據(jù)包處理
在前面隊(duì)列初始化完成后,我們會(huì)輪詢RX和TX兩個(gè)方向上的包。RX代表接受數(shù)據(jù),TX代表傳輸數(shù)據(jù)。具體是通過(guò)注冊(cè)一個(gè)poller不停的輪詢處理。
RX注冊(cè)的是 reactor::poller::simple([&] { return poll_rx_once(); })
TX注冊(cè)的是 reactor::poller::simple([this] { return poll_tx(); }))
對(duì)于Server端,首先會(huì)通過(guò)RSS(Receive-Side Scaling, also known as multi-queue receive) 將包分發(fā)給指定的隊(duì)列。將不同的流分發(fā)給不同的CPU,同一個(gè)流始終會(huì)在同一個(gè)CPU上,避免TCP的順序和CPU的并行發(fā)生沖突。
在協(xié)議棧實(shí)現(xiàn)中,在二三層都有RX方向的rx_stream,高層會(huì)調(diào)用低層stream的listen方法,注冊(cè)包處理的回調(diào)函數(shù),從dpdk_qp收到包,會(huì)將包從dpdk的rte_mbuf轉(zhuǎn)化成packet格式,然后往上交給L2的stream,再往上交給L3的stream。
包傳遞到二三層是通過(guò)stream/subscription的回調(diào)方式,送到四層直接調(diào)用l4→receive方法直接處理。
目前Seastar的用戶態(tài)協(xié)議棧已經(jīng)支持了ARP、DHCP、ICMP、IP、TCP、UDP協(xié)議。四層主要是TCP/UDP層,我們重點(diǎn)分析TCP層。協(xié)議棧抽象了TCB對(duì)應(yīng)內(nèi)核協(xié)議棧的連接概念。每一個(gè)TCP連接對(duì)應(yīng)一個(gè)TCB對(duì)象。當(dāng)數(shù)據(jù)從三層傳遞給TCP層的時(shí)候,根據(jù)IP/PORT四元組可以找到對(duì)應(yīng)的TCB,然后會(huì)將數(shù)據(jù)保存到TCB的接受隊(duì)列里。
上層的應(yīng)用通過(guò)調(diào)用READ訪問(wèn)從TCB的接受隊(duì)列讀取數(shù)據(jù),這里協(xié)議棧的實(shí)現(xiàn)為了zero-copy,單次讀取是一個(gè)packet,我們優(yōu)化成批量讀取,單個(gè)連接的通信速度整體提升了一倍,從100MB/s提高到200MB/s.
發(fā)送數(shù)據(jù)通過(guò)上層調(diào)用WRITE方法,首先將數(shù)據(jù)保存到對(duì)應(yīng)TCB的unsent隊(duì)列,在發(fā)送的時(shí)候會(huì)將數(shù)據(jù)先放到packetq的循環(huán)buffer,發(fā)送成功后然后再保存到發(fā)送的data隊(duì)列里,等到對(duì)端ACK后才會(huì)將發(fā)送隊(duì)列data的數(shù)據(jù)刪除。
3.4 隔離模式和流規(guī)則
DPDK的應(yīng)用程序默認(rèn)需要接管網(wǎng)卡,一旦程序運(yùn)行,網(wǎng)卡的所有數(shù)據(jù)都通過(guò)DPDK處理,比如常見(jiàn)的SSH登錄可能就會(huì)失敗,還有一些其他公司的Agent探測(cè)和上報(bào)都會(huì)失效。
對(duì)于這個(gè)問(wèn)題我們通過(guò)DPDK提供的isolate模式來(lái)解決,也就是上文提到的設(shè)置隔離模式。isolate模式是指所有通過(guò)DPDK的數(shù)據(jù)默認(rèn)還是走內(nèi)核協(xié)議棧,然后通過(guò)配置一些流規(guī)則,可以將指定的流指向DPDK。
4、性能數(shù)據(jù)
單分片六核
對(duì)比結(jié)果可見(jiàn),CKV寫(xiě)性能比Tair高60%,讀性能與Tair基本一致,value比較大時(shí)性能略低于Tair,是CKV未來(lái)優(yōu)化方向。
六分片六核
在六分片六核場(chǎng)景下,基于DPDK的CKV相對(duì)內(nèi)核協(xié)議棧CKV,整體提升明顯,提升了100%左右。
整機(jī)
從測(cè)試結(jié)果看,CKV在引入DPDK后,通過(guò)DPDK代替了內(nèi)核協(xié)議棧,優(yōu)化了網(wǎng)絡(luò)收發(fā)包的性能瓶頸,達(dá)到了單機(jī)千萬(wàn)的QPS。
總結(jié)
我們從性能方面對(duì)CKV做了一系列優(yōu)化。引入DPDK,通過(guò)優(yōu)化網(wǎng)絡(luò)瓶頸,將整機(jī)性能提升到千萬(wàn)QPS。目前適配部分網(wǎng)卡,后續(xù)會(huì)適配更多類型的網(wǎng)卡。
在壓測(cè)的過(guò)程中,基于DPDK的原生協(xié)議棧在單個(gè)連接上收發(fā)包目前只能達(dá)到250MB/s的傳輸速度,對(duì)于CKV目前來(lái)說(shuō)是可以應(yīng)對(duì)絕大部分場(chǎng)景,但是還需要持續(xù)地優(yōu)化提升單個(gè)連接上的傳輸性能。
未來(lái)CKV會(huì)持續(xù)優(yōu)化迭代,成本上會(huì)采用全類型數(shù)據(jù)下沉到SSD介質(zhì),進(jìn)一步降低成本,性能上面會(huì)持續(xù)優(yōu)化原生協(xié)議棧,持續(xù)挖掘性能。
- END -
看完一鍵三連在看,轉(zhuǎn)發(fā),點(diǎn)贊
是對(duì)文章最大的贊賞,極客重生感謝你
推薦閱讀
我們究竟為誰(shuí)打工?
大廠后臺(tái)開(kāi)發(fā)基本功修煉路線和經(jīng)典資料
如何從0搭建公司的后端技術(shù)棧
你好,這里是極客重生,我是阿榮,大家都叫我榮哥,從華為->外企->到互聯(lián)網(wǎng)大廠,目前是大廠資深工程師,多次獲得五星員工,多年職場(chǎng)經(jīng)驗(yàn),技術(shù)扎實(shí),專業(yè)后端開(kāi)發(fā)和后臺(tái)架構(gòu)設(shè)計(jì),熱愛(ài)底層技術(shù),豐富的實(shí)戰(zhàn)經(jīng)驗(yàn),分享技術(shù)的本質(zhì)原理,希望幫助更多人蛻變重生,拿BAT大廠offer,培養(yǎng)高級(jí)工程師能力,成為技術(shù)專家,實(shí)現(xiàn)高薪夢(mèng)想,期待你的關(guān)注!點(diǎn)擊藍(lán)字查看我的成長(zhǎng)之路。
校招/社招/簡(jiǎn)歷/面試技巧/大廠技術(shù)棧分析/后端開(kāi)發(fā)進(jìn)階/優(yōu)秀開(kāi)源項(xiàng)目/直播分享/技術(shù)視野/實(shí)戰(zhàn)高手等,?極客星球希望成為最有技術(shù)價(jià)值星球,盡最大努力為星球的同學(xué)提供技術(shù)和成長(zhǎng)幫助!詳情查看->極客星球
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 求點(diǎn)贊,在看,分享三連
總結(jié)
以上是生活随笔為你收集整理的深入理解缓存系统|单机QPS突破千万优化之路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++学习路线和参考资料
- 下一篇: 记一次失败的Windows环境编译Ngi