一個秒殺系統的設計思考,超詳細!

前言

秒殺你們都不陌生。自2011年首次出現以來,不管是雙十一購物仍是 12306 搶票,秒殺場景已隨處可見。簡單來講,秒殺就是在同一時刻大量請求爭搶購買同一商品並完成交易的過程。從架構視角來看,秒殺系統本質是一個高性能、高一致、高可用的三高系統。而打造並維護一個超大流量的秒殺系統須要進行哪些關注,就是本文討論的話題。前端

總體思考

首先從高維度出發,總體思考問題。秒殺無外乎解決兩個核心問題,一是併發讀,一是併發寫,對應到架構設計,就是高可用、一致性和高性能的要求。關於秒殺系統的設計思考,本文即基於此 3 層依次推動,簡述以下——sql

  • 高性能。 秒殺涉及高讀和高寫的支持,如何支撐高併發,如何抵抗高IOPS?核心優化理念實際上是相似的:高讀就儘可能"少讀"或"讀少",高寫就數據拆分。本文將從動靜分離、熱點優化以及服務端性能優化 3 個方面展開
  • 一致性。 秒殺的核心關注是商品庫存,有限的商品在同一時間被多個請求同時扣減,並且要保證準確性,顯而易見是一個難題。如何作到既很少又很多?本文將從業界通用的幾種減庫存方案切入,討論一致性設計的核心邏輯
  • 高可用。 大型分佈式系統在實際運行過程當中面對的工況是很是複雜的,業務流量的突增、依賴服務的不穩定、應用自身的瓶頸、物理資源的損壞等方方面面都會對系統的運行帶來大大小小的的衝擊。如何保障應用在複雜工況環境下還能高效穩定運行,如何預防和麪對突發問題,系統設計時應該從哪些方面着手?本文將從架構落地的全景視角進行關注思考

高性能

1 動靜分離

你們可能會注意到,秒殺過程當中你是不須要刷新整個頁面的,只有時間在不停跳動。這是由於通常都會對大流量的秒殺系統作系統的靜態化改造,即數據意義上的動靜分離。動靜分離三步走:一、數據拆分;二、靜態緩存;三、數據整合。數據庫

1.1 數據拆分

動靜分離的首要目的是將動態頁面改形成適合緩存的靜態頁面。所以第一步就是分離出動態數據,主要從如下 2 個方面進行:segmentfault

  1. 用戶。用戶身份信息包括登陸狀態以及登陸畫像等,相關要素能夠單獨拆分出來,經過動態請求進行獲取;與之相關的廣平推薦,如用戶偏好、地域偏好等,一樣能夠經過異步方式進行加載
  2. 時間。秒殺時間是由服務端統一管控的,能夠經過動態請求進行獲取 這裏你能夠打開電商平臺的一個秒殺頁面,看看這個頁面裏都有哪些動靜數據。
1.2 靜態緩存

分離出動靜態數據以後,第二步就是將靜態數據進行合理的緩存,由此衍生出兩個問題:一、怎麼緩存;二、哪裏緩存後端

1.2.1 怎麼緩存

靜態化改造的一個特色是直接緩存整個 HTTP 鏈接而不是僅僅緩存靜態數據,如此一來,Web 代理服務器根據請求 URL,能夠直接取出對應的響應體而後直接返回,響應過程無需重組 HTTP 協議,也無需解析 HTTP 請求頭。而做爲緩存鍵,URL惟一化是必不可少的,只是對於商品系統,URL 自然是能夠基於商品 ID 來進行惟一標識的,好比淘寶的 https://item.taobao.com/item....。瀏覽器

1.2.2 哪裏緩存

靜態數據緩存到哪裏呢?能夠有三種方式:一、瀏覽器;二、CDN ;三、服務端。緩存

瀏覽器固然是第一選擇,但用戶的瀏覽器是不可控的,主要體如今若是用戶不主動刷新,系統很難主動地把消息推送給用戶(注意,當討論靜態數據時,潛臺詞是 「相對不變」,言外之意是 「可能會變」),如此可能會致使用戶端在很長一段時間內看到的信息都是錯誤的。對於秒殺系統,保證緩存能夠在秒級時間內失效是不可或缺的。安全

服務端主要進行動態邏輯計算及加載,自己並不擅長處理大量鏈接,每一個鏈接消耗內存較多,同時 Servlet 容器解析 HTTP 較慢,容易侵佔邏輯計算資源;另外,靜態數據下沉至此也會拉長請求路徑。性能優化

所以一般將靜態數據緩存在 CDN,其自己更擅長處理大併發的靜態文件請求,既能夠作到主動失效,又離用戶儘量近,同時規避 Java 語言層面的弱點。須要注意的是,上 CDN 有如下幾個問題須要解決:服務器

  1. 失效問題。任何一個緩存都應該是有時效的,尤爲對於一個秒殺場景。因此,系統須要保證全國各地的 CDN 在秒級時間內失效掉緩存信息,這實際對 CDN 的失效系統要求是很高的
  2. 命中率問題。高命中是緩存系統最爲核心的性能要求,否則緩存就失去了意義。若是將數據放到全國各地的 CDN ,勢必會致使請求命中同一個緩存的可能性下降,那麼命中率就成爲一個問題

所以,將數據放到全國全部的 CDN 節點是不太現實的,失效問題、命中率問題都會面臨比較大的挑戰。更爲可行的作法是選擇若干 CDN 節點進行靜態化改造,節點的選取一般須要知足如下幾個條件:

  1. 臨近訪問量集中的地區
  2. 距離主站較遠的地區
  3. 節點與主站間網絡質量良好的地區

基於以上因素,選擇 CDN 的二級緩存比較合適,由於二級緩存數量偏少,容量也更大,訪問量相對集中,這樣就能夠較好解決緩存的失效問題以及命中率問題,是當前比較理想的一種 CDN 化方案。部署方式以下圖所示:

1.3 數據整合

分離出動靜態數據以後,前端如何組織數據頁就是一個新的問題,主要在於動態數據的加載處理,一般有兩種方案:ESI(Edge Side Includes)方案和 CSI(Client Side Include)方案。

  1. ESI 方案:Web 代理服務器上請求動態數據,並將動態數據插入到靜態頁面中,用戶看到頁面時已是一個完整的頁面。這種方式對服務端性能要求高,但用戶體驗較好
  2. CSI 方案:Web 代理服務器上只返回靜態頁面,前端單獨發起一個異步 JS 請求動態數據。這種方式對服務端性能友好,但用戶體驗稍差
1.4 小結

動靜分離對於性能的提高,抽象起來只有兩點,一是數據要儘可能少,以便減小不必的請求,二是路徑要儘可能短,以便提升單次請求的效率。具體方法其實就是基於這個大方向進行的。

2 熱點優化

熱點分爲熱點操做和熱點數據,如下分開進行討論。

2.1 熱點操做

零點刷新、零點下單、零點添加購物車等都屬於熱點操做。熱點操做是用戶的行爲,很差改變,但能夠作一些限制保護,好比用戶頻繁刷新頁面時進行提示阻斷。

2.2 熱點數據

熱點數據的處理三步走,一是熱點識別,二是熱點隔離,三是熱點優化。

2.2.1 熱點識別

熱點數據分爲靜態熱點和動態熱點,具體以下:

  1. 靜態熱點:可以提早預測的熱點數據。大促前夕,能夠根據大促的行業特色、活動商家等緯度信息分析出熱點商品,或者經過賣家報名的方式提早篩選;另外,還能夠經過技術手段提早預測,例如對買家天天訪問的商品進行大數據計算,而後統計出 TOP N 的商品,便可視爲熱點商品
  2. 動態熱點:沒法提早預測的熱點數據。冷熱數據每每是隨實際業務場景發生交替變化的,尤爲是現在直播賣貨模式的興起——帶貨商臨時作一個廣告,就有可能致使一件商品在短期內被大量購買。因爲此類商品平常訪問較少,即便在緩存系統中一段時間後也會被逐出或過時掉,甚至在db中也是冷數據。瞬時流量的涌入,每每致使緩存被擊穿,請求直接到達DB,引起DB壓力過大

所以秒殺系統須要實現熱點數據的動態發現能力,一個常見的實現思路是:

  1. 異步採集交易鏈路各個環節的熱點 Key 信息,如 Nginx採集訪問URL或 Agent 採集熱點日誌(一些中間件自己已具有熱點發現能力),提早識別潛在的熱點數據
  2. 聚合分析熱點數據,達到必定規則的熱點數據,經過訂閱分發推送到鏈路系統,各系統根據自身需求決定如何處理熱點數據,或限流或緩存,從而實現熱點保護

須要注意的是:

  1. 熱點數據採集最好採用異步方式,一方面不會影響業務的核心交易鏈路,一方面能夠保證採集方式的通用性
  2. 熱點發現最好作到秒級實時,這樣動態發現纔有意義,實際上也是對核心節點的數據採集和分析能力提出了較高的要求
2.2.2 熱點隔離

熱點數據識別出來以後,第一原則就是將熱點數據隔離出來,不要讓 1% 影響到另外的 99%,能夠基於如下幾個層次實現熱點隔離:

  1. 業務隔離。秒殺做爲一種營銷活動,賣家須要單獨報名,從技術上來講,系統能夠提早對已知熱點作緩存預熱
  2. 系統隔離。系統隔離是運行時隔離,經過分組部署和另外 99% 進行分離,另外秒殺也能夠申請單獨的域名,入口層就讓請求落到不一樣的集羣中
  3. 數據隔離。秒殺數據做爲熱點數據,能夠啓用單獨的緩存集羣或者DB服務組,從而更好的實現橫向或縱向能力擴展

固然,實現隔離還有不少種辦法。好比,能夠按照用戶來區分,爲不一樣的用戶分配不一樣的 Cookie,入口層路由到不一樣的服務接口中;再好比,域名保持一致,但後端調用不一樣的服務接口;又或者在數據層給數據打標進行區分等等,這些措施的目的都是把已經識別的熱點請求和普通請求區分開來。

2.2.3 熱點優化

熱點數據隔離以後,也就方便對這 1% 的請求作針對性的優化,方式無外乎兩種:

  1. 緩存:熱點緩存是最爲有效的辦法。若是熱點數據作了動靜分離,那麼能夠長期緩存靜態數據
  2. 限流:流量限制更可能是一種保護機制。須要注意的是,各服務要時刻關注請求是否觸發限流並及時進行review
2.2.4 小結

數據的熱點優化與動靜分離是不同的,熱點優化是基於二八原則對數據進行了縱向拆分,以便進行鍼對性地處理。熱點識別和隔離不只對「秒殺」這個場景有意義,對其餘的高性能分佈式系統也很是有參考價值。

3 系統優化

對於一個軟件系統,提升性能能夠有不少種手段,如提高硬件水平、調優JVM 性能,這裏主要關注代碼層面的性能優化——

  1. 減小序列化:減小 Java 中的序列化操做能夠很好的提高系統性能。序列化大部分是在 RPC 階段發生,所以應該儘可能減小 RPC 調用,一種可行的方案是將多個關聯性較強的應用進行 「合併部署」,從而減小不一樣應用之間的 RPC 調用(微服務設計規範)
  2. 直接輸出流數據:只要涉及字符串的I/O操做,不管是磁盤 I/O 仍是網絡 I/O,都比較耗費 CPU 資源,由於字符須要轉換成字節,而這個轉換又必須查表編碼。因此對於經常使用數據,好比靜態字符串,推薦提早編碼成字節並緩存,具體到代碼層面就是經過 OutputStream() 類函數從而減小數據的編碼轉換;另外,熱點方法toString()不要直接調用ReflectionToString實現,推薦直接硬編碼,而且只打印DO的基礎要素和核心要素
  3. 裁剪日誌異常堆棧:不管是外部系統異常仍是應用自己異常,都會有堆棧打出,超大流量下,頻繁的輸出完整堆棧,只會加重系統當前負載。能夠經過日誌配置文件控制異常堆棧輸出的深度
  4. 去組件框架:極致優化要求下,能夠去掉一些組件框架,好比去掉傳統的 MVC 框架,直接使用 Servlet 處理請求。這樣能夠繞過一大堆複雜且用處不大的處理邏輯,節省毫秒級的時間,固然,須要合理評估你對框架的依賴程度

4 總結一下

性能優化須要一個基準值,因此係統還須要作好應用基線,好比性能基線(什麼時候性能忽然降低)、成本基線(去年大促用了多少機器)、鏈路基線(核心流程發生了哪些變化),經過基線持續關注系統性能,促使系統在代碼層面持續提高編碼質量、業務層面及時下掉不合理調用、架構層面不斷優化改進。

一致性

秒殺系統中,庫存是個關鍵數據,賣不出去是個問題,超賣更是個問題。秒殺場景下的一致性問題,主要就是庫存扣減的準確性問題。

1 減庫存的方式

電商場景下的購買過程通常分爲兩步:下單和付款。「提交訂單」即爲下單,「支付訂單」即爲付款。基於此設定,減庫存通常有如下幾個方式:

  1. 下單減庫存。買家下單後,扣減商品庫存。下單減庫存是最簡單的減庫存方式,也是控制最爲精確的一種
  2. 付款減庫存。買家下單後,並不當即扣減庫存,而是等到付款後才真正扣減庫存。但由於付款時才減庫存,若是併發比較高,可能出現買家下單後付不了款的狀況,由於商品已經被其餘人買走了
  3. 預扣庫存。這種方式相對複雜一些,買家下單後,庫存爲其保留必定的時間(如 15 分鐘),超過這段時間,庫存自動釋放,釋放後其餘買家能夠購買

可以看到,減庫存方式是基於購物過程的多階段進行劃分的,但不管是在下單階段仍是付款階段,都會存在一些問題,下面進行具體分析。

2 減庫存的問題

2.1 下單減庫存

優點:用戶體驗最好。下單減庫存是最簡單的減庫存方式,也是控制最精確的一種。下單時能夠直接經過數據庫事務機制控制商品庫存,因此必定不會出現已下單卻付不了款的狀況。

劣勢:可能賣不出去。正常狀況下,買家下單後付款機率很高,因此不會有太大問題。但有一種場景例外,就是當賣家參加某個促銷活動時,競爭對手經過惡意下單的方式將該商品所有下單,致使庫存清零,那麼這就不能正常售賣了——要知道,惡意下單的人是不會真正付款的,這正是 「下單減庫存」 的不足之處。

2.2 付款減庫存

優點:必定實際售賣。「下單減庫存」 可能致使惡意下單,從而影響賣家的商品銷售, 「付款減庫存」 因爲須要付出真金白銀,能夠有效避免。

劣勢:用戶體驗較差。用戶下單後,不必定會實際付款,假設有 100 件商品,就可能出現 200 人下單成功的狀況,由於下單時不會減庫存,因此也就可能出現下單成功數遠遠超過真正庫存數的狀況,這尤爲會發生在大促的熱門商品上。如此一來就會致使不少買家下單成功後卻付不了款,購物體驗天然是比較差的。

2.3 預扣庫存

優點:緩解了以上兩種方式的問題。預扣庫存實際就是「下單減庫存」和 「付款減庫存」兩種方式的結合,將兩次操做進行了先後關聯,下單時預扣庫存,付款時釋放庫存。

劣勢:並無完全解決以上問題。好比針對惡意下單的場景,雖然能夠把有效付款時間設置爲 10 分鐘,但惡意買家徹底能夠在 10 分鐘以後再次下單。

2.4 小結

減庫存的問題主要體如今用戶體驗和商業訴求兩方面,其本質緣由在於購物過程存在兩步甚至多步操做,在不一樣階段減庫存,容易存在被惡意利用的漏洞。

3 實際如何減庫存

業界最爲常見的是預扣庫存。不管是外賣點餐仍是電商購物,下單後通常都有個 「有效付款時間」,超過該時間訂單自動釋放,這就是典型的預扣庫存方案。但如上所述,預扣庫存還須要解決惡意下單的問題,保證商品賣的出去;另外一方面,如何避免超賣,也是一個痛點。

  1. 賣的出去:惡意下單的解決方案主要仍是結合安全和反做弊措施來制止。好比,識別頻繁下單不付款的買家並進行打標,這樣能夠在打標買家下單時不減庫存;再好比爲大促商品設置單人最大購買件數,一人最多隻能買 N 件商品;又或者對重複下單不付款的行爲進行次數限制阻斷等
  2. 避免超賣:庫存超賣的狀況實際分爲兩種。對於普通商品,秒殺只是一種大促手段,即便庫存超賣,商家也能夠經過補貨來解決;而對於一些商品,秒殺做爲一種營銷手段,徹底不容許庫存爲負,也就是在數據一致性上,須要保證大併發請求時數據庫中的庫存字段值不能爲負,通常有多種方案:一是在經過事務來判斷,即保證減後庫存不能爲負,不然就回滾;二是直接設置數據庫字段類型爲無符號整數,這樣一旦庫存爲負就會在執行 SQL 時報錯;三是使用 CASE WHEN 判斷語句:
 

業務手段保證商品賣的出去,技術手段保證商品不會超賣,庫存問題歷來就不是簡單的技術難題,解決問題的視角是多種多樣的。

4 一致性性能的優化

庫存是個關鍵數據,更是個熱點數據。對系統來講,熱點的實際影響就是 「高讀」 和 「高寫」,也是秒殺場景下最爲核心的一個技術難題。

4.1 高併發讀

秒殺場景解決高併發讀問題,關鍵詞是「分層校驗」。即在讀鏈路時,只進行不影響性能的檢查操做,如用戶是否具備秒殺資格、商品狀態是否正常、用戶答題是否正確、秒殺是否已經結束、是否非法請求等,而不作一致性校驗等容易引起瓶頸的檢查操做;直到寫鏈路時,纔對庫存作一致性檢查,在數據層保證最終準確性。

所以,在分層校驗設定下,系統能夠採用分佈式緩存甚至LocalCache來抵抗高併發讀。即容許讀場景下必定的髒數據,這樣只會致使少許本來無庫存的下單請求被誤認爲是有庫存的,等到真正寫數據時再保證最終一致性,由此作到高可用和一致性之間的平衡。

實際上,分層校驗的核心思想是:不一樣層次儘量過濾掉無效請求,只在「漏斗」 最末端進行有效處理,從而縮短系統瓶頸的影響路徑。

4.2 高併發寫

高併發寫的優化方式,一種是更換DB選型,一種是優化DB性能,如下分別進行討論。

4.2.1 更換DB選型

秒殺商品和普通商品的減庫存是有差別的,核心區別在數據量級小、交易時間短,所以可否把秒殺減庫存直接放到緩存系統中實現呢,也就是直接在一個帶有持久化功能的緩存中進行減庫存操做,好比 Redis?

若是減庫存邏輯很是單一的話,好比沒有複雜的 SKU 庫存和總庫存這種聯動關係的話,我的認爲是徹底能夠的。但若是有比較複雜的減庫存邏輯,或者須要使用到事務,那就必須在數據庫中完成減庫存操做。

4.2.2 優化DB性能

庫存數據落地到數據庫實現實際上是一行存儲(MySQL),所以會有大量線程來競爭 InnoDB 行鎖。但併發越高,等待線程就會越多,TPS 降低,RT 上升,吞吐量會受到嚴重影響——注意,這裏假設數據庫已基於上文【性能優化】完成數據隔離,以便於討論聚焦 。

解決併發鎖的問題,有兩種辦法:

  1. 應用層排隊。經過緩存加入集羣分佈式鎖,從而控制集羣對數據庫同一行記錄進行操做的併發度,同時也能控制單個商品佔用數據庫鏈接的數量,防止熱點商品佔用過多的數據庫鏈接
  2. 數據層排隊。應用層排隊是有損性能的,數據層排隊是最爲理想的。業界中,阿里的數據庫團隊開發了針對InnoDB 層上的補丁程序(patch),能夠基於DB層對單行記錄作併發排隊,從而實現秒殺場景下的定製優化——注意,排隊和鎖競爭是有區別的,若是熟悉 MySQL 的話,就會知道 InnoDB 內部的死鎖檢測,以及 MySQL Server 和 InnoDB 的切換都是比較消耗性能的。另外阿里的數據庫團隊還作了不少其餘方面的優化,如 COMMIT_ON_SUCCESS 和 ROLLBACK_ON_FAIL 的補丁程序,經過在 SQL 里加入提示(hint),實現事務不須要等待實時提交,而是在數據執行完最後一條 SQL 後,直接根據 TARGET_AFFECT_ROW 的結果進行提交或回滾,減小網絡等待的時間(毫秒級)。目前阿里已將包含這些補丁程序的 MySQL 開源:AliSQL
4.3 小結

高讀和高寫的兩種處理方式截然不同。讀請求的優化空間要大一些,而寫請求的瓶頸通常都在存儲層,優化思路的本質仍是基於 CAP 理論作平衡。

5 總結一下

固然,減庫存還有不少細節問題,例如預扣的庫存超時後如何進行回補,再好比第三方支付如何保證減庫存和付款時的狀態一致性,這些也是很大的挑戰。

高可用

盯過秒殺流量監控的話,會發現它不是一條蜿蜒而起的曲線,而是一條挺拔的直線,這是由於秒殺請求高度集中於某一特定的時間點。這樣一來就會形成一個特別高的零點峯值,而對資源的消耗也幾乎是瞬時的。因此秒殺系統的可用性保護是不可或缺的。

1 流量削峯

對於秒殺的目標場景,最終可以搶到商品的人數是固定的,不管 100 人和 10000 人蔘加結果都是同樣的,即有效請求額度是有限的。併發度越高,無效請求也就越多。但秒殺做爲一種商業營銷手段,活動開始以前是但願有更多的人來刷頁面,只是真正開始後,秒殺請求不是越多越好。所以系統能夠設計一些規則,人爲的延緩秒殺請求,甚至能夠過濾掉一些無效請求。

1.1 答題

早期秒殺只是簡單的點擊秒殺按鈕,後來才增長了答題。爲何要增長答題呢?主要是經過提高購買的複雜度,達到兩個目的:

  1. 防止做弊。早期秒殺器比較猖獗,存在惡意買家或競爭對手使用秒殺器掃貨的狀況,商家沒有達到營銷的目的,因此增長答題來進行限制
  2. 延緩請求。零點流量的起效時間是毫秒級的,答題能夠人爲拉長峯值下單的時長,由以前的 <1s 延長到 <10s。這個時間對於服務端很是重要,會大大減輕高峯期併發壓力;另外,因爲請求具備前後順序,答題後置的請求到來時可能已經沒有庫存了,所以根本沒法下單,此階段落到數據層真正的寫也就很是有限了

須要注意的是,答題除了作正確性驗證,還須要對提交時間作驗證,好比<1s 人爲操做的可能性就很小,能夠進一步防止機器答題的狀況。

答題目前已經使用的很是廣泛了,本質是經過在入口層削減流量,從而讓系統更好地支撐瞬時峯值。

1.2 排隊

最爲常見的削峯方案是使用消息隊列,經過把同步的直接調用轉換成異步的間接推送緩衝瞬時流量。除了消息隊列,相似的排隊方案還有不少,例如:

  1. 線程池加鎖等待
  2. 本地內存蓄洪等待
  3. 本地文件序列化寫,再順序讀

排隊方式的弊端也是顯而易見的,主要有兩點:

  1. 請求積壓。流量高峯若是長時間持續,達到了隊列的水位上限,隊列一樣會被壓垮,這樣雖然保護了下游系統,可是和請求直接丟棄也沒多大區別
  2. 用戶體驗。異步推送的實時性和有序性天然是比不上同步調用的,由此可能出現請求先發後至的狀況,影響部分敏感用戶的購物體驗

排隊本質是在業務層將一步操做轉變成兩步操做,從而起到緩衝的做用,但鑑於此種方式的弊端,最終仍是要基於業務量級和秒殺場景作出妥協和平衡。

1.3 過濾

過濾的核心結構在於分層,經過在不一樣層次過濾掉無效請求,達到數據讀寫的精準觸發。常見的過濾主要有如下幾層:

  1. 讀限流:對讀請求作限流保護,將超出系統承載能力的請求過濾掉
  2. 讀緩存:對讀請求作數據緩存,將重複的請求過濾掉
  3. 寫限流:對寫請求作限流保護,將超出系統承載能力的請求過濾掉
  4. 寫校驗:對寫請求作一致性校驗,只保留最終的有效數據

過濾的核心目的是經過減小無效請求的數據IO保障有效請求的IO性能。

1.4 小結

系統能夠經過入口層的答題、業務層的排隊、數據層的過濾達到流量削峯的目的,本質是在尋求商業訴求與架構性能之間的平衡。另外,新的削峯手段也層出不窮,以業務切入居多,好比零點大促時同步發放優惠券或發起抽獎活動,將一部分流量分散到其餘系統,這樣也能起到削峯的做用。

Plan B

當一個系統面臨持續的高峯流量時,實際上是很難單靠自身調整來恢復狀態的,平常運維沒有人可以預估全部狀況,意外老是沒法避免。尤爲在秒殺這一場景下,爲了保證系統的高可用,必須設計一個 Plan B 方案來進行兜底。

高可用建設,實際上是一個系統工程,貫穿在系統建設的整個生命週期。

具體來講,系統的高可用建設涉及架構階段、編碼階段、測試階段、發佈階段、運行階段,以及故障發生時,逐一進行分析:

  1. 架構階段:考慮系統的可擴展性和容錯性,避免出現單點問題。例如多地單元化部署,即便某個IDC甚至地市出現故障,仍不會影響系統運轉
  2. 編碼階段:保證代碼的健壯性,例如RPC調用時,設置合理的超時退出機制,防止被其餘系統拖垮,同時也要對沒法預料的返回錯誤進行默認的處理
  3. 測試階段:保證CI的覆蓋度以及Sonar的容錯率,對基礎質量進行二次校驗,並按期產出總體質量的趨勢報告
  4. 發佈階段:系統部署最容易暴露錯誤,所以要有前置的checklist模版、中置的上下游周知機制以及後置的回滾機制
  5. 運行階段:系統多數時間處於運行態,最重要的是運行時的實時監控,及時發現問題、準確報警並能提供詳細數據,以便排查問題
  6. 故障發生:首要目標是及時止損,防止影響面擴大,而後定位緣由、解決問題,最後恢復服務

對於平常運維而言,高可用更可能是針對運行階段而言的,此階段須要額外進行增強建設,主要有如下幾種手段:

  1. 預防:創建常態壓測體系,按期對服務進行單點壓測以及全鏈路壓測,摸排水位
  2. 管控:作好線上運行的降級、限流和熔斷保護。須要注意的是,不管是限流、降級仍是熔斷,對業務都是有損的,因此在進行操做前,必定要和上下游業務確認好再進行。就拿限流來講,哪些業務能夠限、什麼狀況下限、限流時間多長、什麼狀況下進行恢復,都要和業務方反覆確認
  3. 監控:創建性能基線,記錄性能的變化趨勢;創建報警體系,發現問題及時預警
  4. 恢復:遇到故障可以及時止損,並提供快速的數據訂正工具,不必定要好,但必定要有 在系統建設的整個生命週期中,每一個環節中均可能犯錯,甚至有些環節犯的錯,後面是沒法彌補的或者成本極高的。因此高可用是一個系統工程,必須放到整個生命週期中進行全面考慮。同時,考慮到服務的增加性,高可用更須要長期規劃並進行體系化建設。

3 總結一下

高可用實際上是在說 「穩定性」,穩定性是一個平時不重要,但出了問題就要命的事情,然而它的落地又是一個問題——平時業務發展良好,穩定性建設就會降級給業務讓路。解決這個問題必須在組織上有所保障,好比讓業務負責人背上穩定性績效指標,同時在部門中創建穩定性建設小組,小組成員由每條線的核心力量兼任,績效由穩定性負責人來打分,這樣就能夠把體系化的建設任務落實到具體的業務系統中了。

我的總結

一個秒殺系統的設計,能夠根據不一樣級別的流量,由簡單到複雜打造出不一樣的架構,本質是各方面的取捨和權衡。固然,你可能注意到,本文並無涉及具體的選型方案,由於這些對於架構來講並不重要,做爲架構師,應該時刻提醒本身主線是什麼。

同時也在這裏抽象、提煉一下,主要是我的對於秒殺設計的提綱式整理,方便各位同窗進行參考—!

來源:http://www.javashuo.com/article/p-osusvoam-bu.html

相關文章
相關標籤/搜索