架構與思維:秒殺和競拍的業務架構,永不過時的話題

Hello-Brand發表於2024-07-19

1 網際網路架構越來越複雜?

為啥感覺網際網路架構越來越複雜了,早期我們的系統,可能也就那麼少部分人使用,大都是一些後臺管理系統。
所以不用考慮很多東西,比如:

  • 流量少,無需考慮併發問題
  • 資料少,不用考慮什麼索引最佳化、分庫分表
  • 訪問不集中,不用考慮快取、過載保護
  • 如果資料不重要,不用考慮安全策略,甚至不用考慮容災備份
  • 可重複提交,所以不用關係冪等性
  • 允許短暫當機和定期關停維護,所以不用考慮多活架構

但是隨著網際網路的普及和使用者的激增,為了應對流量增量帶來的各種問題,我們的架構體系衍生出很多強大的技術方案。

2 什麼是秒殺/競拍業務

秒殺業務也是隨著網際網路電商的發展而不斷普及的,我們來看看普通業務和秒殺業務的區別

2.1 普通的業務

  1. 微信的個人資訊:個人的註冊資訊,公眾號、影片號的基礎資訊,微信好友列表,微信群列表。這種是 1:1 的,一般也不會被別人看到。
  2. 微信朋友圈:你盆友圈公開的內容是可以被多個好友看到的,你也可以對應看到你多個好友的盆友圈。這種是 1:n 的,多讀的一種場景。

2.2 秒殺/競拍業務

只有少量的資料,卻會在集中的時間段被一批人看到和搶購,集中式的高頻讀寫。
業內也稱為 群蜂請求 ,你可以想象下你捅了馬蜂窩的場景。哈哈哈

典型秒殺/競拍業務案例:

  1. 春運前的火車票開售那一刻,可能瞬間有千萬級請求湧入
  2. 將來某個遙遙領先開售,可能是一秒售罄

這些業務場景有如下技術難點:

  1. 瞬時流量特別大,你的接入層、應用層、資料層等能否扛得住
  2. 大量流量湧入 對一個資料進行操作,怎麼保證資料原子增減、順序公平性,怎麼保證資料不超賣
  3. 如何 保證資料安全,如防攻擊、防刷數、保持冪等
  4. 如果使用 併發控制,如何保證不產生死鎖

所以,一個優秀的秒殺業務架構,在現在的網際網路業務中,是一個永不過時的話題

3 如何最佳化

這邊只針對幾個對秒殺業務有效改進的點做展開,什麼叢集動態擴容、流量控制、彈性伸縮、智慧限流啊,可以參考我的這篇文章《千萬級流量衝擊下,如何保證極致效能》。

3.1 清除無效請求

儘量在前面就把一些無效請求給清理掉,所以這些操作Web前端 或者 App Client端做就行了,越前端越好,儘量不要傷害到服務端,比如:

  • 未登入攔截
  • 重複提交攔截(未響應則按鈕置灰,直至響應或者5S超時才恢復,冪等保證)
  • 頻繁提交攔截(單使用者一分鐘不超過100次,避免AI刷機)
  • 驗證碼攔截(避免AI刷資料、駭客攻擊等)
  • 參與條件攔截(可提前載入名單):如使用者等級不夠、註冊未滿3個月、使用者進入黑名單等

image

3.2 服務端+快取層做高效原子操作

公共資料做快取
快取是提升系統效能的重要手段。透過快取熱點資料,快取還可以提高資料的訪問速度,見很少對資料庫的訪問速度,提升使用者體驗。Redis單機每秒10w沒什麼問題,再加上多叢集多副本模式。

原子操作保證秒殺的計數
在Redis中,高效地進行原子計數通常使用INCRINCRBYDECRDECRBY等命令。這些命令都是原子操作,意味著在執行時不會被其他Redis命令打斷,從而保證了計數的準確性和一致性。

# 計算已售賣1000臺庫裡南
> INCRBY cullinan_counter 1000

# 獲取當前售賣數量
> GET cullinan_counter
> 1000

# 超過1000,返回秒殺失敗

佇列保證請求有序進入
使用Redis的 Stream 佇列功能。Stream 實際上是一個 key,你可以使用 XADD 命令向其中新增訊息。

XADD mystream * field1 value1 field2 value2

這裡 mystream 是 Stream 的名稱,* 表示讓 Redis 自動生成一個唯一的訊息 ID。field1 value1 和 field2 value2 是訊息的內容,你可以根據需要新增任意數量的欄位。
如果你只有1000臺庫裡南供搶購,那麼第1001就不要進入佇列了。

擴充套件閱讀
快取可以擴充套件閱讀作者的這個系列的文章:★ Redis24篇集合

image

3.3 資料層做終兜底

經過上面的保證之後,到資料層的量就很少了,大機率就是你定額的商品數量同等的數量。
比如1000,資料庫絕對的扛得住的。
唯一可以做的就是檢查數量是否符合預期,這個可以建立約束或者觸發器來實現。

image

3.4 全球式業務,單元化處理

有些人可能會說,我的商品全球售賣,那我的快取中心、資料中心放哪裡,如果放中國,那跨地域跨機房訪問,在0.1微妙都能決定我是不是買得到,歐洲的客戶鐵定搶不到庫裡南了。
現在的做法一般是單元化隔離,比如:

image

A/B中心都有這樣的快取或者資料結構,配置中心統一下發配置。然後在各自的單元裡面玩耍,互不干預。 秒殺業務千萬不要想著跨地域+跨機房,使用者存在不公平性。

4 寫在最後

  1. 無效請求攔截,儘量在前端完成,避免走入後端,造成服務端壓力
  2. 快取支援高效能檢索、原子計算和有序佇列
  3. 資料層做儲存兜底
  4. 分治原理:單元化隔離,避免集中處理

相關文章