如果緩衝失效,瞬間大量請求可能會直接存取資料庫,請問如何在代碼層面應該怎麼處理?

來源:互聯網
上載者:User
關鍵字 cache data return database write
最近有人問我這個問題,我個人沒有這方面的實戰經驗。我個人的想法是,由於訪問資料庫並寫入緩衝需要一定的時間。可能導致較早的部分請求直接讀取資料庫,當這部分資料要寫入緩衝時,判斷緩衝是否存在,不存在則寫入,存在則不寫入,並返回結果。
if ($cache) {
return $cache;
} else {
$data = read database;
if (!$cache) write $cache $data;
return $data;
}

但思前想後,覺得這樣的回答似乎沒有正確回答多個請求同時讀取資料庫的問題,雖然可以屏蔽後期的請求直接存取資料庫,但前期還是有多了連結直接存取了資料庫。不知道各位是否有更好的解決方案。求教!

回複內容:

答案都在問題裡了。

「如果緩衝失效」

在設計的時候就可以把 cache 設計成持久化的,避免因為失效導致緩衝被穿透。如果對穩定性有更高的要求,就在 cache 上下功夫做災備。比如 redis 的主從模式,rdb dump 等等。

如果系統或者涉及 cache 的 feature 是第一次上線,則需要提前對緩衝做預熱。方法有很多,寫指令碼或者灰階發布的方式都可以。


「瞬間大量的請求會訪問資料庫」

這裡有兩個問題,一是瞬間大量的請求怎麼處理,二是如何避免這麼多請求對資料庫造成壓力。

具體如何實施還得看業務情境,但解決的思路就是儘可能避免瞬間大量的請求,再就是大量請求產生時不要讓資料庫壓力過大。

限流的方法要看業務情境,並非所有情境都適合限流所以這裡不多說。所以這裡要限制的是後端到資料庫的最大串連數自己資料庫自己能接受的最大串連數。只要請求資料庫設計合理,即使有瞬間很高的並發一般也不會造成什麼實際的問題。

所以怎麼從代碼層面解決這個問題,是取決於你是如何設計這個系統的。二級緩衝。把資料放在一個失效時間比較長的key裡。雪崩的時候加鎖,保證只有一個php進程訪問資料庫,其餘的看到鎖就不再訪問,直接返回在緩衝裡的資料。效果就是雖然好幾個人沒看到最新的資料,再刷一下就成了。搶到鎖的看到了最新資料。鎖可以用mc的add來實現。在我大渣浪的時候這套東西扛過nba的直播,c好幾十k,資料庫沒事,頻寬才是問題facebook 放過一篇論文《Scaling Memcache at Facebook》有討論過這個問題:

3.2.1 Leases
We introduce a new mechanism we call leases to addresstwo problems: stale sets and thundering herds.

其中 "thundering herds" 正是樓主提到的資料庫穿透問題,一個熱的緩衝如果失效,在第一個訪問資料庫的請求得到結果寫入緩衝之前,期間的大量請求打穿到資料庫;然後 “stale set” 屬於資料一致性問題,假如一個執行個體更新了資料想去重新整理緩衝,而另一個執行個體讀 miss 嘗試讀取資料庫,這時兩次緩衝寫入順序不能保證,可能會導致到期資料寫入緩衝。

這兩個問題都是 look-aside cache 所固有的,需要提供一個機制來協調緩衝的寫入,這篇論文給出的方案就是 lease 機制,限制同一時刻一個鍵只有擁有唯一 lease 的用戶端才能有權寫入緩衝:
  • 如果 get 某鍵讀 miss,返回用戶端一個 64 位元的 lease;
  • 然後該鍵在寫入之前如果收到 get 請求,將返回一個 hot miss 報錯,用戶端依據它判斷自己要稍後重試,而不向資料庫讀取資料;
  • 如果該鍵收到 delete 請求,那麼會使 lease 失效;持有失效 lease 的 set 請求仍將成功,但後來的 get 請求將得到 hot miss 報錯,並攜帶一個新的 lease;這裡的 hot miss 報錯中帶有最後的值,但認為它處於 stale 狀態,留給用戶端去判斷是否採用它,在一致性要求不嚴格的情境中可以進一步減少資料庫請求;

這一來允許 memcache 服務端協調資料庫的訪問,從而解決這兩個問題。

不過 lease 方案並不完美,因為 1. 需要改 memcache;2. 仍泄露邏輯到用戶端,要求用戶端遵循 lease 和 hot miss 的約定。

在 facebook 後面的論文《TAO: Facebook's Distributed Data Store for the Social Graph》中介紹TAO 系統嘗試解決的問題之一提到:

Distributed control logic: In a lookaside cache architecturethe control logic is run on clients that don’t communicatewith each other. This increases the number offailure modes, and makes it difficult to avoid thunderingherds. Nishtala et al. provide an in-depth discussion ofthe problems and present leases, a general solution [21].For objects and associations the fixed API allows us tomove the control logic into the cache itself, where theproblem can be solved more efficiently.

也就是說,我們並不一定非 look aside cache 不可,如果把緩衝的修改入口封裝起來,走 write though cache,就不需要分布式地去協調所有用戶端,在一個地方排隊就夠了。

References
  • Scaling Memcache at Facebook
  • TAO: Facebook's Distributed Data Store for the Social Graph
  • https://www.quora.com/How-does-the-lease-token-solve-the-stale-sets-problem-in-Facebooks-memcached-servers
根據實際情境選擇方案,訪問資料庫的請求處理使用隊列並對同樣的請求做加鎖處理是比較穩妥的解決思路。
這種問題業內有個名詞,叫雪崩效應,就是說叢集在正常負載的時候沒有問題,一旦其中幾台伺服器崩了,伺服器過載的壓力壓到後端資料庫上面,就會導致整個叢集像雪崩一樣完全崩潰。
讀多寫少的業務情境下
對於這種問題,緩衝擋在資料庫伺服器前面是必須的,即問題的解決方案之一,根據sql查詢的條件在快取服務器層面做鎖處理,同樣的請求只放一個到後端資料庫上面,其它請求則阻塞等待資料更新。
其次是擋住這些緩衝大批失效導致的峰值,不允許直接連接資料庫進行查詢,在快取服務器向資料庫發起的請求處理上全部都要使用隊列,把峰值分攤到更長的時間段上避免後端資料庫受到衝擊。
此外為了避免大批緩衝失效又重建的這些緩衝再次同時失效的問題,快取服務器的資料緩衝時間需要去一個時間令牌分發的伺服器申請令牌,在基本緩衝時間的基礎上,再加上一個令牌緩衝時間,將峰值分攤到更長的時間上。
這樣的處理可以避免資料庫負載被峰值請求衝垮。
寫入比例更大的業務情境下:
對資料更新頻繁且即時性要求較高的時候,比如直播室情境,搶購情境,緩衝失效時間很短。需要考慮業務切分,把同類業務路由到不同伺服器上面,獨立開來。儘可能把寫入負載分攤到單點叢集可以承擔的程度。技術上面可選的策略就和業務相關度很大了,類似直播室的情境,甚至不需要走到資料庫伺服器,業務都在緩衝上面周轉,持久化到資料庫的業務則走非同步扔隊列裡面慢慢處理。電商搶購這類必須要校正的業務又不一樣,但是思路還是同樣的,秒殺業務瞬時進來的業務太大,那就加驗證碼加排隊,總之儘可能拖延提交訂單的時間,把瞬時負載分攤到系統可以接受的時間段內,資料庫層面依然還是加鎖與隊列,不過在隊列入隊上還需要做個鎖,一旦排隊長度超過商品總數的一個倍數(具體按需求過往訂單支付成功率計算),則加鎖將後續請求擋住不再入隊。
還有很多寫入業務量非常極端的情境,類似LBS產品的地理位置更新,SNS產品的訊息推送,大致思路都是類似的,加鎖和隊列,資料庫層面儘可能選用寫入效能優良的nosql承擔這些持久化的需求。否則也儘可能減少索引的使用,比如把mysql當key-value資料庫用等形式,儘可能減少寫入的效能消耗提高。在DB的監控或者介面層面處理高並發其實是區分宏觀和微觀的,你理解的並發是同一時刻多個請求讀資料庫,但是其實請求對於電腦一定不是同一時刻,只是宏觀上是同時。比如1-10毫秒之間或者微秒之間,那麼其實電腦是怎麼處理的呢?說白了是會對計算操作,io操作做獨立的進程來分配處理器的任務,記憶體的分配,比如同一時刻你在讀資料庫,同一時刻又有別的io操作,那麼你這裡也可以再類比一下電腦這種設計。

比如你在寫緩衝時,把這個寫操作推到一個獨立的進程,做一個寫緩衝的隊列,然後再做一個檢測進程來組織這個隊列的處理順序和失敗分支,比如提前和後排,比如寫快取作業失敗了,那麼進行隊列的下一個任務或者再試一次。所有寫入成功的快取資料再推入緩衝池,那麼這個隊列會保留更新緩衝的所有任務,也不會阻塞主進程,也就是使用者訪問的主進程不會被阻塞。他們只是去讀緩衝池的資料,不關心時效。如果寫得慢就讀老的,甚至資料庫掛了,都不會影響使用者讀資料。

並發的概念再仔細想想都是在一個相對的時間裡來看待的。電腦會自己處理,你也不用太操心,業務代碼只需要解決阻塞的問題就ok了。

ps:我是個前端,大過年無聊瞎扯的…摺疊吧。

再補充一下,如果緩衝失效了,這個問題要解決的是緩衝的容災處理辦法,比如多個緩衝池快速切換,負載平衡等。超大量請求直接存取資料庫,這個可以用多機多資料庫,多個進程來讀,來解決。我覺得從代碼層面不太好做…

因為解決辦法是擴容加分配請求到多機,不能即時擴容又想支援高並發大量請求是不可能的啊…光讓驢跑不給吃草,就想著怎麼給驢美容不好用啊…

-------

專業上這種雪崩後緩衝穿透直接到了db層,實際先掛的是頻寬,db不會是瓶頸,之後要解決可以使用最大串連數來控制讀資料庫,超過的使用2級緩衝來做,之後預警,或者動態擴容機制來解決。(諮詢的後端同事)throttle解決之道就在書上, 在(書名忘記了,封面上有動車的那個)那個小書上開始就是介紹的這個問題,然後提出了好幾種的解決方案。。三本書,作業系統,資料庫原理,分布式系統。很古老的問題。
1、假設單機緩衝。鎖機制就可以了,因為你的問題是讀資料,不是別的。無論是多進程還是多線程,加鎖就可以了。
2、用緩衝池,降低等待的延遲。
3、以上是作業系統的內容。
4、如果同時有更新操作,要考慮資料一致性的問題,即悲觀鎖或樂觀鎖。1、服務本身要提供過載保護、熔斷機制,說到底就是限流,要懂得保護自己
2、緩衝重建的時候要加鎖,cache mutex
  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.