订单失效怎么做的_数据库压力降低90%,携程机票订单缓存系统实践
本文旨在分享攜程機(jī)票后服務(wù)訂單處理團(tuán)隊(duì),在構(gòu)建機(jī)票訂單緩存系統(tǒng)過程中的一些思考總結(jié),希望能給大家一些啟發(fā)或幫助。通篇分為以下七大部分:背景,瓶頸,選型,架構(gòu),方案,優(yōu)化,總結(jié),文章概要如下圖:
一、背景
近些年隨著攜程機(jī)票業(yè)務(wù)的不斷發(fā)展,用戶量和訂單量也穩(wěn)定地增長(zhǎng),再加上用戶訪問入口的多樣性、機(jī)票的有效期特別長(zhǎng)等特征,導(dǎo)致查詢流量不斷增長(zhǎng)。這些,給基于強(qiáng)依賴訂單數(shù)據(jù)庫(kù)的訂單查詢系統(tǒng)帶來了不小的壓力。
不僅業(yè)務(wù)上帶來的流量壓力,技術(shù)改造也帶來了更多的流量,如微服務(wù)化的推進(jìn)、機(jī)票前后臺(tái)訂單業(yè)務(wù)解耦,不可避免地使得訂單查詢系統(tǒng)被依賴程度進(jìn)一步提高。而且我們沒有使用類似GraphQL 的技術(shù),之前的前后臺(tái)服務(wù)直連數(shù)據(jù)庫(kù),現(xiàn)在使用查詢API,沒有高度定制化產(chǎn)生了數(shù)據(jù)冗余,帶來額外的查詢壓力。
作為典型的用戶訂單詳情查詢服務(wù),承受著越來越重的壓力,峰值時(shí)期訂單詳情查詢QPS數(shù)倍于同期訂單TPS。
為了保障用戶的使用體驗(yàn),支持機(jī)票業(yè)務(wù)的持續(xù)發(fā)展,保證機(jī)票訂單查詢系統(tǒng)的穩(wěn)定高效,構(gòu)建和不斷升級(jí)機(jī)票訂單查詢系統(tǒng),自然成為重要且緊急的事情。
二、瓶頸
大部分應(yīng)用系統(tǒng)的瓶頸都會(huì)出在比較慢的地方,如外部資源及磁盤IO或數(shù)據(jù)庫(kù),訂單系統(tǒng)的瓶頸顯而易見,重復(fù)高頻的訂單數(shù)據(jù)訪問,最終帶來的是訂單數(shù)據(jù)庫(kù)訪問的壓力。
盡管之前通過數(shù)據(jù)庫(kù)主從復(fù)制、讀寫分離的手段,一定程度上緩解數(shù)據(jù)庫(kù)的壓力。但是從節(jié)點(diǎn)的增加無疑會(huì)帶來一些不得不考慮的問題,包括數(shù)據(jù)同步時(shí)的AG延遲問題,數(shù)據(jù)庫(kù)服務(wù)器的運(yùn)維成本等。
不僅局部?jī)?yōu)化有瓶頸,而且我們一直認(rèn)為局部的優(yōu)化,不如總線級(jí)別的優(yōu)化來的更有效率,數(shù)據(jù)庫(kù)總線跟內(nèi)存總線對(duì)于訂單系統(tǒng)數(shù)據(jù)存取速度的提升并不是一個(gè)級(jí)別。
基于以上種種,機(jī)票訂單后處理團(tuán)隊(duì),決定構(gòu)建一套相對(duì)完整的基于內(nèi)存數(shù)據(jù)緩存體系,不僅用于緩解機(jī)票訂單數(shù)據(jù)庫(kù)的訪問壓力,也用來提高訂單查詢服務(wù)的穩(wěn)定性和容災(zāi)能力。
三、選型
目前各種類型的緩存都活躍在成千上萬的應(yīng)用服務(wù)中,還沒有一種緩存方案可以解決一切的業(yè)務(wù)場(chǎng)景或數(shù)據(jù)類型,我們需要根據(jù)自身的特殊場(chǎng)景和背景,選擇最適合的緩存方案。
目前業(yè)內(nèi)用來做緩存的,通常主要有以下幾種:Memcache、Redis,MongoDB。關(guān)于幾者的優(yōu)劣對(duì)比網(wǎng)絡(luò)上也有不少的相關(guān)文章,這里不再詳細(xì)列舉。
選型并非是一個(gè)固定不變的標(biāo)準(zhǔn),雖然系統(tǒng)設(shè)計(jì)沒有銀彈,但是路就在腳下,我們必須要結(jié)合自己的業(yè)務(wù)場(chǎng)景及運(yùn)維能力做出綜合判斷(取舍),這里說明一下我們關(guān)注的點(diǎn)。
(1)故障快速恢復(fù),機(jī)票訂單的查詢服務(wù)面臨的QPS較高,又是許多上層應(yīng)用服務(wù)的依賴項(xiàng),由于業(yè)務(wù)比較核心,所以數(shù)據(jù)安全問題必須考慮。因此第一要求故障快速恢復(fù),攜程的Redis部署是多組多實(shí)例多機(jī)柜,完全滿足需求。
(2)性能要好,機(jī)票訂單數(shù)據(jù)的查詢,超過92%的場(chǎng)景都是根據(jù)訂單號(hào)的查詢,場(chǎng)景比較單一,很少有使用索引條件的緩存查詢需求。單一機(jī)票訂單的數(shù)據(jù)量并不多,所以要求的緩存性能越高,用于查詢集群機(jī)器線程池效率越高,整個(gè)查詢集群機(jī)器越少,硬件成本及維護(hù)成本才會(huì)變低。
(3)輸出穩(wěn)定,緩存性能穩(wěn)定,查詢集群的線程池運(yùn)行越穩(wěn)定,尖刺就會(huì)少,整個(gè)系統(tǒng)穩(wěn)定性會(huì)提高。
我們綜合機(jī)票訂單業(yè)務(wù)場(chǎng)景,采用了Redis作為機(jī)票訂單緩存的存儲(chǔ)介質(zhì)。
因?yàn)镽edis的部署特性,要求我們的單個(gè)實(shí)例不能太大,為了避免實(shí)例太大,我們前期做了很多工作,比如容量評(píng)估及預(yù)測(cè),通過拆分+壓縮來規(guī)避容量問題。
四、架構(gòu)
在攜程機(jī)票的微服務(wù)化架構(gòu)體系之下,按照業(yè)務(wù)層級(jí)的不同,后臺(tái)服務(wù)呈現(xiàn)層次化調(diào)用的樹狀結(jié)構(gòu)。
比如訂單詳情服務(wù)輸出機(jī)票訂單的狀態(tài)、價(jià)格、航班、乘客等各類基本信息,同時(shí)依賴于機(jī)票的改簽業(yè)務(wù)、退票業(yè)務(wù)、航變業(yè)務(wù),支付業(yè)務(wù)等提供的查詢服務(wù),而這些獨(dú)立的子業(yè)務(wù)與訂單詳情服務(wù)本身一樣需要訂單基本信息數(shù)據(jù)作為業(yè)務(wù)支撐。為了避免循環(huán)依賴,需要把訂單基本數(shù)據(jù)查詢業(yè)務(wù)剝離出來,單獨(dú)提供底層數(shù)據(jù)訪問服務(wù),同時(shí)支撐不同業(yè)務(wù)模塊。所以機(jī)票訂單后服務(wù)團(tuán)隊(duì)依據(jù)服務(wù)的層次設(shè)計(jì)了兩級(jí)緩存的體系,如下圖所示。
(1)一級(jí)緩存
一級(jí)緩存是訂單查詢服務(wù)響應(yīng)結(jié)果級(jí)別的緩存。訂單查詢服務(wù)作為一系列核心服務(wù),支撐了不同前端渠道的訂單查詢請(qǐng)求,包括APP,Online、H5等,同時(shí)也支持后臺(tái)訂單處理的各個(gè)環(huán)節(jié)業(yè)務(wù)對(duì)訂單數(shù)據(jù)的查詢需求。
訂單查詢服務(wù)的請(qǐng)求QPS很高,對(duì)同一個(gè)訂單的請(qǐng)求在1秒之內(nèi)有時(shí)會(huì)達(dá)到十幾次。對(duì)于許多業(yè)務(wù)場(chǎng)景來說,1秒的時(shí)間內(nèi)訂單的數(shù)據(jù)基本不會(huì)發(fā)生變化,因此完全可以將該訂單的整個(gè)訂單查詢服務(wù)響應(yīng)結(jié)果緩存起來,設(shè)置秒級(jí)別的過期時(shí)間。這樣做一方面能夠提高服務(wù)的響應(yīng)速度,另一方面能夠減少對(duì)底層數(shù)據(jù)庫(kù)服務(wù)的訪問,降低數(shù)據(jù)庫(kù)的壓力,預(yù)防意外的或者惡意的流量沖擊。
(2)二級(jí)緩存
二級(jí)緩存是針對(duì)訂單數(shù)據(jù)庫(kù)熱點(diǎn)數(shù)據(jù)表的數(shù)據(jù)的底層緩存。根據(jù)對(duì)機(jī)票訂單業(yè)務(wù)的梳理和對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)表的訪問頻率的統(tǒng)計(jì),我們篩選出了80+的熱點(diǎn)機(jī)票訂單數(shù)據(jù)表,對(duì)每個(gè)表的數(shù)據(jù)建立緩存,并提供數(shù)據(jù)查詢服務(wù)接口。
這樣一來,將不同業(yè)務(wù)方與訂單數(shù)據(jù)庫(kù)解耦,統(tǒng)一的數(shù)據(jù)訪問服務(wù)層更有利于統(tǒng)一建立和管理數(shù)據(jù)緩存系統(tǒng),提高緩存數(shù)據(jù)的使用效率。同時(shí)對(duì)底層數(shù)據(jù)庫(kù)的垂直拆分和水平拆分,做到數(shù)據(jù)使用的業(yè)務(wù)方無感,提高了系統(tǒng)的擴(kuò)展性。
五、方案
緩存數(shù)據(jù)一致性的實(shí)現(xiàn)方案,一般可以分為兩大類:主動(dòng)式和被動(dòng)式緩存。
主動(dòng)式緩存就是提前將可能訪問到的數(shù)據(jù)加載到緩存中,然后設(shè)置過期時(shí)間,周期性刷新。等到需要查詢數(shù)據(jù)的時(shí)候,可以優(yōu)先命中緩存。這種方式比較適合于數(shù)據(jù)量不大,變化不頻繁的場(chǎng)景。
被動(dòng)式緩存是指每次查詢時(shí)先從緩存中查詢數(shù)據(jù),沒有則查詢底層數(shù)據(jù)庫(kù),然后把數(shù)據(jù)庫(kù)的查詢結(jié)果加載到緩存,并設(shè)置一定的過期時(shí)間。數(shù)據(jù)過期后,當(dāng)再次遇到查詢請(qǐng)求時(shí)重復(fù)前面所說的過程。被動(dòng)式緩存具有延遲加載的特性,也就是只有真正被訪問過的數(shù)據(jù)才會(huì)被加載到緩存中,能夠更有效的利用緩存數(shù)據(jù)。
機(jī)票訂單的數(shù)據(jù)熱度具有階段性,用戶訂單成交的前后階段訂單查詢頻率較高,用戶的歷史訂單查詢頻率就很低了。因此我們采用被動(dòng)式加載的方案,隨著時(shí)間的推移,老的歷史訂單數(shù)據(jù)會(huì)自動(dòng)逐漸過期,新的訂單數(shù)據(jù)被逐漸加載。緩存數(shù)據(jù)的總量保持平穩(wěn),同時(shí)避免了老舊數(shù)據(jù)的清理。
分布式緩存架構(gòu)的數(shù)據(jù)一致性是個(gè)難點(diǎn),這也是訂單處理團(tuán)隊(duì)一直在努力思考并解決的重中之重。緩存數(shù)據(jù)意味著數(shù)據(jù)的變更在過期時(shí)間之內(nèi)是被忽略的,可能與實(shí)際的數(shù)據(jù)庫(kù)數(shù)據(jù)有一定差異。一般情況下,一種方案是盡量設(shè)置較短的過期時(shí)間,以使緩存數(shù)據(jù)盡快與數(shù)據(jù)源同步。但是較短的過期時(shí)間帶來的是緩存命中率的大大降低,削弱了緩存數(shù)據(jù)的效果。
為了解決數(shù)據(jù)一致性問題,我們?cè)O(shè)計(jì)了兩套方案來更新緩存數(shù)據(jù),保證緩存數(shù)據(jù)的實(shí)時(shí)性。兩套方案相互補(bǔ)充,避免其一短暫失效而產(chǎn)生一致性問題。
方案一的思路是掃描數(shù)據(jù)庫(kù)的數(shù)據(jù)變化記錄,比如數(shù)據(jù)表里面的數(shù)據(jù)更新時(shí)間戳,或者訂閱數(shù)據(jù)庫(kù)的binlog記錄。當(dāng)掃描到數(shù)據(jù)變化時(shí),刪除對(duì)應(yīng)的緩存數(shù)據(jù)。
方案二的思路是借助于攜程的消息通知平臺(tái)服務(wù)。在訂單處理流轉(zhuǎn)的各個(gè)重要環(huán)節(jié),會(huì)有消息事件產(chǎn)生。通過訂閱這些消息,能夠感知到訂單數(shù)據(jù)的變化,并且通過對(duì)不同消息的影響數(shù)據(jù)范圍可以精準(zhǔn)地配置緩存數(shù)據(jù)更新。
兩套方案的并行實(shí)施,保證了緩存數(shù)據(jù)的延遲控制在了100毫秒以內(nèi)。
緩存的構(gòu)建過程中,我們也遇到幾個(gè)必須慎重處理的關(guān)鍵點(diǎn):緩存穿透、緩存擊穿、緩存雪崩。
(1)緩存穿透的解決方案(空標(biāo)記)
緩存穿透是指,在數(shù)據(jù)存儲(chǔ)系統(tǒng)中不存在的記錄,不會(huì)被存儲(chǔ)到緩存中。這種記錄每次的查詢流量都會(huì)穿透到數(shù)據(jù)存儲(chǔ)層。在高流量的場(chǎng)景下,不斷查詢空結(jié)果會(huì)大量消耗數(shù)據(jù)查詢服務(wù)的資源,甚至在惡意流量攻擊下可能拖垮數(shù)據(jù)庫(kù)系統(tǒng)。
以機(jī)票訂單為例,有些訂單購(gòu)買了保險(xiǎn),也有的訂單沒有購(gòu)買保險(xiǎn)的記錄。沒有保險(xiǎn)產(chǎn)品的訂單每次查詢時(shí)都會(huì)從數(shù)據(jù)庫(kù)中查到空的結(jié)果,命中不了緩存,消耗了大量的Batch Request。
針對(duì)這種情況,我們采取了設(shè)置空標(biāo)記的措施,針對(duì)查詢正常返回但是記錄為空的數(shù)據(jù),將特定的空標(biāo)記寫入緩存,避免了這部分流量穿透到數(shù)據(jù)庫(kù)。
(2)緩存擊穿的解決方案(分布式鎖)
緩存擊穿是指,對(duì)于一個(gè)熱點(diǎn)KEY,在其失效的瞬時(shí),如果有大量的對(duì)這個(gè)KEY的請(qǐng)求,都會(huì)到達(dá)數(shù)據(jù)庫(kù)。
我們通過分布式鎖的方式,在緩存數(shù)據(jù)失效的時(shí)刻,僅允許單一請(qǐng)求讀取數(shù)據(jù)庫(kù)數(shù)據(jù)并加載到緩存。
(3)緩存雪崩的解決方案(隨機(jī)時(shí)間差)
緩存雪崩是指,在同一時(shí)間有大量緩存一起失效。
我們使用了一個(gè)簡(jiǎn)單有效的方法,對(duì)不同的緩存數(shù)據(jù)設(shè)置過期時(shí)間時(shí),采用不同的時(shí)間,比如增加一個(gè)小的隨機(jī)值。
此外,由于不同訂單處理過程中的消息發(fā)生時(shí)間不同,而訂單處理過程一般會(huì)有等待隊(duì)列,處理時(shí)間會(huì)有差異,也降低了緩存同時(shí)失效的風(fēng)險(xiǎn)。
六、優(yōu)化
做完以上這些,應(yīng)該算個(gè)及格的緩存系統(tǒng),但是我們相信更優(yōu)秀的系統(tǒng)是通過一步步的迭代及演進(jìn)出來的。機(jī)票訂單緩存系統(tǒng)的優(yōu)化,必然要與機(jī)票訂單的自身特征深度吻合,高度定制化,才能發(fā)揮更加有效的作用。
我們通過對(duì)攜程機(jī)票訂單以及機(jī)票用戶行為的大量數(shù)據(jù)分析,制定了一些針對(duì)性的優(yōu)化措施。
(1)全天候分段緩存過期策略(按時(shí)間)
根據(jù)對(duì)機(jī)票訂單系統(tǒng)每日實(shí)時(shí)訂單量數(shù)據(jù)的分析,實(shí)時(shí)訂單量呈現(xiàn)雙駝峰的形態(tài),上午9點(diǎn)到11點(diǎn)以及下午1點(diǎn)到4點(diǎn)之間這兩個(gè)時(shí)間段內(nèi),訂單量處于峰值狀態(tài),中午處于相對(duì)的低谷。而夜間訂單量為逐漸下降的趨勢(shì),直至凌晨1點(diǎn)到凌晨3點(diǎn)達(dá)到最低點(diǎn)。這與人們的出行作息時(shí)間是非常吻合的。
在訂單量的峰值時(shí)段,數(shù)據(jù)庫(kù)訪問壓力比較大,反之低谷時(shí)段則壓力較小。對(duì)此我們?yōu)橛唵尾樵兎?wù)的一級(jí)緩存設(shè)置了分時(shí)段的過期時(shí)間策略,業(yè)務(wù)高峰時(shí)間段緩存時(shí)間相對(duì)調(diào)整變長(zhǎng),低峰時(shí)段調(diào)整變短。每個(gè)查詢服務(wù)可以單獨(dú)配置這套規(guī)則,隨時(shí)動(dòng)態(tài)調(diào)整。這樣對(duì)于數(shù)據(jù)存儲(chǔ)端壓力來說也就是起到了削峰填谷的作用。
(2)訂單狀態(tài)分類策略(按業(yè)務(wù))
機(jī)票訂單根據(jù)業(yè)務(wù)定義有各種不同的狀態(tài),表示著訂單處理流轉(zhuǎn)的各個(gè)階段。基于對(duì)訂單狀態(tài)及其后續(xù)變更行為數(shù)據(jù)的分析,對(duì)于已退票或取消的終態(tài)訂單,其訂單數(shù)據(jù)基本不會(huì)再發(fā)生變化。這類訂單的緩存過期時(shí)間可以設(shè)置的相對(duì)較長(zhǎng);對(duì)于已過起飛時(shí)間的訂單,用戶再查看訪問訂單數(shù)據(jù)的概率也大大降低,其訂單數(shù)據(jù)緩存時(shí)間也可以相對(duì)長(zhǎng)一些;而訂單處理中未出票等過程態(tài)訂單,短時(shí)間狀態(tài)變更的可能性很高,因此不應(yīng)該放入緩存之中。
(3)用戶行為的訂單訪問預(yù)測(cè)(預(yù)加載)
根據(jù)統(tǒng)計(jì),用戶乘機(jī)前的幾個(gè)小時(shí)內(nèi),出于關(guān)注出發(fā)時(shí)間、機(jī)場(chǎng)航站樓、航班變動(dòng),辦理登機(jī)手續(xù)等需要,會(huì)頻繁查看訂單數(shù)據(jù)。數(shù)據(jù)訪問的壓力集中在了白天的峰值時(shí)間段內(nèi)。
基于這種對(duì)用戶行為的預(yù)測(cè),我們考慮了在前一天的凌晨業(yè)務(wù)低峰階段,將第二天業(yè)務(wù)高峰時(shí)間段內(nèi)起飛的訂單數(shù)據(jù),提前加載到緩存中預(yù)熱,將高峰時(shí)間段的一部分緩存構(gòu)建壓力,轉(zhuǎn)移到了低峰時(shí)間段,既提高了低峰時(shí)間段的系統(tǒng)資源利用,也緩解了高峰時(shí)間段數(shù)據(jù)庫(kù)的壓力。不僅如此,更大的好處是,第二天訂單數(shù)據(jù)庫(kù)即使有短暫的故障,用戶查詢當(dāng)天出行訂單也不會(huì)受到影響,因?yàn)閿?shù)據(jù)都已經(jīng)提前構(gòu)建在緩存里了。
七、總結(jié)
經(jīng)過查詢系統(tǒng)的不斷迭代,埋點(diǎn)數(shù)據(jù)的統(tǒng)計(jì)顯示,訂單緩存數(shù)據(jù)的命中率達(dá)到了90%以上,也就是說對(duì)數(shù)據(jù)庫(kù)的訪問壓力降低到了原來的十分之一的水平。
從數(shù)據(jù)庫(kù)層面的性能統(tǒng)計(jì)監(jiān)控來看,數(shù)據(jù)庫(kù)服務(wù)器的cpu利用率峰值由60%降低到了15%,均值由20%以上降低到了10%以下。對(duì)數(shù)據(jù)系統(tǒng)的可用性來說改善效果非常顯著。
在服務(wù)響應(yīng)性能方面,緩存系統(tǒng)的建立也帶來了性能的提升。以訂單詳情服務(wù)為例,服務(wù)響應(yīng)時(shí)間(PT99)均值由150ms降低到了120ms以下。
以下列舉一些保證性能及可靠性的具體技術(shù)點(diǎn):
1)采用了Redis Pipeline技術(shù),節(jié)省了建立鏈接的時(shí)間。
2)采用了GZIP壓縮技術(shù),節(jié)省4-5倍的Redis存儲(chǔ)空間。
3)采用了NIO技術(shù),讓我們的緩存服務(wù)支撐更多的鏈接。
4)采用了Hystrix熔斷技術(shù),保證了單個(gè)Redis實(shí)例異常時(shí)候,可以放棄該實(shí)例,保證其他正常讀取,保證高可用。
整個(gè)緩存系統(tǒng)的構(gòu)建,沒有侵入到復(fù)雜的機(jī)票業(yè)務(wù)中,更像一個(gè)“外掛”系統(tǒng),工作量及業(yè)務(wù)風(fēng)險(xiǎn)可控。除緩存系統(tǒng)外,未來的訂單查詢業(yè)務(wù)核心挑戰(zhàn)依然是穩(wěn)定性,包括故障的快速定位,調(diào)用鏈的梳理及簡(jiǎn)化。
【作者簡(jiǎn)介】Chaplin,攜程資深PMO,平時(shí)喜歡解決系統(tǒng)相關(guān)的問題,包括但不限于分布式/大數(shù)據(jù)量/性能/體驗(yàn)等,不畏復(fù)雜但更喜歡簡(jiǎn)單。
更多攜程技術(shù)人一手干貨文章,請(qǐng)關(guān)注“攜程技術(shù)”微信公眾號(hào)。
總結(jié)
以上是生活随笔為你收集整理的订单失效怎么做的_数据库压力降低90%,携程机票订单缓存系统实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Skia Windows 编译
- 下一篇: 60分钟入门pytorch