【MySQL】sql_mode引起的一個問題和總結
【背景】
之前項目中,項目組計劃將現場的MySQL5.5升級到5.7,以提升主從同步性能、使用半同步復制,以及解決一些現場問題等。安排測試組進行驗證,測試同事反饋實驗室環境中發現有入庫失敗,我查看了error_log日誌,發現有不少如下報錯。
[Err] 1364 - Field `xx_field` doesn‘t have a default value
【排查與分析】
業務版本前後都是一樣的,好端端的mysql怎麽突然就部分表寫入失敗呢?根據上面的日誌很快猜到是 sql_mode 問題: NOT NULL 列沒有默認值但代碼裏也沒給值,在非嚴格模式下,int列默認為0,string列默認為‘‘了,所以不成問題;但在嚴格模式下,是直接返回失敗的。
那麽看看吧,果然如此。
mysql> show variables like "sql_mode"; +---------------+--------------------------------------------+ | Variable_name | Value | +---------------+--------------------------------------------+ | sql_mode | STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |+---------------+--------------------------------------------+
但測試同事反饋並沒有更改過該參數。查閱資料,發現:MySQL5.6.6 以後版本默認就是NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,5.5默認為 ‘‘ 。
【解決】
嚴格模式是合理的,更能保證系統的健壯性,所以解決方案並不是set global variables去除STRICT_TRANS_TABLES,而是業務側修改完善,加上默認值。
【總結】
凡事講究舉一反三,PDCA。
- 本次出現問題的STRICT_TRANS_TABLES
嚴格模式,進行數據的嚴格校驗,錯誤數據不能插入,報error錯誤。
單獨指 INSERT、UPDATE出現少值或無效值該如何處理:
- 把 ‘‘ 傳給int,嚴格模式下非法,若啟用非嚴格模式則變成0,產生一個warning
- Out Of Range,變成插入最大邊界值
- 非null字段沒有默認值,插入記錄時該字段缺失時報錯。A value is missing when a new row to be inserted does not contain a value for a non-NULL column that has no explicit DEFAULT clause in its definition
- sql_mode取值很多,官方有打包組合,ANSI、TRADITIONAL,當然,每項也可以單獨設置。註:大小寫不敏感,都可以。
1. sql_mode=‘ANSI‘
ANSI模式:寬松模式,對插入數據進行校驗,如果不符合定義類型或長度,對數據類型調整或截斷保存,報warning警告。
更改語法和行為,使其更符合標準SQL,相當於REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE。
2.sql_mode=‘TRADITIONAL‘
TRADITIONAL 模式:嚴格模式,當向mysql數據庫插入數據時,進行數據的嚴格校驗,保證錯誤數據不能插入,報error錯誤。用於事物時,會進行事物的回滾。
更像傳統SQL數據庫系統,該模式的簡單描述是當在列中插入不正確的值時“給出錯誤而不是警告”。
相當於 STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION。
- 其他常用
set session sql_mode = ‘no_auto_create_user‘;
MySQL5.7及之前版本可設置,MySQL8之後為默認設置,先create user才能grant。
set session sql_mode = ‘no_zero_in_date‘;
set session sql_mode = ‘no_zero_date‘;
no_zero_date認為日期 ‘0000-00-00‘ 非法,與是否設置後面的嚴格模式有關。
- 如果設置了嚴格模式,則 NO_ZERO_DATE 自然滿足。但如果是 INSERT IGNORE 或 UPDATE IGNORE,‘0000-00-00‘依然允許且只顯示warning
- 如果在非嚴格模式下,設置了NO_ZERO_DATE,效果與上面一樣,‘0000-00-00‘允許但顯示warning;如果沒有設置NO_ZERO_DATE,no warning,當做完全合法的值。
- NO_ZERO_IN_DATE情況與上面類似,不同的是控制日期和天,是否可為 0 ,即 2010-01-00 是否合法。
set session sql_mode = ‘no_engine_substitution‘;
使用 ALTER TABLE或CREATE TABLE 指定 ENGINE 時, 需要的存儲引擎被禁用或未編譯,該如何處理。
啟用NO_ENGINE_SUBSTITUTION時,此時直接拋出錯誤;
不設置此值時,CREATE用默認的存儲引擎替代,ATLER不進行更改,並拋出一個 warning。
【MySQL】sql_mode引起的一個問題和總結