萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 高併發計數器設計:從單行熱點到隨機分桶優化

問題背景

文章瀏覽數、商品點擊數等高頻更新場景,若採用單表單行 UPDATE article SET view=view+1 WHERE id=?,InnoDB 會對該行加排他鎖,導致序列化,高併發下嚴重阻塞。

方案演進

階段 1:單表單行(基線)

CREATE TABLE article_view (
  article_id INT PRIMARY KEY,
  view INT NOT NULL DEFAULT 0
) ENGINE=InnoDB;

缺點:熱點行鎖競爭,TPS 受限。

階段 2:拆分計數器表(仍單行)

將計數器移出主表,減少主表鎖爭用,但熱點行問題未解。

階段 3:隨機分桶 / 槽位計數器(推薦)

CREATE TABLE article_view (
  article_id INT NOT NULL,
  pond TINYINT NOT NULL COMMENT '桶編號 0-99',
  view INT NOT NULL DEFAULT 0,
  PRIMARY KEY (article_id, pond)
) ENGINE=InnoDB;
  • 每篇文章預設 100 個桶(低流量可用 3~5 個)。
  • 更新時:pond = FLOOR(RAND() * 100) 隨機選桶。
  • 讀取總數:SELECT SUM(view) FROM article_view WHERE article_id = ?。

單條 SQL 完成 Upsert

INSERT INTO article_view (article_id, pond, view)
VALUES (123, FLOOR(RAND()*100), 1)
ON DUPLICATE KEY UPDATE view = view + 1;

利用唯一鍵衝突自動轉更新,避免應用層 SELECT→判斷→INSERT/UPDATE 的競態條件。

效能對比

方案寫入並發讀取總數複雜度
單行低 (序列化)O(1)低
100 桶高 (鎖分散)O(100) 聚合中

權衡建議

  • 讀多寫少、流量中低:單行足夠,開發維護成本低。
  • 寫入極高 (千 QPS+):分桶必要,可搭配 Redis 預聚合定期落庫。
  • 桶數量 = 核心數 × 2~4 為宜,過多增加聚合成本。
copyright © 萬盛學電腦網 all rights reserved