MySQL 不同版本對整型溢出的處理機制存在關鍵差異:5.1 默默回滾/截斷,5.5+ 拋出錯誤中斷。這不僅導致主從同步中斷,更可能引發積分、金額等核心業務邏輯錯誤。本文通過實測對比,揭示版本差異根因,給出升級與應用層雙重防護方案。
| 類型 | 儲存 | 有符號範圍 | 無符號範圍 |
|---|---|---|---|
| TINYINT | 1 byte | -128 ~ 127 | 0 ~ 255 |
| SMALLINT | 2 bytes | -32,768 ~ 32,767 | 0 ~ 65,535 |
| MEDIUMINT | 3 bytes | -8,388,608 ~ 8,388,607 | 0 ~ 16,777,215 |
| INT | 4 bytes | -2,147,483,648 ~ 2,147,483,647 | 0 ~ 4,294,967,295 |
| BIGINT | 8 bytes | -9.22E18 ~ 9.22E18 | 0 ~ 1.84E19 |
注意:MySQL 內部運算會將整型提升為 BIGINT 計算,故溢出邊界以 BIGINT 為準。
SELECT CAST(0 AS UNSIGNED) - 1;
| 版本 | 結果 | 行為 |
|---|---|---|
| 5.1 | 18446744073709551615 (最大無符號值) | 靜默回滾 (wrap-around) |
| 5.5+ | ERROR 1690 (22003): BIGINT UNSIGNED value is out of range | 顯式報錯中斷 |
SELECT 9223372036854775807 + 1;
| 版本 | 結果 | 行為 |
|---|---|---|
| 5.1 | -9223372036854775808 (最小有符號值) | 靜默回滾 |
| 5.5+ | ERROR 1690 (22003): BIGINT value is out of range | 顯式報錯中斷 |
UPDATE account SET balance = balance - 100 WHERE id=1 若 balance 不足,5.1 變巨大正數,5.5+ 報錯回滾。將所有實例升級至 MySQL 5.5+ (建議 5.7/8.0),獲得標準 SQL 模式下的溢出報錯保護。主從版本必須一致,避免混合部署。
SELECT balance FROM account WHERE id=1 FOR UPDATE,判斷 balance >= amount 再執行扣減。CHECK (balance >= 0) (8.0.16+ 支援) 或觸發器攔截負值。DECIMAL(18,2),避免整型溢出且精度精確。SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION',強化數據校驗。若遇到「Data truncated for column」或數值異常:
SHOW WARNINGS 獲取截斷詳情。DESC tbl)。CAST(val AS UNSIGNED) 或 CAST(val AS DECIMAL(30,0)) 顯式轉型觀察行為。MySQL 5.1 的靜默溢出行為是重大缺陷,5.5+ 修正為標準報錯機制。核心業務務必升級至 5.7/8.0,並建立「應用層校驗 + 資料庫約束 + 嚴格模式」三層防線,從根源杜絕整型溢出引發的數據災難。