大分区表高并发性能提升100倍?阿里云 RDS PostgreSQL 12 解读
1. 問(wèn)題
阿里云某客戶發(fā)現(xiàn)自己使用讀寫(xiě)分離實(shí)例,master的cpu特別高,而讀寫(xiě)分離中承擔(dān)讀流量的slave節(jié)點(diǎn)卻相對(duì)空閑。用戶CPU打滿后,訪問(wèn)到主節(jié)點(diǎn)的的線上服務(wù)受到了較大影響。
1.1 讀寫(xiě)分離原理
Redis讀寫(xiě)分離實(shí)例的原理是:key統(tǒng)一寫(xiě)入到master,然后通過(guò)主從復(fù)制同步到slave,用戶的請(qǐng)求通過(guò)proxy做判斷,如果是寫(xiě)請(qǐng)求,轉(zhuǎn)發(fā)到master;如果是讀請(qǐng)求,分散轉(zhuǎn)發(fā)到slave,這種架構(gòu)適合讀請(qǐng)求數(shù)量遠(yuǎn)大于寫(xiě)請(qǐng)求數(shù)量的業(yè)務(wù),讀寫(xiě)分離架構(gòu)示意圖如下所示。
圖1. 阿里云Redis讀寫(xiě)分離版讀寫(xiě)命令轉(zhuǎn)發(fā)示例
1.2 bitfield命令
經(jīng)過(guò)和客戶溝通查看后,客戶使用了大量的bitfield做讀取,首先介紹一下這個(gè)命令的用法和場(chǎng)景,bitfield 是針對(duì)bitmap數(shù)據(jù)類型操作的命令,bitmap通常被用來(lái)在極小空間消耗下通過(guò)位的運(yùn)算(AND/OR/XOR/NOT)實(shí)現(xiàn)對(duì)狀態(tài)的判斷,常見(jiàn)的使用場(chǎng)景例如:
- 通過(guò)bitmap來(lái)記錄用戶每天應(yīng)用登錄狀態(tài),即如果$ID用戶登錄,就SETBIT logins:20200404 $ID 1,表示用戶$ID在20200404這一天登錄了,通過(guò)BITCOUNT logins:20200404可以得到這一天所有登錄過(guò)的用戶數(shù)量;通過(guò)對(duì)兩天的記錄求AND,可以判斷哪個(gè)用戶連續(xù)兩天登錄了,即BITOP AND logins:20200404-05 logins:20200404 logins:20200404。
- 判斷用戶是否閱讀了共同的文章,觀看了共同的視頻等。
- 前一陣子,答題領(lǐng)獎(jiǎng)活動(dòng)非常火爆,“答對(duì)12道題的同學(xué)有機(jī)會(huì)瓜分獎(jiǎng)池”,這種如果使用bitmap來(lái)實(shí)現(xiàn),就非常容易判斷出用戶是否全部答對(duì)。
圖2. 一個(gè)使用Redis BITMAP設(shè)計(jì)的答題游戲系統(tǒng)
答題系統(tǒng)設(shè)計(jì)如:
可見(jiàn),Redis的bitmap接口可以用非常高的存儲(chǔ)效率和計(jì)算加速效果。回到bitfiled命令,它的語(yǔ)法如下所示:
BITFIELD key [GET type offset] // 獲取指定位的值 [SET type offset value] // 設(shè)置指定位的值 [INCRBY type offset increment] // 增加指定位的值 [OVERFLOW WRAP|SAT|FAIL] // 控制INCR的界限1.3 讀寫(xiě)分離實(shí)例處理bitfield的問(wèn)題
從上文可知,bitfield的子命令中,GET命令是讀屬性,SET/INCRBY命令為寫(xiě)屬性,因此Redis將其歸類為寫(xiě)屬性,從而只能被轉(zhuǎn)發(fā)到master實(shí)例,如下圖所示為bitfield的路由情況。
這就是為什么客戶使用了讀寫(xiě)分離版,而只有master節(jié)點(diǎn)cpu使用高,其余slave節(jié)點(diǎn)卻沒(méi)有收到這個(gè)命令的打散的原因。
2. 思路和處理
2.1 解決方案
? 方案一:改造Redis內(nèi)核,將bitfield命令屬性標(biāo)記為讀屬性,但是當(dāng)其包含SET/INCRBY等寫(xiě)屬性的子命令時(shí)候,仍舊將其同步到slave等。此方案優(yōu)點(diǎn)是外部組件(proxy和客戶端)不需要做修改,缺點(diǎn)是需要對(duì)bitfiled命令做特殊處理,破壞引擎命令統(tǒng)一處理的一致性。
? 方案二:增加bitfield_ro命令,類似于georadius_ro命令,用來(lái)只支持get選項(xiàng),從而作為讀屬性,這樣就避免了slave無(wú)法讀取的問(wèn)題。此方案優(yōu)點(diǎn)是方案清晰可靠,缺點(diǎn)是需要proxy和客戶端做適配才能使用。
經(jīng)過(guò)討論,最終采取了方案二,因?yàn)檫@個(gè)方案更優(yōu)雅,也更標(biāo)準(zhǔn)化。
2.2 添加bitfield_ro
{"bitfield_ro",bitfieldroCommand,-2, "read-only fast @bitmap", 0,NULL,1,1,1,0,0,0},完成之后,下圖是在slave上執(zhí)行bitfield_ro命令,可以看到被正確執(zhí)行。
tair-redis > SLAVEOF 127.0.0.1 6379 OK tair-redis > set k v (error) READONLY You can't write against a read only replica. tair-redis > BITFIELD mykey GET u4 0 (error) READONLY You can't write against a read only replica. tair-redis > BITFIELD_RO mykey GET u4 0 1) (integer) 02.3 Proxy轉(zhuǎn)發(fā)
為了保持用戶不做代碼修改,我們?cè)趐roxy上對(duì)bitfiled命令做了兼容,即如果用戶的bitfield命令只有g(shù)et選項(xiàng),proxy會(huì)將此命令轉(zhuǎn)換為bitfield_ro分散轉(zhuǎn)發(fā)到后端多個(gè)節(jié)點(diǎn)上,從而實(shí)現(xiàn)加速,用戶不用做任何改造即可完成加速,如下圖所示。
圖4. 添加BITFIELD_RO命令后處理BITFIELD邏輯流程
2.4 貢獻(xiàn)社區(qū)
我們將自己的修改回饋給了社區(qū),并且被Redis官方接受(https://github.com/antirez/redis/pull/6951)
值得一提的是,阿里云在國(guó)內(nèi)是最大的Redis社區(qū)contributer,如在新發(fā)布的Redis-6.0rc中,阿里云的貢獻(xiàn)排第三,僅次于作者和Redis vendor(Redis Labs)。阿里云仍舊在不斷的回饋和貢獻(xiàn)社區(qū)。
圖5. Redis6.0 RC commit數(shù)目榜
3. 引申和討論
3.1 總結(jié)
阿里云Redis通過(guò)增加bitfield_ro命令,解決了官方bitfield get命令無(wú)法在slave上加速執(zhí)行的問(wèn)題。
除過(guò)bitfield命令,阿里云Redis也同時(shí)對(duì)georadius命令做了兼容轉(zhuǎn)換,即在讀寫(xiě)分離實(shí)例上,如果georadius/georadiusbymember命令沒(méi)有store/storedist選項(xiàng),將會(huì)被自動(dòng)判斷為讀命令轉(zhuǎn)發(fā)到slave加速執(zhí)行。
3.2 思考
我們思考讀寫(xiě)分離版的場(chǎng)景,為什么用戶需要讀寫(xiě)分離呢?為什么不是用集群版呢?我們做一下簡(jiǎn)單對(duì)比,比如設(shè)置社區(qū)版的服務(wù)能力為K,那么表的對(duì)比如下(我們只添加了增強(qiáng)版Tair的主備做對(duì)比,集群版可以直接乘以分片數(shù)):
| 寫(xiě)(key均勻情況) | K*分片數(shù) | K | K*3 |
| 讀(key均勻情況) | K*分片數(shù) | K*只讀節(jié)點(diǎn)數(shù) | K*3 |
| 寫(xiě)(單key或熱key) | K(最壞情況) | K | K*3 |
| 讀(單key或熱key) | K(最壞情況) | K*只讀節(jié)點(diǎn)數(shù) | K*3 |
表1. Redis社區(qū)版(集群/讀寫(xiě)分離)和增強(qiáng)版(主備)簡(jiǎn)單場(chǎng)景對(duì)比
可見(jiàn),其實(shí)讀寫(xiě)分離版屬于對(duì)單個(gè)key和熱key的讀能力的擴(kuò)展的一種方法,比較適合中小用戶有大key的情況,它無(wú)法解決用戶的突發(fā)寫(xiě)的瓶頸,比如在這個(gè)場(chǎng)景下,如果用戶的bitfield命令是寫(xiě)請(qǐng)求(子命令中帶有INCRBY和SET),就會(huì)遇到無(wú)法解決的性能問(wèn)題。
從表的對(duì)比看,這種情況下,用戶如果能把key拆散,或者把大key拆成很多小key,就可以使用集群版獲得良好的線性加速能力。大key帶來(lái)的問(wèn)題包含但不僅限于:
- 大key會(huì)造成數(shù)據(jù)傾斜,使得Redis的容量和服務(wù)能力不能線性擴(kuò)展
- 大key意味著大概率這個(gè)key是熱點(diǎn)
- 一旦不小心針對(duì)大key有range類的操作,會(huì)出現(xiàn)慢查詢,還容易打爆帶寬
這也是Tair增強(qiáng)版在阿里集團(tuán)內(nèi)各個(gè)應(yīng)用建議的:“避免設(shè)計(jì)出大key和慢查,能避免90%以上的Redis問(wèn)題”。
但是在實(shí)際使用中,用戶仍舊不可避免的遇到熱點(diǎn)問(wèn)題,比如搶購(gòu),比如熱劇,比如超大型直播間等;尤其是很多熱點(diǎn)具備“突發(fā)性”的特點(diǎn),事先并不知曉,沖擊隨時(shí)可達(dá)。Redis增強(qiáng)版的性能增強(qiáng)實(shí)例具備單key在O(1)操作40~45w ops的服務(wù)能力和極強(qiáng)的抗沖擊能力,單機(jī)主備版就足夠應(yīng)對(duì)一場(chǎng)中大型的秒殺活動(dòng)!同時(shí)如果用戶沒(méi)有大key,增強(qiáng)性能集群版能夠近乎賦予用戶千萬(wàn)甚至幾千萬(wàn)OPS的服務(wù)能力,這也是Tair作為阿里重器,支持每次平穩(wěn)渡過(guò)雙11購(gòu)物節(jié)秒殺的關(guān)鍵,歡迎大家試用!
最后,打一個(gè)小廣告~如果對(duì)KV存儲(chǔ)系統(tǒng),圖數(shù)據(jù)庫(kù)有興趣的小伙伴,歡迎加入我們團(tuán)隊(duì),簡(jiǎn)歷發(fā)送至:zongdai at taobao dot com
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的大分区表高并发性能提升100倍?阿里云 RDS PostgreSQL 12 解读的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 陈旸:清华博士的模型信仰
- 下一篇: 一文带你了解MySQL中的各种锁机制!