首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

為什麼消息隊列不用 MySQL?高併發下「查詢-修改」競態條件導致不可靠

     編輯:程式設計疑難解答

新手常問:消息隊列不就是「存進去、取出來」嗎?為什麼不直接用 MySQL 建張表,state=0 未讀、state=1 已讀,甚至讀完刪除?本文從併發、效能、可靠性三個維度,揭示 MySQL 做隊列的致命缺陷。

MySQL 做隊列的典型邏輯

-- 生產者
INSERT INTO mq_table (payload, state) VALUES ('msg', 0);

-- 消費者(輪詢)
SELECT * FROM mq_table WHERE state = 0 LIMIT 1 FOR UPDATE;
UPDATE mq_table SET state = 1 WHERE id = ?;
-- 或刪除
DELETE FROM mq_table WHERE id = ?;

三大致命缺陷

1. 高併發下的「查詢-修改」競態條件

  • 多消費者並發 SELECT ... FOR UPDATE,資料庫行鎖爭用極其嚴重。
  • 即使加鎖,SELECT 到 UPDATE 間隙,其他事務可能已修改,導致重複消費或消費丟失。
  • MySQL 鎖升級(行鎖→表鎖)頻繁,吞吐量上不去。

2. 輪詢模式極其低效

  • 消費者需定時 SELECT ... WHERE state=0,空輪詢浪費 CPU/IO。
  • 長輪詢需維持長連接,MySQL 連接數限制(默認 151)極易耗盡。
  • 無法實現真正的「推送」語義,延遲不可控。

3. 缺乏隊列核心特性

特性專業 MQ (RabbitMQ/Kafka/RocketMQ)MySQL 模擬
持久化 + 高吞吐順序寫入/頁面緩存/零拷貝,百萬級 TPSB+ 樹隨機寫入,萬級 TPS 即瓶頸
ACK 機制手動/自動 ACK、重試、死信隊列需自寫事務狀態機,極易 Bug
順序消費分區/分片 + 單消費者保序ORDER BY id 但併發下無序
延遲/定時消息原生支援 (時間輪/延遲隊列)需定時掃描表,精度差、擴展難
優先級隊列原生支援需 ORDER BY priority DESC, id,全表掃描
集群高可用Mirror Queue / ISR / 多副本主從復制延遲、故障切換複雜

何時可以用 MySQL 做隊列?

  • 極低併發(< 10 QPS)、無順序要求、無重試需求、團隊無運維 MQ 能力的內部小工具。
  • 作為「最後手段」的備用存儲(如 MQ 持久化備份表)。

輕量級替代方案

  • Redis List/Stream: LPUSH/BRPOP、XADD/XREADGROUP,單機 10 萬+ QPS,支援消費組、ACK、阻塞讀。
  • SQLite + 應用層隊列: 單文件、零配置、適合嵌入式/邊緣設備。
  • Disruptor / RingBuffer: 進程內無鎖隊列,納秒級延遲,適合高頻交易系統。

結論:MySQL 是關係型數據庫,不是消息中間件。用對工具,少寫 Bug,睡好覺。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved