首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL 高並發計數器設計:從單行熱點到隨機分桶槽位優化方案

     編輯:MySQL 綜合教學

MySQL 高並發計數器設計:從單行熱點到隨機分桶槽位優化方案

文章瀏覽數、商品點讚數、API 呼叫計數 — 高並發計數器是典型熱點寫入場景。單行 UPDATE counter SET cnt=cnt+1 會導致行鎖爭用、間隙鎖衝突、Redo Log 爭用,嚴重限制吞吐。本文演進三代方案,實現無鎖高並發計數。

演進路線圖

代數架構核心思想並發能力缺點
第 1 代業務表混合計數欄article_view INT 在主表極低 (行鎖串行)讀寫耦合、主表膨脹、鎖爭用劇烈
第 2 代獨立計數表 + 單行article_id PK, view BIGINT低 (仍是單行熱點)分離了壓力但未解決熱點
第 3 代分桶槽位article_id, slot, view 多行聚合高 (分散鎖爭用)讀取需 SUM 聚合、槽位數需調優

第 3 代方案:隨機分桶槽位

表結構

CREATE TABLE article_view (
  article_id BIGINT NOT NULL,
  slot       TINYINT UNSIGNED NOT NULL COMMENT '槽位 0-99',
  view       BIGINT NOT NULL DEFAULT 0,
  PRIMARY KEY (article_id, slot)
) ENGINE=InnoDB;

寫入邏輯:隨機選槽 + UPSERT

-- 單條 SQL 完成「存在則加1,不存在則插1」
INSERT INTO article_view (article_id, slot, view)
VALUES (12345, FLOOR(RAND()*100), 1)
ON DUPLICATE KEY UPDATE view = view + 1;
  • RAND()*100:0-99 隨機槽位,將寫入壓力分散到 100 行
  • ON DUPLICATE KEY UPDATE:原子性 UPSERT,無需 SELECT 判斷
  • 槽位數建議:CPU 核心數 × 4 - 100,視實際並發調整

讀取邏輯:聚合求和

-- 獲取文章總瀏覽數
SELECT SUM(view) AS total_views FROM article_view WHERE article_id = 12345;

效能對比(模擬 1000 並發寫入)

方案QPS平均延遲鎖等待
主表單行 UPDATE~20050ms+極高
獨立表單行 UPDATE~50020ms高
100 槽位分桶~80001-2ms極低
1000 槽位分桶~15000<1ms可忽略

進階優化技巧

  1. 熱點預熱:應用啟動時預先插入所有熱門文章的 100 槽位,避免冷啟動首次 INSERT 爭用
  2. 讀取快取:Redis 快取 SUM(view) 結果,定期 (如每分鐘) 同步回 MySQL 或直接以 Redis 為準
  3. 分區表:PARTITION BY HASH(article_id) 進一步分散 I/O
  4. 非同步寫入:應用層本地累加,批次刷新 DB (如每秒 flush),極致吞吐犧牲強一致性
  5. 槽位動態調整:監控單槽位鎖等待,熱門文章自動擴槽 (如 100→500)

適用場景與限制

適用不適用
文章瀏覽/點讚/分享計數金融帳戶餘額、庫存扣減 (需強一致、不可丟失)
API 呼叫統計、錯誤計數需要精確實時值的業務決策
日誌/審計行數統計槽位數極大導致聚合查詢慢 (需快取)

遷移指南:從單行遷移到分桶

  1. 建新分桶表
  2. 雙寫:應用同時寫舊單行表 + 新分桶表
  3. 回填歷史:舊表 view 平均分配到 100 槽位
  4. 驗證聚合結果一致
  5. 切換讀取路徑 → 下線舊表
分桶槽位是「空間換時間、隨機換確定」的經典權衡。寫入無鎖化、讀取聚合化、快取前置化 — 三板斧解決 99% 高並發計數場景。切記:槽位數不是越大越好,聚合成本與寫入分散收益需找平衡點,生產環境以壓測結果為準。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved