MySQL 5.1 對整型溢出採用靜默回繞(wrap-around),5.5+ 預設啟用嚴格模式報錯攔截。版本混用(主 5.1、從 5.5)會導致複寫中斷或資料不一致。本文實測對比並給出升級建議。
| 型別 | 有號範圍 | 無號範圍 | 儲存空間 |
|---|---|---|---|
| TINYINT | -128 ~ 127 | 0 ~ 255 | 1 byte |
| SMALLINT | -32768 ~ 32767 | 0 ~ 65535 | 2 bytes |
| MEDIUMINT | -8388608 ~ 8388607 | 0 ~ 16777215 | 3 bytes |
| INT | -2147483648 ~ 2147483647 | 0 ~ 4294967295 | 4 bytes |
| BIGINT | -9223372036854775808 ~ 9223372036854775807 | 0 ~ 18446744073709551615 | 8 bytes |
MySQL 內部運算會轉為 BIGINT 處理,以下以 BIGINT 為例測試。
mysql> SELECT CAST(0 AS UNSIGNED) - 1; +-------------------------+ | CAST(0 AS UNSIGNED) - 1 | +-------------------------+ | 18446744073709551615 | -- 回繞到最大無號值 +-------------------------+ mysql> SELECT 9223372036854775807 + 1; +-------------------------+ | 9223372036854775807 + 1 | +-------------------------+ | -9223372036854775808 | -- 有號溢出回繞到最小值 +-------------------------+
mysql> SELECT CAST(0 AS UNSIGNED) - 1; ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0 as unsigned) - 1)' mysql> SELECT 9223372036854775807 + 1; ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
UPDATE account SET balance = balance - 100 WHERE id=1 若 balance 為 0,5.1 會變成巨大正數,5.5+ 直接報錯保護資料。sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION。SIGNAL 或觸發器攔截溢出。UNSIGNED,並評估上限是否足夠(如積分用 BIGINT UNSIGNED)。核心觀念:資料庫層面的攔截是最後防線,應用層驗證是第一道防線,兩者缺一不可。