首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 高並發計數器設計:從單行熱點到分桶隨機更新方案

     編輯:MySQL 資料庫相關知識

文章瀏覽數、商品點讚數等高頻計數場景,若採用單行 UPDATE cnt SET v=v+1 WHERE id=1 會產生嚴重行鎖競爭,TPS 急劇下降。本文演進三種方案,從單行 → 分表 → 分桶隨機更新,並給出基準測試參考。

方案演進

1. 單行計數器 (基準,高並發不可用)

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

並發 1000 QPS 時鎖等待 > 200ms,吞吐崩潰。

2. 分表隔離 (緩解讀寫衝突,寫入仍單點)

CREATE TABLE article_view (
  article_id INT PRIMARY KEY,
  view_cnt BIGINT NOT NULL DEFAULT 0
) ENGINE=InnoDB;
-- 文章詳情表分離,計數器獨立表,減少主表鎖持有時間

讀寫分離但計數器更新仍串行,並發 2000 QPS 時仍達瓶頸。

3. 分桶隨機更新 (推薦,高並發最佳)

CREATE TABLE article_view_bucket (
  article_id INT NOT NULL,
  bucket_id TINYINT NOT NULL COMMENT '0-99 分桶',
  view_cnt BIGINT NOT NULL DEFAULT 0,
  PRIMARY KEY (article_id, bucket_id)
) ENGINE=InnoDB;

更新邏輯:

-- 隨機選擇一個桶
INSERT INTO article_view_bucket (article_id, bucket_id, view_cnt)
VALUES (123, FLOOR(RAND()*100), 1)
ON DUPLICATE KEY UPDATE view_cnt = view_cnt + 1;

讀取總數:

SELECT SUM(view_cnt) FROM article_view_bucket WHERE article_id = 123;

關鍵參數選擇

並發量級桶數建議預期鎖競爭降低
< 500 QPS10-2010-20 倍
500-5000 QPS50-10050-100 倍
> 5000 QPS200-1000200+ 倍

桶數過大會增加 SUM() 掃描成本,可配合 INNODB_BUFFER_POOL 緩存熱點桶。

進階優化

  1. 應用層本地聚合:每 100 次請求批次寫入一次,減少 DB 交互 99%。
  2. Redis 預聚合 + 定時落地:INCRBY article:123:view 1,每分鐘 Lua 腳本將增量寫回 MySQL。
  3. 讀取端緩存:總數緩存 10-60 秒,接受極短暫不一致。

基準測試參考 (sysbench OLTP 修改版)

方案          | 1000 並發 QPS | 平均延遲 | P99 延遲
單行          | 1,200         | 85 ms    | 420 ms
分表隔離      | 2,800         | 35 ms    | 180 ms
分桶 100 桶   | 18,500        | 5 ms     | 22 ms
Redis+定時落地| 50,000+       | 1 ms     | 5 ms

選型建議

  • 中小流量 (日 PV < 100 萬):分桶 50-100 即可,實作簡單、零外部依賴。
  • 大流量/嚴格一致性要求:Redis 預聚合 + MySQL 持久化雙寫。
  • 極高流量/最終一致性可接受:累積在 Kafka/ClickHouse 離線聚合。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved