一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

從設(shè)計(jì)方案到案例分析,詳解常見性能優(yōu)化策略

 emo_ 2016-12-08

作者:邵曉明

編輯:小智

本文旨在形成可實(shí)踐、可借鑒、可參考的各種性能優(yōu)化的方案以及選型考慮點(diǎn),同時(shí)配合具體的真實(shí)案例,讓其他人遇到相似問題時(shí),不用從零開始。除性能優(yōu)化外,也提供通用的常見思路及方案選型的考慮點(diǎn),幫助培養(yǎng)在方案選型時(shí)的意識、思維以及做各種權(quán)衡的能力。

本文轉(zhuǎn)載自美團(tuán)點(diǎn)評技術(shù)團(tuán)隊(duì)博客,已獲授權(quán)。

常見性能優(yōu)化策略分類

代碼

之所以把代碼放到第一位,是因?yàn)檫@一點(diǎn)最容易引起技術(shù)人員的忽視。很多技術(shù)人員拿到一個(gè)性能優(yōu)化的需求以后,言必稱緩存、異步、JVM等。實(shí)際上,第一步就應(yīng)該是分析相關(guān)的代碼,找出相應(yīng)的瓶頸,再來考慮具體的優(yōu)化策略。有一些性能問題,完全是由于代碼寫的不合理,通過直接修改一下代碼就能解決問題的,比如for循環(huán)次數(shù)過多、作了很多無謂的條件判斷、相同邏輯重復(fù)多次等。

數(shù)據(jù)庫

數(shù)據(jù)庫的調(diào)優(yōu),總的來說分為以下三部分:

SQL調(diào)優(yōu)

這是最常用、每一個(gè)技術(shù)人員都應(yīng)該掌握基本的SQL調(diào)優(yōu)手段(包括方法、工具、輔助系統(tǒng)等)。這里以MySQL為例,最常見的方式是,由自帶的慢查詢?nèi)罩净蛘唛_源的慢查詢系統(tǒng)定位到具體的出問題的SQL,然后使用explain、profile等工具來逐步調(diào)優(yōu),最后經(jīng)過測試達(dá)到效果后上線。這方面的細(xì)節(jié),可以參考MySQL索引原理及慢查詢優(yōu)化。

架構(gòu)層面的調(diào)優(yōu)

這一類調(diào)優(yōu)包括讀寫分離、多從庫負(fù)載均衡、水平和垂直分庫分表等方面,一般需要的改動較大,但是頻率沒有SQL調(diào)優(yōu)高,而且一般需要DBA來配合參與。那么什么時(shí)候需要做這些事情?我們可以通過內(nèi)部監(jiān)控報(bào)警系統(tǒng)(比如Zabbix),定期跟蹤一些指標(biāo)數(shù)據(jù)是否達(dá)到瓶頸,一旦達(dá)到瓶頸或者警戒值,就需要考慮這些事情。通常,DBA也會定期監(jiān)控這些指標(biāo)值。

連接池調(diào)優(yōu)

我們的應(yīng)用為了實(shí)現(xiàn)數(shù)據(jù)庫連接的高效獲取、對數(shù)據(jù)庫連接的限流等目的,通常會采用連接池類的方案,即每一個(gè)應(yīng)用節(jié)點(diǎn)都管理了一個(gè)到各個(gè)數(shù)據(jù)庫的連接池。隨著業(yè)務(wù)訪問量或者數(shù)據(jù)量的增長,原有的連接池參數(shù)可能不能很好地滿足需求,這個(gè)時(shí)候就需要結(jié)合當(dāng)前使用連接池的原理、具體的連接池監(jiān)控?cái)?shù)據(jù)和當(dāng)前的業(yè)務(wù)量作一個(gè)綜合的判斷,通過反復(fù)的幾次調(diào)試得到最終的調(diào)優(yōu)參數(shù)。

緩存

分類

本地緩存(HashMap/ConcurrentHashMap、Ehcache、Guava Cache等),緩存服務(wù)(Redis/Tair/Memcache等)。

使用場景

什么情況適合用緩存?考慮以下兩種場景:

  • 短時(shí)間內(nèi)相同數(shù)據(jù)重復(fù)查詢多次且數(shù)據(jù)更新不頻繁,這個(gè)時(shí)候可以選擇先從緩存查詢,查詢不到再從數(shù)據(jù)庫加載并回設(shè)到緩存的方式。此種場景較適合用單機(jī)緩存。

  • 高并發(fā)查詢熱點(diǎn)數(shù)據(jù),后端數(shù)據(jù)庫不堪重負(fù),可以用緩存來扛。

選型考慮

  • 如果數(shù)據(jù)量小,并且不會頻繁地增長又清空(這會導(dǎo)致頻繁地垃圾回收),那么可以選擇本地緩存。具體的話,如果需要一些策略的支持(比如緩存滿的逐出策略),可以考慮Ehcache;如不需要,可以考慮HashMap;如需要考慮多線程并發(fā)的場景,可以考慮ConcurentHashMap。

  • 其他情況,可以考慮緩存服務(wù)。目前從資源的投入度、可運(yùn)維性、是否能動態(tài)擴(kuò)容以及配套設(shè)施來考慮,我們優(yōu)先考慮Tair。除非目前Tair還不能支持的場合(比如分布式鎖、Hash類型的value),我們考慮用Redis。

設(shè)計(jì)關(guān)鍵點(diǎn)

什么時(shí)候更新緩存?如何保障更新的可靠性和實(shí)時(shí)性?

更新緩存的策略,需要具體問題具體分析。這里以門店P(guān)OI的緩存數(shù)據(jù)為例,來說明一下緩存服務(wù)型的緩存更新策略是怎樣的?目前約10萬個(gè)POI數(shù)據(jù)采用了Tair作為緩存服務(wù),具體更新的策略有兩個(gè):

  • 接收門店變更的消息,準(zhǔn)實(shí)時(shí)更新。

  • 給每一個(gè)POI緩存數(shù)據(jù)設(shè)置5分鐘的過期時(shí)間,過期后從DB加載再回設(shè)到DB。這個(gè)策略是對第一個(gè)策略的有力補(bǔ)充,解決了手動變更DB不發(fā)消息、接消息更新程序臨時(shí)出錯(cuò)等問題導(dǎo)致的第一個(gè)策略失效的問題。通過這種雙保險(xiǎn)機(jī)制,有效地保證了POI緩存數(shù)據(jù)的可靠性和實(shí)時(shí)性。

緩存是否會滿,緩存滿了怎么辦?

對于一個(gè)緩存服務(wù),理論上來說,隨著緩存數(shù)據(jù)的日益增多,在容量有限的情況下,緩存肯定有一天會滿的。如何應(yīng)對?

  1. 給緩存服務(wù),選擇合適的緩存逐出算法,比如最常見的LRU。

  2. 針對當(dāng)前設(shè)置的容量,設(shè)置適當(dāng)?shù)木渲担热?0G的緩存,當(dāng)緩存數(shù)據(jù)達(dá)到8G的時(shí)候,就開始發(fā)出報(bào)警,提前排查問題或者擴(kuò)容。

  3. 給一些沒有必要長期保存的key,盡量設(shè)置過期時(shí)間。

緩存是否允許丟失?丟失了怎么辦?

根據(jù)業(yè)務(wù)場景判斷,是否允許丟失。如果不允許,就需要帶持久化功能的緩存服務(wù)來支持,比如Redis或者Tair。更細(xì)節(jié)的話,可以根據(jù)業(yè)務(wù)對丟失時(shí)間的容忍度,還可以選擇更具體的持久化策略,比如Redis的RDB或者AOF。

緩存被“擊穿”問題

對于一些設(shè)置了過期時(shí)間的key,如果這些key可能會在某些時(shí)間點(diǎn)被超高并發(fā)地訪問,是一種非?!盁狳c(diǎn)”的數(shù)據(jù)。這個(gè)時(shí)候,需要考慮另外一個(gè)問題:緩存被“擊穿”的問題。

  • 概念:緩存在某個(gè)時(shí)間點(diǎn)過期的時(shí)候,恰好在這個(gè)時(shí)間點(diǎn)對這個(gè)Key有大量的并發(fā)請求過來,這些請求發(fā)現(xiàn)緩存過期一般都會從后端DB加載數(shù)據(jù)并回設(shè)到緩存,這個(gè)時(shí)候大并發(fā)的請求可能會瞬間把后端DB壓垮。

  • 如何解決:業(yè)界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時(shí)候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者M(jìn)emcache的ADD)去set一個(gè)mutex key,當(dāng)操作返回成功時(shí),再進(jìn)行l(wèi)oad db的操作并回設(shè)緩存;否則,就重試整個(gè)get緩存的方法。類似下面的代碼:


異步

使用場景

針對某些客戶端的請求,在服務(wù)端可能需要針對這些請求做一些附屬的事情,這些事情其實(shí)用戶并不關(guān)心或者用戶不需要立即拿到這些事情的處理結(jié)果,這種情況就比較適合用異步的方式處理這些事情。

作用

  • 縮短接口響應(yīng)時(shí)間,使用戶的請求快速返回,用戶體驗(yàn)更好。

  • 避免線程長時(shí)間處于運(yùn)行狀態(tài),這樣會引起服務(wù)線程池的可用線程長時(shí)間不夠用,進(jìn)而引起線程池任務(wù)隊(duì)列長度增大,從而阻塞更多請求任務(wù),使得更多請求得不到技術(shù)處理。

  • 線程長時(shí)間處于運(yùn)行狀態(tài),可能還會引起系統(tǒng)Load、CPU使用率、機(jī)器整體性能下降等一系列問題,甚至引發(fā)雪崩。異步的思路可以在不增加機(jī)器數(shù)和CPU數(shù)的情況下,有效解決這個(gè)問題。

常見做法

一種做法,是額外開辟線程,這里可以采用額外開辟一個(gè)線程或者使用線程池的做法,在IO線程(處理請求響應(yīng))之外的線程來處理相應(yīng)的任務(wù),在IO線程中讓response先返回。

如果異步線程處理的任務(wù)設(shè)計(jì)的數(shù)據(jù)量非常巨大,那么可以引入阻塞隊(duì)列BlockingQueue作進(jìn)一步的優(yōu)化。具體做法是讓一批異步線程不斷地往阻塞隊(duì)列里扔數(shù)據(jù),然后額外起一個(gè)處理線程,循環(huán)批量從隊(duì)列里拿預(yù)設(shè)大小的一批數(shù)據(jù),來進(jìn)行批處理(比如發(fā)一個(gè)批量的遠(yuǎn)程服務(wù)請求),這樣進(jìn)一步提高了性能。

另一種做法,是使用消息隊(duì)列(MQ)中間件服務(wù),MQ天生就是異步的。一些額外的任務(wù),可能不需要我這個(gè)系統(tǒng)來處理,但是需要其他系統(tǒng)來處理。這個(gè)時(shí)候可以先把它封裝成一個(gè)消息,扔到消息隊(duì)列里面,通過消息中間件的可靠性保證把消息投遞到關(guān)心它的系統(tǒng),然后讓這個(gè)系統(tǒng)來做相應(yīng)的處理。

比如C端在完成一個(gè)提單動作以后,可能需要其它端做一系列的事情,但是這些事情的結(jié)果不會立刻對C端用戶產(chǎn)生影響,那么就可以先把C端下單的請求響應(yīng)先返回給用戶,返回之前往MQ中發(fā)一個(gè)消息即可。而且這些事情理應(yīng)不是C端的負(fù)責(zé)范圍,所以這個(gè)時(shí)候用MQ的方式,來解決這個(gè)問題最合適。

NoSQL

和緩存的區(qū)別

先說明一下,這里介紹的和緩存那一節(jié)不一樣,雖然可能會使用一樣的數(shù)據(jù)存儲方案(比如Redis或者Tair),但是使用的方式不一樣,這一節(jié)介紹的是把它作為DB來用。如果當(dāng)作DB來用,需要有效保證數(shù)據(jù)存儲方案的可用性、可靠性。

使用場景

需要結(jié)合具體的業(yè)務(wù)場景,看這塊業(yè)務(wù)涉及的數(shù)據(jù)是否適合用NoSQL來存儲,對數(shù)據(jù)的操作方式是否適合用NoSQL的方式來操作,或者是否需要用到NoSQL的一些額外特性(比如原子加減等)。

如果業(yè)務(wù)數(shù)據(jù)不需要和其他數(shù)據(jù)作關(guān)聯(lián),不需要事務(wù)或者外鍵之類的支持,而且有可能寫入會異常頻繁,這個(gè)時(shí)候就比較適合用NoSQL(比如HBase)。

比如,美團(tuán)點(diǎn)評內(nèi)部有一個(gè)對exception做的監(jiān)控系統(tǒng),如果在應(yīng)用系統(tǒng)發(fā)生嚴(yán)重故障的時(shí)候,可能會短時(shí)間產(chǎn)生大量exception數(shù)據(jù),這個(gè)時(shí)候如果選用MySQL,會造成MySQL的瞬間寫壓力飆升,容易導(dǎo)致MySQL服務(wù)器的性能急劇惡化以及主從同步延遲之類的問題,這種場景就比較適合用Hbase類似的NoSQL來存儲。

JVM調(diào)優(yōu)

什么時(shí)候調(diào)?

通過監(jiān)控系統(tǒng)(如沒有現(xiàn)成的系統(tǒng),自己做一個(gè)簡單的上報(bào)監(jiān)控的系統(tǒng)也很容易)上對一些機(jī)器關(guān)鍵指標(biāo)(gc time、gc count、各個(gè)分代的內(nèi)存大小變化、機(jī)器的Load值與CPU使用率、JVM的線程數(shù)等)的監(jiān)控報(bào)警,也可以看gc log和jstat等命令的輸出,再結(jié)合線上JVM進(jìn)程服務(wù)的一些關(guān)鍵接口的性能數(shù)據(jù)和請求體驗(yàn),基本上就能定位出當(dāng)前的JVM是否有問題,以及是否需要調(diào)優(yōu)。

怎么調(diào)?

  1. 如果發(fā)現(xiàn)高峰期CPU使用率與Load值偏大,這個(gè)時(shí)候可以觀察一些JVM的thread count以及gc count(可能主要是young gc count),如果這兩個(gè)值都比以往偏大(也可以和一個(gè)歷史經(jīng)驗(yàn)值作對比),基本上可以定位是young gc頻率過高導(dǎo)致,這個(gè)時(shí)候可以通過適當(dāng)增大young區(qū)大小或者占比的方式來解決。

  2. 如果發(fā)現(xiàn)關(guān)鍵接口響應(yīng)時(shí)間很慢,可以結(jié)合gc time以及gc log中的stop the world的時(shí)間,看一下整個(gè)應(yīng)用的stop the world的時(shí)間是不是比較多。如果是,可能需要減少總的gc time,具體可以從減小gc的次數(shù)和減小單次gc的時(shí)間這兩個(gè)維度來考慮,一般來說,這兩個(gè)因素是一對互斥因素,我們需要根據(jù)實(shí)際的監(jiān)控?cái)?shù)據(jù)來調(diào)整相應(yīng)的參數(shù)(比如新生代與老生代比值、eden與survivor比值、MTT值、觸發(fā)cms回收的old區(qū)比率閾值等)來達(dá)到一個(gè)最優(yōu)值。

  3. 如果發(fā)生full gc或者old cms gc非常頻繁,通常這種情況會誘發(fā)STW的時(shí)間相應(yīng)加長,從而也會導(dǎo)致接口響應(yīng)時(shí)間變慢。這種情況,大概率是出現(xiàn)了“內(nèi)存泄露”,Java里的內(nèi)存泄露指的是一些應(yīng)該釋放的對象沒有被釋放掉(還有引用拉著它)。那么這些對象是如何產(chǎn)生的呢?為啥不會釋放呢?對應(yīng)的代碼是不是出問題了?問題的關(guān)鍵是搞明白這個(gè),找到相應(yīng)的代碼,然后對癥下藥。所以問題的關(guān)鍵是轉(zhuǎn)化成尋找這些對象。怎么找?綜合使用jmap和MAT,基本就能定位到具體的代碼。

多線程與分布式

使用場景

離線任務(wù)、異步任務(wù)、大數(shù)據(jù)任務(wù)、耗時(shí)較長任務(wù)的運(yùn)行**,適當(dāng)?shù)乩?,可達(dá)到加速的效果。

注意:線上對響應(yīng)時(shí)間要求較高的場合,盡量少用多線程,尤其是服務(wù)線程需要等待任務(wù)線程的場合(很多重大事故就是和這個(gè)息息相關(guān)),如果一定要用,可以對服務(wù)線程設(shè)置一個(gè)最大等待時(shí)間。

常見做法

如果單機(jī)的處理能力可以滿足實(shí)際業(yè)務(wù)的需求,那么盡可能地使用單機(jī)多線程的處理方式,減少復(fù)雜性;反之,則需要使用多機(jī)多線程的方式。

對于單機(jī)多線程,可以引入線程池的機(jī)制,作用有二:

  • 提高性能,節(jié)省線程創(chuàng)建和銷毀的開銷

  • 限流,給線程池一個(gè)固定的容量,達(dá)到這個(gè)容量值后再有任務(wù)進(jìn)來,就進(jìn)入隊(duì)列進(jìn)行排隊(duì),保障機(jī)器極限壓力下的穩(wěn)定處理能力在使用JDK自帶的線程池時(shí),一定要仔細(xì)理解構(gòu)造方法的各個(gè)參數(shù)的含義,如core pool size、max pool size、keepAliveTime、worker queue等,在理解的基礎(chǔ)上通過不斷地測試調(diào)整這些參數(shù)值達(dá)到最優(yōu)效果。

如果單機(jī)的處理能力不能滿足需求,這個(gè)時(shí)候需要使用多機(jī)多線程的方式。這個(gè)時(shí)候就需要一些分布式系統(tǒng)的知識了。首先就必須引入一個(gè)單獨(dú)的節(jié)點(diǎn),作為調(diào)度器,其他的機(jī)器節(jié)點(diǎn)都作為執(zhí)行器節(jié)點(diǎn)。調(diào)度器來負(fù)責(zé)拆分任務(wù),和分發(fā)任務(wù)到合適的執(zhí)行器節(jié)點(diǎn);執(zhí)行器節(jié)點(diǎn)按照多線程的方式(也可能是單線程)來執(zhí)行任務(wù)。

這個(gè)時(shí)候,我們整個(gè)任務(wù)系統(tǒng)就由單擊演變成一個(gè)集群的系統(tǒng),而且不同的機(jī)器節(jié)點(diǎn)有不同的角色,各司其職,各個(gè)節(jié)點(diǎn)之間還有交互。這個(gè)時(shí)候除了有多線程、線程池等機(jī)制,像RPC、心跳等網(wǎng)絡(luò)通信調(diào)用的機(jī)制也不可少。后續(xù)我會出一個(gè)簡單的分布式調(diào)度運(yùn)行的框架。

度量系統(tǒng)(監(jiān)控、報(bào)警、服務(wù)依賴管理)

嚴(yán)格來說,度量系統(tǒng)不屬于性能優(yōu)化的范疇,但是這方面和性能優(yōu)化息息相關(guān),可以說為性能優(yōu)化提供一個(gè)強(qiáng)有力的數(shù)據(jù)參考和支撐。沒有度量系統(tǒng),基本上就沒有辦法定位到系統(tǒng)的問題,也沒有辦法有效衡量優(yōu)化后的效果。很多人不重視這方面,但我認(rèn)為它是系統(tǒng)穩(wěn)定性和性能保障的基石。

關(guān)鍵流程

如果要設(shè)計(jì)這套系統(tǒng),總體來說有哪些關(guān)鍵流程需要設(shè)計(jì)呢?

  1. 確定指標(biāo)

  2. 采集數(shù)據(jù)

  3. 計(jì)算數(shù)據(jù),存儲結(jié)果

  4. 展現(xiàn)和分析

需要監(jiān)控和報(bào)警哪些指標(biāo)數(shù)據(jù)?需要關(guān)注哪些?

按照需求出發(fā),主要需要二方面的指標(biāo):

  1. 接口性能相關(guān),包括單個(gè)接口和全部的QPS、響應(yīng)時(shí)間、調(diào)用量(統(tǒng)計(jì)時(shí)間維度越細(xì)越好;最好是,既能以節(jié)點(diǎn)為維度,也可以以服務(wù)集群為維度,來查看相關(guān)數(shù)據(jù))。其中還涉及到服務(wù)依賴關(guān)系的管理,這個(gè)時(shí)候需要用到服務(wù)依賴管理系統(tǒng)

  2. 單個(gè)機(jī)器節(jié)點(diǎn)相關(guān),包括CPU使用率、Load值、內(nèi)存占用率、網(wǎng)卡流量等。如果節(jié)點(diǎn)是一些特殊類型的服務(wù)(比如MySQL、Redis、Tair),還可以監(jiān)控這些服務(wù)特有的一些關(guān)鍵指標(biāo)。

數(shù)據(jù)采集方式

通常采用異步上報(bào)的方式,具體做法有兩種:第一種,發(fā)到本地的Flume端口,由Flume進(jìn)程收集到遠(yuǎn)程的Hadoop集群或者Storm集群來進(jìn)行運(yùn)算;第二種,直接在本地運(yùn)算好以后,使用異步和本地隊(duì)列的方式,發(fā)送到監(jiān)控服務(wù)器。

數(shù)據(jù)計(jì)算

可以采用離線運(yùn)算(MapReduce/Hive)或者實(shí)時(shí)/準(zhǔn)實(shí)時(shí)運(yùn)算(Storm/Spark)的方式,運(yùn)算后的結(jié)果存入MySQL或者HBase;某些情況,也可以不計(jì)算,直接采集發(fā)往監(jiān)控服務(wù)器。

展現(xiàn)和分析

提供統(tǒng)一的展現(xiàn)分析平臺,需要帶報(bào)表(列表/圖表)監(jiān)控和報(bào)警的功能。

真實(shí)案例分析

案例一:商家與控制區(qū)關(guān)系的刷新job

背景

這是一個(gè)每小時(shí)定期運(yùn)行一次的job,作用是用來刷新商家與控制區(qū)的關(guān)系。具體規(guī)則就是根據(jù)商家的配送范圍(多個(gè))與控制區(qū)是否有交集,如果有交集,就把這個(gè)商家劃到這個(gè)控制區(qū)的范圍內(nèi)。

業(yè)務(wù)需求

需要這個(gè)過程越短越好,最好保持在20分鐘內(nèi)。

優(yōu)化過程

原有代碼的主要處理流程是:

  1. 拿到所有門店的配送范圍列表和控制區(qū)列表。

  2. 遍歷控制區(qū)列表,針對每一個(gè)控制區(qū):

    遍歷商家的配送范圍列表,找到和這個(gè)控制區(qū)相交的配送范圍列表。

    遍歷上述商家配送范圍列表,對里面的商家ID去重,保存到一個(gè)集合里。

    批量根據(jù)上述商家ID集合,取到對應(yīng)的商家集合。

    遍歷上述商家集合,從中拿到每一個(gè)商家對象,進(jìn)行相應(yīng)的處理(根據(jù)是否已是熱門商家、自營、在線支付等條件來判斷是否需要插入或者更新之前的商家和控制區(qū)的關(guān)系)。

    刪除這個(gè)控制區(qū)當(dāng)前已有的,但是不應(yīng)該存在的商家關(guān)系列表。

分析代碼,發(fā)現(xiàn)第2步的a步驟和b步驟,找出和某控制區(qū)相交的配送范圍集合并對商家ID去重,可以采用R樹空間索引的方式來優(yōu)化。具體做法是:

  • 任務(wù)開始先更新R樹,然后利用R樹的結(jié)構(gòu)和匹配算法來拿到和控制區(qū)相交的配送范圍ID列表。

  • 再批量根據(jù)配送范圍ID列表,拿到配送范圍列表。

  • 然后針對這一批配送范圍列表(數(shù)量很小),用原始多邊形相交匹配的方法做進(jìn)一步過濾,并且對過濾后的商家ID去重。

這個(gè)優(yōu)化已經(jīng)在第一期優(yōu)化中上線,整個(gè)過程耗時(shí)由40多分鐘縮短到20分鐘以內(nèi)。

第一期優(yōu)化改為R樹以后,運(yùn)行了一段時(shí)間,隨著數(shù)據(jù)量增大,性能又開始逐漸惡化,一個(gè)月后已經(jīng)惡化到50多分鐘。于是繼續(xù)深入代碼分析,尋找了兩個(gè)優(yōu)化點(diǎn),安排第二期優(yōu)化并上線。

這兩個(gè)優(yōu)化點(diǎn)是:

  • 第2步的c步驟,原來是根據(jù)門店ID列表從DB批量獲取門店,現(xiàn)在可以改成mget的方式從緩存批量獲?。ù藭r(shí)商家數(shù)據(jù)已被緩存);

  • 第2步的d步驟,根據(jù)是否已是熱門商家、自營、在線支付等條件來判斷是否需要插入或者更新之前的商家和控制區(qū)的關(guān)系。

上線后效果

通過日志觀察,執(zhí)行時(shí)間由50多分鐘縮短到15分鐘以內(nèi),下圖是截取了一天的4臺機(jī)器的日志時(shí)間(單位:毫秒):

可以看到,效果還是非常明顯的。

案例二:POI緩存設(shè)計(jì)與實(shí)現(xiàn)

背景

2014年Q4,數(shù)據(jù)庫中關(guān)于POI(這里可以簡單理解為外賣的門店)相關(guān)的數(shù)據(jù)的讀流量急劇上升,雖然說加入從庫節(jié)點(diǎn)可以解決一部分問題,但是畢竟節(jié)點(diǎn)的增加是會達(dá)到極限的,達(dá)到極限后主從復(fù)制會達(dá)到瓶頸,可能會造成數(shù)據(jù)不一致。所以此時(shí),急需引入一種新的技術(shù)方案來分擔(dān)數(shù)據(jù)庫的壓力,降低數(shù)據(jù)庫POI相關(guān)數(shù)據(jù)的讀流量。另外,任何場景都考慮加DB從庫的做法,會對資源造成一定的浪費(fèi)。

實(shí)現(xiàn)方案

基于已有的經(jīng)過考驗(yàn)的技術(shù)方案,我選擇Tair來作為緩存的存儲方案,來幫DB分擔(dān)來自于各應(yīng)用端的POI數(shù)據(jù)的讀流量的壓力。理由主要是從可用性、高性能、可擴(kuò)展性、是否經(jīng)過線上大規(guī)模數(shù)據(jù)和高并發(fā)流量的考驗(yàn)、是否有專業(yè)運(yùn)維團(tuán)隊(duì)、是否有成熟工具等幾個(gè)方面綜合考量決定。

詳細(xì)設(shè)計(jì)

第一版設(shè)計(jì)

緩存的更新策略,根據(jù)業(yè)務(wù)的特點(diǎn)、已有的技術(shù)方案和實(shí)現(xiàn)成本,選擇了用MQ來接收POI改變的消息來觸發(fā)緩存的更新,但是這個(gè)過程有可能失??;同時(shí)啟用了key的過期策略,并且調(diào)用端會先判斷是否過期,如過期,會從后端DB加載數(shù)據(jù)并回設(shè)到緩存,再返回。通過兩個(gè)方面雙保險(xiǎn)確保了緩存數(shù)據(jù)的可用。

第二版設(shè)計(jì)

第一版設(shè)計(jì)運(yùn)行到一段時(shí)間以后,我們發(fā)現(xiàn)了兩個(gè)問題:

  1. 某些情況下不能保證數(shù)據(jù)的實(shí)時(shí)一致(比如技術(shù)人員手動改動DB數(shù)據(jù)、利用MQ更新緩存失敗),這個(gè)時(shí)候只能等待5分鐘的過期時(shí)間,有的業(yè)務(wù)是不允許的。

  2. 加入了過期時(shí)間導(dǎo)致另外一個(gè)問題:Tair在緩存不命中的那一刻,會嘗試從硬盤中Load數(shù)據(jù),如果硬盤沒有再去DB中Load數(shù)據(jù)。這無疑會進(jìn)一步延長Tair的響應(yīng)時(shí)間,這樣不僅使得業(yè)務(wù)的超時(shí)比率加大,而且會導(dǎo)致Tair的性能進(jìn)一步變差。

為了解決上述問題,我們從美團(tuán)點(diǎn)評負(fù)責(zé)基礎(chǔ)架構(gòu)的同事那里了解到Databus可以解決緩存數(shù)據(jù)在某些情況下不一致的問題,并且可以去掉過期時(shí)間機(jī)制,從而提高查詢效率,避免tair在內(nèi)存不命中時(shí)查詢硬盤。而且為了防止DataBus單點(diǎn)出現(xiàn)故障影響我們的業(yè)務(wù),我們保留了之前接MQ消息更新緩存的方案,作了切換開關(guān),利用這個(gè)方案作容錯(cuò),整體架構(gòu)如下:

上線后效果

上線后,通過持續(xù)地監(jiān)控?cái)?shù)據(jù)發(fā)現(xiàn),隨著調(diào)用量的上升,到DB的流量有了明顯地減少,極大地減輕了DB的壓力。同時(shí)這些數(shù)據(jù)接口的響應(yīng)時(shí)間也有了明顯地減少。緩存更新的雙重保障機(jī)制,也基本保證了緩存數(shù)據(jù)的可用。見下圖:

案例三:業(yè)務(wù)運(yùn)營后臺相關(guān)頁面的性能優(yōu)化

背景

隨著業(yè)務(wù)的快速發(fā)展,帶來的訪問量和數(shù)據(jù)量的急劇上升,通過我們相應(yīng)的監(jiān)控系統(tǒng)可以發(fā)現(xiàn),系統(tǒng)的某些頁面的性能開始出現(xiàn)惡化。 從用戶方的反饋,也證明了這點(diǎn)。此時(shí)此刻,有必要迅速排期,敏捷開發(fā),對這些頁面進(jìn)行調(diào)優(yōu)。

歡迎頁

  • 需求背景:歡迎頁是地推人員乃至總部各種角色人員進(jìn)入外賣運(yùn)營后臺的首頁,會顯示地推人員最想看到最關(guān)心的一些核心數(shù)據(jù),其重要性不言而喻,所以該頁面的性能惡化會嚴(yán)重影響到用戶體驗(yàn)。因此,首先需要優(yōu)化的就是歡迎頁。通過相應(yīng)定位和分析,發(fā)現(xiàn)導(dǎo)致性能惡化的主要原因有兩個(gè):數(shù)據(jù)接口層和計(jì)算展現(xiàn)層。

  • 解決方案:對癥下藥,分而治之。經(jīng)過仔細(xì)排查、分析定位,數(shù)據(jù)接口層采用接口調(diào)用批量化、異步RPC調(diào)用的方式來進(jìn)行有效優(yōu)化,計(jì)算展現(xiàn)層決定采用預(yù)先計(jì)算、再把計(jì)算好的結(jié)果緩存的方式來提高查詢速度。其中,緩存方案根據(jù)業(yè)務(wù)場景和技術(shù)特點(diǎn),選用Redis。定好方案后,快速開發(fā)上線。

  • 上線效果:上線后性能對比圖,如下:

組織架構(gòu)頁

  • 需求背景:組織架構(gòu)頁,采用了四層樹形結(jié)構(gòu)圖,一起呈現(xiàn)加載,第一版上線后發(fā)現(xiàn)性能非常差。用戶迫切希望對這個(gè)頁面的性能進(jìn)行調(diào)優(yōu)。

  • 解決方案:經(jīng)過分析代碼,定位到一個(gè)比較經(jīng)典的問題:里面執(zhí)行了太多次小數(shù)據(jù)量的SQL查詢。于是采用多個(gè)SQL合并成大SQL的方式,然后使用本地緩存來緩存這些數(shù)據(jù),合理預(yù)估數(shù)據(jù)量和性能,充分測試后上線。

  • 上線效果:上線后性能對比圖,如下:

訂單關(guān)聯(lián)樓宇頁

  • 需求背景:隨著訂單量日益增大,訂單表積累的數(shù)據(jù)日益增多,訂單關(guān)聯(lián)樓宇頁的性能也日益變差(響應(yīng)時(shí)間線性上升)。而這個(gè)頁面和地推人員的業(yè)績息息相關(guān),所以地推人員使用該頁面的頻率非常高,性能日益惡化極大地影響了地推人員的用戶體驗(yàn)。

  • 解決方案:經(jīng)過分析與設(shè)計(jì),決定采用當(dāng)時(shí)已有的訂單二級索引月分表來代替原始的訂單表來供前端的查詢請求;并且限制住篩選的時(shí)間條件,使得篩選的開始時(shí)間和結(jié)束時(shí)間不能跨月(事先和用戶溝通過,可以接受,能滿足用戶的基本需求),這樣就只需一個(gè)月分索引表即可,通過適當(dāng)?shù)墓δ芟拗苼磉_(dá)到性能的調(diào)優(yōu)。這樣從二級索引月分表中根據(jù)各種查詢條件查到最終的分頁的訂單ID集合,然后再根據(jù)訂單ID從訂單庫來查出相應(yīng)的訂單數(shù)據(jù)集合。

  • 上線效果:上線后發(fā)現(xiàn)在調(diào)用量幾乎沒怎么變的情況下,性能提升明顯,如下圖:

老司機(jī)簡介

邵曉明,美團(tuán)點(diǎn)評技術(shù)專家,目前是外賣事業(yè)部的商品與活動模塊的技術(shù)負(fù)責(zé)人。2014年1月加入原美團(tuán)外賣技術(shù)團(tuán)隊(duì),先后負(fù)責(zé)美團(tuán)外賣的商家、商品、活動、CRM、客服、品控審核等核心模塊與服務(wù)的研發(fā),經(jīng)歷了美團(tuán)外賣業(yè)務(wù)的從0到1以及后續(xù)的不斷演進(jìn)的過程。有豐富的后臺架構(gòu)、穩(wěn)定性建設(shè)、性能優(yōu)化、基礎(chǔ)組件與中間件、數(shù)據(jù)存儲與查詢方面的經(jīng)驗(yàn)。


今日薦文

點(diǎn)擊下方圖片即可閱讀

微信異步化改造實(shí)踐:

8億月活、萬臺機(jī)器背后的解決方案



喜歡我們的會點(diǎn)贊,愛我們的會分享!

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    精品精品国产自在久久高清| 中国美女偷拍福利视频| 日韩成人中文字幕在线一区| 99久久国产精品亚洲| 午夜福利大片亚洲一区| 中国一区二区三区人妻| 欧美韩日在线观看一区| 日本不卡在线视频你懂的| 国产精品一区二区视频成人| 一区二区三区四区亚洲专区| a久久天堂国产毛片精品| 国产精品超碰在线观看| 色婷婷成人精品综合一区| 欧美中文字幕日韩精品| 亚洲欧美日韩综合在线成成| 日韩在线视频精品中文字幕| 成人精品一区二区三区在线| 亚洲熟女乱色一区二区三区| 国产一级性生活录像片| 国产成人综合亚洲欧美日韩| 精产国品一二三区麻豆| 国产午夜福利在线观看精品| 欧美一区二区三区99| 中文字幕免费观看亚洲视频| 国产在线一区二区免费| 九九视频通过这里有精品| 国产精品午夜福利免费阅读 | 亚洲中文字幕日韩在线| 亚洲午夜精品视频观看| 久久99午夜福利视频| 日韩中文字幕有码午夜美女| 国产在线成人免费高清观看av| 国产午夜福利一区二区| 中文字幕日韩精品人一妻| 亚洲黄香蕉视频免费看| 国产精品视频一区二区秋霞| 女人高潮被爽到呻吟在线观看| 日本免费一级黄色录像| 亚洲国产av在线观看一区| 久久精品a毛片看国产成人| 高跟丝袜av在线一区二区三区|