新手常問:消息隊列不就是「存進去、取出來」嗎?為什麼不直接用 MySQL 建張表,state=0 未讀、state=1 已讀,甚至讀完刪除?本文從併發、效能、可靠性三個維度,揭示 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 = ?;
SELECT ... FOR UPDATE,資料庫行鎖爭用極其嚴重。SELECT 到 UPDATE 間隙,其他事務可能已修改,導致重複消費或消費丟失。SELECT ... WHERE state=0,空輪詢浪費 CPU/IO。| 特性 | 專業 MQ (RabbitMQ/Kafka/RocketMQ) | MySQL 模擬 |
|---|---|---|
| 持久化 + 高吞吐 | 順序寫入/頁面緩存/零拷貝,百萬級 TPS | B+ 樹隨機寫入,萬級 TPS 即瓶頸 |
| ACK 機制 | 手動/自動 ACK、重試、死信隊列 | 需自寫事務狀態機,極易 Bug |
| 順序消費 | 分區/分片 + 單消費者保序 | ORDER BY id 但併發下無序 |
| 延遲/定時消息 | 原生支援 (時間輪/延遲隊列) | 需定時掃描表,精度差、擴展難 |
| 優先級隊列 | 原生支援 | 需 ORDER BY priority DESC, id,全表掃描 |
| 集群高可用 | Mirror Queue / ISR / 多副本 | 主從復制延遲、故障切換複雜 |
LPUSH/BRPOP、XADD/XREADGROUP,單機 10 萬+ QPS,支援消費組、ACK、阻塞讀。結論:MySQL 是關係型數據庫,不是消息中間件。用對工具,少寫 Bug,睡好覺。