文章評論數、商品庫存等計數器字段常需原子自增/自減。無符號整數(UNSIGNED)自減至 0 再減會發生**下溢回繞**(變為最大值 65535/4294967295),導致嚴重數據錯誤。本文提供單條 SQL 安全寫法。
-- comments SMALLINT UNSIGNED (0~65535)
UPDATE info SET comments = comments - 1 WHERE id = 32;
-- 當 comments=0 時,執行後變為 65535(錯誤)
-- 自減 1,最小值鎖定 0
UPDATE info SET comments = IF(comments < 1, 0, comments - 1) WHERE id = 32;
-- 通用:自減 x,最小值 0
UPDATE info SET comments = IF(comments < x, 0, comments - x) WHERE id = 32;
-- 自增無上溢風險(除非達到類型上限),直接加
UPDATE info SET comments = comments + 1 WHERE id = 32;
IF(condition, true_val, false_val):MySQL 流程控制函數,條件為真返回 true_val,否則 false_val。comments < 1 覆蓋 comments = 0 情況,直接返回 0,避開減法運算。SELECT → 判斷 → UPDATE 兩次往返。-- 庫存扣減,不超過總庫存 total_stock
UPDATE products SET stock = IF(stock >= buy_num, stock - buy_num, 0) WHERE id = 100;
-- 計數器自增,不超過上限 max_val
UPDATE counters SET cnt = IF(cnt < max_val, cnt + 1, max_val) WHERE id = 1;
| 方案 | SQL 次數 | 鎖持有時間 | 併發安全 | 適用場景 |
|---|---|---|---|---|
| 應用層 SELECT+UPDATE | 2 | 長(含網路 RTT) | 需額外鎖/樂觀鎖 | 低併發、邏輯複雜 |
| 單條 IF SQL | 1 | 極短(行鎖) | 原子安全 | 高併發計數器、庫存扣減 |
| 存儲過程 | 1 | 短 | 原子安全 | 極複雜邏輯 |
| 類型 | 無符號上限 |
|---|---|
| TINYINT UNSIGNED | 255 |
| SMALLINT UNSIGNED | 65,535 |
| MEDIUMINT UNSIGNED | 16,777,215 |
| INT UNSIGNED | 4,294,967,295 |
| BIGINT UNSIGNED | 18,446,744,073,709,551,615 |
單條 IF 語句,徹底解決無符號整數自減下溢問題,高併發首選。