王 磊,孟利民
(浙江工業(yè)大學(xué) 信息工程學(xué)院,杭州 310023)
突如其來的新冠肺炎疫情一度讓人們的線下生活按下“暫停鍵”,卻也讓數(shù)字生活[1]按下了“快捷鍵”。直播帶貨、外賣點餐、在線教育等行業(yè)的興起,正在影響和改變?nèi)藗兊南M習(xí)慣和生活方式,螞蟻集團(tuán)CEO胡曉明在支付寶合作伙伴大會上表示,數(shù)字生活新服務(wù)將是下一個十年最大的互聯(lián)網(wǎng)紅利。數(shù)字生活依托于互聯(lián)網(wǎng)和一系列數(shù)字科技技術(shù)應(yīng)用,為了能夠應(yīng)對高并發(fā)場景并支撐多樣化的服務(wù),應(yīng)用軟件需要基于分布式思想[2]進(jìn)行架構(gòu)?;诜植际剿枷爰軜?gòu)的系統(tǒng)(又稱分布式系統(tǒng))具有可靠性、高容錯性和大吞吐量等特點,但也帶來了新的問題。比如重復(fù)冗余操作會導(dǎo)致相同的請求分配到不同的服務(wù)實例,數(shù)據(jù)的插入與更新出現(xiàn)錯亂,無法保證數(shù)據(jù)的一致性和準(zhǔn)確性。特別是涉及金融支付等系統(tǒng)的開發(fā)時,如果不采取措施將會造成嚴(yán)重的后果,因而需要對分布式系統(tǒng)做冪等設(shè)計。
隨著計算機(jī)技術(shù)的發(fā)展,冪等性設(shè)計方法也在不斷演進(jìn)。文獻(xiàn)[3]探討了基于唯一索引、悲觀鎖等方式保障系統(tǒng)的冪等性,采用數(shù)據(jù)庫鎖控制并發(fā)訪問。文獻(xiàn)[4]設(shè)計基于虛擬服務(wù)器的分布式PC共享平臺時,采用了全局唯一ID機(jī)制,根據(jù)操作的內(nèi)容生成一個全局ID,通過客戶端與服務(wù)器端交互以控制頁面的重復(fù)提交。文獻(xiàn)[5]采用Redis分布式鎖設(shè)計系統(tǒng)的冪等性,分布的服務(wù)實例通過競爭鎖獲取程序執(zhí)行權(quán)限,利用緩存的高性能讀寫特性降低服務(wù)端損耗。
分布式系統(tǒng)在進(jìn)行冪等設(shè)計時,應(yīng)充分考量業(yè)務(wù)需求本身的高并發(fā)特性,以及冪等設(shè)計方法在高并發(fā)場景下的性能表現(xiàn),同時需要關(guān)注服務(wù)端性能損耗問題。通過對比研究,提出一種改進(jìn)的分布式鎖冪等設(shè)計方法。實驗結(jié)果表明,該方法在高并發(fā)場景下能夠保持?jǐn)?shù)據(jù)的一致性和準(zhǔn)確性,并具備良好的性能表現(xiàn)。
隨著數(shù)字生活的深入發(fā)展,計算機(jī)系統(tǒng)越發(fā)面臨高并發(fā)和業(yè)務(wù)多樣化的考驗。高并發(fā)是指系統(tǒng)運行過程中,遇到的一種“短時間內(nèi)遇到大量操作請求”的情況。高并發(fā)時,系統(tǒng)需要處理所有請求,執(zhí)行大量的業(yè)務(wù)邏輯處理,頻繁地請求資源和操作數(shù)據(jù)庫,服務(wù)器開銷驟增。傳統(tǒng)的單體架構(gòu)系統(tǒng)由于業(yè)務(wù)模塊耦合、單節(jié)點部署的特點,遭遇高并發(fā)場景時,單節(jié)點服務(wù)器性能下降,當(dāng)請求數(shù)量超出服務(wù)器承載能力時會導(dǎo)致應(yīng)用崩潰,服務(wù)宕機(jī)。
為了解決以上問題,分布式思想應(yīng)運而生。它的發(fā)展經(jīng)歷了分布式架構(gòu)、面向服務(wù)架構(gòu)(SOA,service oriented architecture)、微服務(wù)架構(gòu)等階段。分布式架構(gòu)將各業(yè)務(wù)模塊拆分成子系統(tǒng),并發(fā)訪問量大的子系統(tǒng)可以進(jìn)行多服務(wù)實例集群部署,請求經(jīng)過Nginx反向代理分發(fā),最終到達(dá)具體的服務(wù)實例完成業(yè)務(wù)處理,分布式架構(gòu)原理框圖如圖1所示。隨后出現(xiàn)的面向服務(wù)架構(gòu)SOA,在分布式架構(gòu)的基礎(chǔ)上集成了ESB企業(yè)服務(wù)總線[6],實現(xiàn)路由轉(zhuǎn)發(fā)、服務(wù)管理監(jiān)控、統(tǒng)一安全管理等功能。微服務(wù)架構(gòu)不再強(qiáng)調(diào)量級比較重的ESB企業(yè)服務(wù)總線,將業(yè)務(wù)系統(tǒng)徹底的組件化和服務(wù)化[7],服務(wù)粒度比SOA架構(gòu)更小,保證了服務(wù)的高可用、低耦合特性。
圖1 分布式架構(gòu)原理框圖
分布式系統(tǒng)為了應(yīng)對高并發(fā)場景,保障數(shù)據(jù)的一致性和準(zhǔn)確性,需要做冪等性設(shè)計。冪等概念來自數(shù)學(xué),表示N次變換和1次變換的結(jié)果是相同的。移植到軟件開發(fā)中主要指代,在HTTP協(xié)議中,除去請求超時、系統(tǒng)出現(xiàn)異常的情況下,系統(tǒng)的某些操作,對其調(diào)用一次和調(diào)用多次所產(chǎn)生的的效果是一樣的。特別是在分布式系統(tǒng)中,業(yè)務(wù)繁忙的子系統(tǒng)需要多服務(wù)實例集群部署,請求的轉(zhuǎn)發(fā)與控制情況比較復(fù)雜,冪等性設(shè)計顯得尤為重要。
分布式系統(tǒng)中,需要進(jìn)行冪等設(shè)計的場景眾多,比較常見的有:
1)用戶重復(fù)點擊提交頁面,請求被分配到多個服務(wù)實例進(jìn)行處理,如果業(yè)務(wù)處理涉及數(shù)據(jù)的插入或更新,重復(fù)的操作將導(dǎo)致臟數(shù)據(jù)的產(chǎn)生。
2)分布式系統(tǒng)經(jīng)常會設(shè)計重試機(jī)制[8]來提高請求的成功率,當(dāng)請求被分配的服務(wù)實例出現(xiàn)異?;蜓訒r,系統(tǒng)觸發(fā)重試,該請求被分配到其它服務(wù)實例,如果涉及數(shù)據(jù)插入或更新操作,可能導(dǎo)致臟數(shù)據(jù)的產(chǎn)生。
3)商品秒殺、搶票等業(yè)務(wù)場景中,涉及多個用戶修改同一條數(shù)據(jù)記錄。如果不做冪等設(shè)計,庫存等需要計數(shù)的敏感字段更新將會產(chǎn)生錯亂,影響整體業(yè)務(wù)的執(zhí)行。
分布式系統(tǒng)冪等設(shè)計,從客戶端角度出發(fā),可以通過設(shè)置請求間隔時間防止頁面的重復(fù)提交,但是間隔時間內(nèi)無法保證服務(wù)端業(yè)務(wù)邏輯執(zhí)行完畢,并且該方法不適用從服務(wù)端發(fā)起請求的場景,如系統(tǒng)接口對接。從服務(wù)端角度出發(fā),可以通過區(qū)分業(yè)務(wù)操作類型從數(shù)據(jù)庫端實現(xiàn)冪等設(shè)計,查詢和刪除是天然的冪等操作,而數(shù)據(jù)插入和更新一般是非冪等操作,執(zhí)行一次和多次的效果往往不同,需要使用唯一索引、悲觀鎖等方法保障系統(tǒng)冪等性;除此之外可以通過借助第三方服務(wù)來維護(hù)分布式鎖,無需區(qū)分業(yè)務(wù)操作的類型,分布的服務(wù)實例通過競爭分布式鎖獲得程序執(zhí)行權(quán)限,進(jìn)而保障業(yè)務(wù)執(zhí)行的唯一性,常用的第三方服務(wù)依賴主要有Zookeeper[9]和Redis。
基于服務(wù)端的分布式系統(tǒng)冪等設(shè)計經(jīng)歷了從數(shù)據(jù)庫端到分布式鎖發(fā)展的過程,由數(shù)據(jù)庫端加鎖控制事務(wù)的并發(fā)訪問,到客戶端與服務(wù)端交互的token機(jī)制,再到依賴第三方服務(wù)的分布式鎖,冪等設(shè)計方法隨著計算技術(shù)發(fā)展在不斷演進(jìn)。類比系統(tǒng)安全性設(shè)計,加入冪等設(shè)計后,系統(tǒng)需要犧牲部分性能來實現(xiàn)冪等,因而系統(tǒng)的性能損耗以及高并發(fā)場景下的性能表現(xiàn),成為衡量分布式系統(tǒng)冪等設(shè)計方法的標(biāo)準(zhǔn)。
數(shù)據(jù)庫索引[10]是數(shù)據(jù)庫管理系統(tǒng)中基于排序的數(shù)據(jù)結(jié)構(gòu),以協(xié)助快速查詢、更新數(shù)據(jù)庫表中數(shù)據(jù),作用類似書本的目錄。唯一索引要求所在列的值必須唯一,如果是組合索引,則要求列值的組合必須唯一。唯一索引不僅能夠加快數(shù)據(jù)的查詢速度,而且保證了表中每一行數(shù)據(jù)的唯一性。
通過對數(shù)據(jù)庫表設(shè)置唯一索引的方式,能夠保障數(shù)據(jù)插入相關(guān)業(yè)務(wù)數(shù)據(jù)的準(zhǔn)確性,因而常作為系統(tǒng)冪等設(shè)計的手段。唯一索引在高并發(fā)場景下,重復(fù)的數(shù)據(jù)插入將會被準(zhǔn)確攔截,隨著數(shù)據(jù)庫表數(shù)據(jù)量的增長,系統(tǒng)響應(yīng)時間會相應(yīng)增加。當(dāng)表中的數(shù)據(jù)增長到一定程度時,頻繁的寫入將導(dǎo)致磁盤I/O負(fù)載增加,需要做分表分庫的操作,比較考驗數(shù)據(jù)庫性能開銷。
需要注意的是,單一使用唯一索引時,如果涉及用戶和系統(tǒng)交互,重復(fù)插入的數(shù)據(jù)被捕捉并以拋錯的形式提示用戶,可能導(dǎo)致用戶更頻繁的重試,高并發(fā)場景下會給系統(tǒng)帶來比較大的壓力。
數(shù)據(jù)庫事務(wù)[11]是一個訪問并可能操作各種數(shù)據(jù)項的數(shù)據(jù)庫操作序列,悲觀鎖假定當(dāng)前事務(wù)操作數(shù)據(jù)資源時,還會有其他事務(wù)同時操作該資源,為了避免當(dāng)前事務(wù)被干擾,先將資源進(jìn)行鎖定。換而言之,當(dāng)多個事務(wù)并發(fā)執(zhí)行時,某個事務(wù)對數(shù)據(jù)加鎖,其他事務(wù)只能等待該事務(wù)執(zhí)行完畢,才能對當(dāng)前數(shù)據(jù)進(jìn)行修改。SELECT FOR UPDATE是一個典型的悲觀鎖調(diào)用語句,常用于多個事務(wù)操作同一條記錄,保證計數(shù)、余額扣減等字段值的準(zhǔn)確性。如果程序執(zhí)行出現(xiàn)異常,當(dāng)前事務(wù)需要回滾,當(dāng)前記錄解除鎖定,數(shù)據(jù)恢復(fù)至事務(wù)操作前的狀態(tài)。
通過使用悲觀鎖,能夠保障數(shù)據(jù)更新相關(guān)業(yè)務(wù)數(shù)據(jù)的準(zhǔn)確性和一致性,滿足了一定的冪等設(shè)計要求。但是悲觀鎖具有強(qiáng)烈的獨占和排他特性,高度依賴數(shù)據(jù)庫提供的鎖機(jī)制,某個事務(wù)處理占用鎖時,其它事務(wù)處于阻塞狀態(tài),因而加鎖和釋放鎖的過程比較消耗資源,只適用于并發(fā)不高的場景。高并發(fā)場景下事務(wù)搶占資源容易造成死鎖,進(jìn)而導(dǎo)致應(yīng)用系統(tǒng)崩潰,因而分布式系統(tǒng)冪等設(shè)計時,應(yīng)慎重選擇。
在分布式系統(tǒng)環(huán)境下,需要保證一個方法在同一時刻只能被一個服務(wù)實例的單個線程執(zhí)行,來實現(xiàn)系統(tǒng)冪等?,F(xiàn)有的做法是維護(hù)一把分布式鎖[12],存儲在所有服務(wù)實例都能訪問的地方,服務(wù)實例間通過高可用、高性能地獲取鎖和釋放鎖,完成并發(fā)訪問控制,具體實現(xiàn)過程如圖2所示。分布式鎖的實現(xiàn)需要依賴第三方服務(wù),常用的有Zookeeper和Redis等,這里主要分析基于緩存Redis實現(xiàn)分布式鎖。
圖2 分布式鎖實現(xiàn)過程
Redis分布式鎖主要利用了Redis緩存高性能讀寫的特性[13],服務(wù)實例利用setnx key value命令進(jìn)行加鎖操作,如果Redis服務(wù)中該key值不存在,則設(shè)置value申請加鎖成功,如果已存在該key值,表示已有服務(wù)實例持有該鎖,從而加鎖失敗。當(dāng)持有鎖的服務(wù)實例方法執(zhí)行完畢后,通過del key命令刪除鍵值釋放鎖,其它服務(wù)實例可以重新競爭加鎖,獲取程序執(zhí)行權(quán)限。為了保證操作的原子性,加鎖和解鎖需要使用lua腳本[14]執(zhí)行。使用Redis分布式鎖時,應(yīng)設(shè)置合理的過期時間避免死鎖問題,同時要保證分布式鎖可重復(fù)可遞歸調(diào)用。
Redis分布式鎖相較于數(shù)據(jù)庫層面的冪等設(shè)計有一定的優(yōu)越性,其性能表現(xiàn)依賴于Redis服務(wù)的性能。高并發(fā)場景下,Redis可以單機(jī)部署或者集群部署[15],單機(jī)部署時,對服務(wù)器硬件配置要求較高,而且一旦單機(jī)服務(wù)宕機(jī),雖然不進(jìn)行數(shù)據(jù)處理但系統(tǒng)訪問將報錯,因而探討Redis集群部署是必要的。Redis集群是由一系列的主從節(jié)點(master-slave)群組成的分布式服務(wù)器群,具有復(fù)制、高可用和分片特性,服務(wù)實例訪問Redis集群如圖3所示。當(dāng)主從節(jié)點中的主節(jié)點master宕機(jī)時,可以實現(xiàn)故障自動切換,把從節(jié)點slave升為主節(jié)點master,解決了單機(jī)部署服務(wù)宕機(jī)問題。但是如果主節(jié)點master加鎖成功,此時master出現(xiàn)異常宕機(jī),由于主從節(jié)點切換是異步過程,加鎖指令并未同步到從節(jié)點slave上,從節(jié)點slave被升為master,該鎖在新的主節(jié)點master上丟失了,進(jìn)而出現(xiàn)短暫的鎖失效問題,從而導(dǎo)致數(shù)據(jù)的插入或更新出現(xiàn)錯亂,系統(tǒng)冪等無法被保障。
圖3 服務(wù)實例訪問Redis集群
為了解決上述問題,Redis作者提出了RedLock算法[16]方案,該方案實現(xiàn)需要部署N個獨立的Redis實例,實例間沒有主從關(guān)系,官方推薦實例數(shù)量N≥5,方案模型如下:
1)服務(wù)實例先獲取當(dāng)前時間戳T1,并依次向N個Redis實例發(fā)起加鎖請求,對每個加鎖請求設(shè)置超時時間,如果某個實例由于鎖被其它服務(wù)實例持有等原因?qū)е录渔i失敗,就立即向下一個Redis實例申請加鎖;
2)循環(huán)申請加鎖完畢后,對(N+1)/2進(jìn)行向上取整運算得到結(jié)果S,如果服務(wù)實例在大于等于S個Redis實例上加鎖成功,再次獲取當(dāng)前時間戳T2,若T2-T1小于鎖的過期時間,則認(rèn)為該服務(wù)實例加鎖成功,否則就認(rèn)為加鎖失??;
3)服務(wù)實例加鎖成功后,執(zhí)行業(yè)務(wù)邏輯處理,加鎖失敗,則向全部Redis實例發(fā)起釋放鎖請求。
RedLock算法方案目前存在爭論,質(zhì)疑者認(rèn)為RedLock通過循環(huán)Redis實例申請加鎖,開銷大效率低;同時Redis節(jié)點會因為機(jī)器時鐘修改或跳躍導(dǎo)致鎖到期,造成分布式服務(wù)實例間持有鎖沖突,最終的結(jié)果是數(shù)據(jù)嚴(yán)重錯誤、永久性不一致或丟失,因而認(rèn)為RedLock無法解決Redis集群主從節(jié)點切換導(dǎo)致的鎖時效問題。
針對RedLock存在的爭論問題,提出一種改進(jìn)的分布式鎖設(shè)計方法,具體的設(shè)計過程是:部署Redis集群環(huán)境,通過分布式鎖的方式對高并發(fā)請求實施第一道攔截;針對極端情況下Redis集群可能出現(xiàn)的主從節(jié)點切換導(dǎo)致分布式鎖失效問題,通過判斷業(yè)務(wù)操作類型施加唯一索引或者數(shù)據(jù)鎖,實現(xiàn)第二道攔截;最后將失效的分布式鎖通過消息隊列異步發(fā)送通知消息,實現(xiàn)Redis集群服務(wù)的監(jiān)測和治理。
上述改進(jìn)的分布式鎖設(shè)計方法,實施的第一道攔截采用Redisson分布式鎖。Redisson[17]是Java技術(shù)棧封裝的用于操作Redis的工具,基于Netty框架進(jìn)行事件驅(qū)動。相較于Jedis、Lettuce等客戶端工具,Redisson實現(xiàn)了分布式和可擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),促使使用者對Redis的關(guān)注分離,提供了很多分布式相關(guān)操作服務(wù),如分布式鎖、分布式集合等。Redisson分布式鎖的工作過程是:分布的服務(wù)實例通過lock或tryLock方法進(jìn)行加鎖操作,底層通過exists指令判斷鎖標(biāo)識是否存在,若鎖標(biāo)識不存在,則使用hset指令進(jìn)行加鎖,再通過pexpire指令設(shè)置鎖過期時間;若鎖標(biāo)識存在,則根據(jù)業(yè)務(wù)需求選擇不停嘗試加鎖或者停止申請加鎖。業(yè)務(wù)邏輯執(zhí)行完畢后,使用unlock方法時釋放鎖,底層通過del指令刪除鎖標(biāo)識。
Redisson加鎖和釋放鎖操作基于lua腳本實現(xiàn),以確保底層exists、hset、pexpire一系列指令不受服務(wù)實例宕機(jī)的影響,能夠執(zhí)行完畢,保證操作的原子性。另外Redisson還提供了watch dog自動延期機(jī)制,后臺線程每隔10 s檢查一次,若服務(wù)實例仍持有鎖標(biāo)識,將不斷延長鎖的過期時間,防止業(yè)務(wù)邏輯未執(zhí)行完畢自動釋放鎖的情況,保障系統(tǒng)的冪等性。
改進(jìn)的分布式鎖設(shè)計方法,針對Redis集群可能出現(xiàn)的主節(jié)點master宕機(jī)問題,在數(shù)據(jù)庫層面進(jìn)行第二道攔截。根據(jù)業(yè)務(wù)數(shù)據(jù)操作類型進(jìn)行判斷,如果是數(shù)據(jù)插入操作,則施加唯一索引限制數(shù)據(jù)重復(fù)插入的問題;如果是數(shù)據(jù)更新操作,則施加數(shù)據(jù)庫鎖,保證數(shù)據(jù)更新的正確性。由于悲觀鎖采用的是阻塞模式,不適用于高并發(fā)場景下數(shù)據(jù)更新操作,方法選用一種樂觀鎖[18]的方式進(jìn)行實現(xiàn)。
樂觀鎖是相對于悲觀鎖而言的,它假設(shè)數(shù)據(jù)一般情況下不會產(chǎn)生沖突,只有在事務(wù)提交時才會對數(shù)據(jù)沖突與否進(jìn)行檢測。樂觀鎖沿用了CAS的思想,通過數(shù)據(jù)庫表增加“版本號”Version字段,檢測事務(wù)沖突,其工作過程如圖4所示:事務(wù)1讀取并記錄時版本號為1,執(zhí)行更新時Version自動加1并更新為版本號2;事務(wù)2順序執(zhí)行,將讀取的版本號2更新為版本號3,此時兩個事務(wù)提交不產(chǎn)生沖突。如果事務(wù)1和事務(wù)2同時讀取的記錄版本號為1,事務(wù)1執(zhí)行更新時Version自動加1并更新為版本號2,事務(wù)2同樣準(zhǔn)備將版本號更新為2,但此時已查詢不到版本號為1的當(dāng)前記錄,發(fā)生沖突。樂觀鎖的優(yōu)勢在于不對數(shù)據(jù)進(jìn)行行鎖和表鎖處理,減小了數(shù)據(jù)庫的壓力開銷,對改進(jìn)的分布鎖設(shè)計高并發(fā)場景的性能表現(xiàn)是一個提升。
圖4 版本號實現(xiàn)樂觀鎖過程
改進(jìn)的分布式鎖設(shè)計通過消息隊列的方式將數(shù)據(jù)庫攔截的失效鎖,以消息的形式通知給開發(fā)維護(hù)人員,方便進(jìn)行鎖失效問題的排查,如果是Redis集群服務(wù)主節(jié)點宕機(jī)的原因,可以快速地重啟服務(wù)節(jié)點。方法選用RabbitMq消息服務(wù)[19]實現(xiàn)消息的生產(chǎn)和消費,通知消息以異步的形式進(jìn)行處理,防止出現(xiàn)同步阻塞影響主要業(yè)務(wù)邏輯的處理。
改進(jìn)的分布式鎖設(shè)計整體工作過程如圖5所示。
圖5 改進(jìn)的分布式鎖工作過程
1)高并發(fā)請求經(jīng)過Nginx負(fù)載均衡服務(wù)被分配到具體的服務(wù)實例執(zhí)行。
2)服務(wù)實例收到轉(zhuǎn)發(fā)的請求,程序接口根據(jù)請求內(nèi)容中的字段或組合字段生成鎖標(biāo)識,Redisson通過lock或tryLock方法調(diào)用Redis集群服務(wù)進(jìn)行加鎖操作。根據(jù)返回結(jié)果判斷服務(wù)實例是否競爭到鎖,如果加鎖成功將進(jìn)入業(yè)務(wù)邏輯處理環(huán)節(jié),加鎖失敗可以結(jié)束當(dāng)前線程或者等待其它服務(wù)實例釋放鎖后重新競爭加鎖。
3)業(yè)務(wù)邏輯處理階段,根據(jù)數(shù)據(jù)操作類型進(jìn)行判斷,若為數(shù)據(jù)插入操作則執(zhí)行唯一索引邏輯,若為數(shù)據(jù)更新操作則執(zhí)行樂觀鎖邏輯,完成對失效鎖的攔截,攔截成功后結(jié)束當(dāng)前線程,對當(dāng)前請求返回錯誤提示。
4)對于數(shù)據(jù)庫攔截的失效鎖,通過RabbitMq消息生產(chǎn)者將相關(guān)信息放入消息隊列,等待RabbitMq消息消費者進(jìn)行異步處理。
5)對于持有Redisson分布式鎖的服務(wù)實例,程序執(zhí)行完畢后,通過調(diào)用unlock方法將。
Redis集群服務(wù)中的鎖標(biāo)識刪除,保證后續(xù)服務(wù)實例能夠繼續(xù)競爭使用該鎖標(biāo)識。
高并發(fā)場景下,網(wǎng)絡(luò)請求首先經(jīng)過Redis集群進(jìn)行加鎖,基于緩存高性能讀寫特性完成操作,保障了服務(wù)端性能損耗主要在訪問Redis集群服務(wù)上,只有極端情況下主從切換出現(xiàn)短暫的鎖失效問題時,才會觸發(fā)數(shù)據(jù)庫層面的攔截,避免單一使用數(shù)據(jù)庫冪等設(shè)計導(dǎo)致重復(fù)試錯帶來的數(shù)據(jù)庫死鎖等風(fēng)險。同時本設(shè)計還包含了Redis集群服務(wù)的監(jiān)測和治理,方便開發(fā)人員能夠快速的了解和掌握Redis服務(wù)的健康狀況,有助于解決節(jié)點宕機(jī)問題和系統(tǒng)優(yōu)化。
為了驗證分布式系統(tǒng)冪等設(shè)計方法在高并發(fā)場景下的性能表現(xiàn)和性能損耗問題,通過實驗?zāi)M和還原高并發(fā)場景進(jìn)行測試。實驗需要部署Redis集群服務(wù)、RabbitMq服務(wù)、多個服務(wù)實例、JMeter測試工具[20]以及千萬級別數(shù)據(jù)量的數(shù)據(jù)庫表。測試方法為:通過JMeter設(shè)置1 000個并發(fā)線程數(shù),分別測試單獨使用悲觀鎖、樂觀鎖、唯一索引、Redis分布式鎖4種冪等設(shè)計方法的性能表現(xiàn),然后測試改進(jìn)的Redisson分布式鎖在Redis集群主動停掉一個主節(jié)點的情況下的性能問題。悲觀鎖、樂觀鎖設(shè)置為秒殺50個商品庫存的場景,Redis分布式鎖和改進(jìn)的Redisson分布式鎖在本實驗中只針對數(shù)據(jù)插入的場景,并且測試插入的數(shù)據(jù)每隔一條設(shè)置重復(fù)數(shù)據(jù)模擬高并發(fā)請求。
實驗結(jié)果如表1所示,其中成功次數(shù)和攔截次數(shù)反映了冪等設(shè)計方法保障數(shù)據(jù)一致性和準(zhǔn)確性的能力,平均響應(yīng)時間和吞吐量反映了高并發(fā)場景下系統(tǒng)性能開銷和損耗。通過實驗結(jié)果對比發(fā)現(xiàn):樂觀鎖相較于悲觀鎖響應(yīng)時間短,系統(tǒng)吞吐量也有提升,有良好的攔截事務(wù)沖突能力;Redis分布式鎖相比于數(shù)據(jù)庫層面的冪等設(shè)計有更好的性能表現(xiàn);通過對比Redis分布式鎖和改進(jìn)的Redisson分布式鎖發(fā)現(xiàn),即使在主動宕機(jī)一個Redis集群主節(jié)點時,改進(jìn)的Redisson分布式鎖仍能保證數(shù)據(jù)攔截的準(zhǔn)確性,并且其平均響應(yīng)時間和吞吐量指標(biāo)和Redis分布式鎖相當(dāng),同時RabbitMq消費者收到一條失效的鎖信息,表明數(shù)據(jù)庫層面的二次攔截生效。
表1 冪等設(shè)計各方法性能參數(shù)
隨著分布式架構(gòu)思想的廣泛應(yīng)用,如何保證系統(tǒng)數(shù)據(jù)的一致性和準(zhǔn)確性愈發(fā)受到關(guān)注。通過分析服務(wù)端冪等設(shè)計方法的原理、應(yīng)用場景以及性能表現(xiàn),提出一種改進(jìn)的Redisson分布式鎖設(shè)計方法,來保證分布式系統(tǒng)數(shù)據(jù)的一致性和準(zhǔn)確性。該方法對Redis分布式鎖進(jìn)行了升級,針對RedLock存在爭論的基礎(chǔ)之上,采用二次攔截的方式,解決Redis集群主從節(jié)點切換造成的鎖失效問題。并且通過消息隊列服務(wù)實現(xiàn)通知,方便Redis集群服務(wù)的監(jiān)測和治理。最后通過實驗驗證了改進(jìn)的Redisson分布式鎖設(shè)計的可行性。