1. 程式人生 > >【20181101】MySQL text型別的column設定為NOT NULL 導致主從1364

【20181101】MySQL text型別的column設定為NOT NULL 導致主從1364

環境

系統版本 : CentOS release 6.8 (Final)
MySQL版本:5.6.29-log MySQL Community Server (GPL)

MySQL主從配置資訊

  1. binlog format :MIXED
  2. sql_mode: NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

問題描述

從庫show slave status監控的時候發現sql_thread程序已經變成NO,並且爆出了1362錯誤,仔細檢視報錯的是一條insert into語句,並且丟擲了一個詳細的錯誤,大致的意思就是欄位column_1設定了NOT NULL但是沒有插入值並且沒有預設值。然後仔細檢查了一下表結構,具體資訊如下

  • 欄位column_1的確設定的是NOT NULL並且沒有設定default 預設值。
  • column_1欄位是是text型別,所以是無法設定defaut預設值。

本地環境手動執行驗證

在看到這個報錯的資訊的時候,直接google了一下,大部分答案顯示都是由於sql_mode的原因所導致的,要想解決這個問題,必須將執行:

mysql> set global sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";

但是我在slave獲取得到的sql_mode就是 NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

,這個時候我在自己本地執行這個insert 插入語句驗證是否可以插入成功,發現是可以插入的。

猜想

這個時候我就想這個是否在主從複製的過程中,slave在執行這個insert插入的時候sql_mode發生了更改了,這個時候我解析slave的relay log資訊獲取得到 :

# at 271
#181019 12:13:00 server id 5403005  end_log_pos 319 CRC32 0x755a7204    GTID [commit=yes]
SET @@SESSION.GTID_NEXT= '8e91de47-8f0b-11e8-824c-246e9699bb48:1408667354'/*!*/;
# at 319
#181019 12:13:00 server id 5403005  end_log_pos 440 CRC32 0x1add7880    Query   thread_id=47560669  exec_time=0 error_code=0
SET TIMESTAMP=1539922380/*!*/;
SET @@session.pseudo_thread_id=47560669/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1344274432/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8mb4 *//*!*/;
SET @@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45/*!*/;
SET @@session.time_zone='SYSTEM'/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 440
#181019 12:13:00 server id 5403005  end_log_pos 689 CRC32 0x1537384f    Query   thread_id=47560669  exec_time=0 error_code=0
use `finance_fixin_product`/*!*/;
SET TIMESTAMP=1539922380/*!*/;
.....
.....
.....

/*!*/;

可以很明顯的得到在上面binlog中有一個 SET @@session.sql_mode=1344274432/!/; 操作,那麼這操作將賦予sql_mode什麼值呢?

mysql>  SET @@session.sql_mode=1344274432;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@global.sql_mode;
+--------------------------------------------+
| @@global.sql_mode                          |
+--------------------------------------------+
| NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+--------------------------------------------+
1 row in set (0.00 sec)

mysql> select @@sql_mode;
+----------------------------------------------------------------+
| @@sql_mode                                                     |
+----------------------------------------------------------------+
| STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+----------------------------------------------------------------+
1 row in set (0.00 sec)

執行操作之後,突然發現sql_mode多出來一個STRICT_TRANS_TABLES,這個時候大致可以獲取得到端倪了,因為在binlog在執行這條SQL的設定了session的sql_mode為嚴格模式,所以導致在sql_thread重放的時候出現了這個問題。

但是這個時候又冒出了一個新的問題,因為線上的表結構和架構和測試環境一模一樣,並且線上運行了這麼久的時間也沒有出現這個問題,帶著這個疑問我再次解析了線上binlog的日誌資訊:

#181101 10:12:26 server id 5403005  end_log_pos 420 CRC32 0x2fe38b78    Query   thread_id=51519927  exec_time=0 error_code=0
SET TIMESTAMP=1541038346/*!*/;
SET @@session.pseudo_thread_id=51519927/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1344274432/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8mb4 *//*!*/;
SET @@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45/*!*/;
SET @@session.time_zone='SYSTEM'/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 420
#181101 10:12:26 server id 5403005  end_log_pos 538 CRC32 0xb848f658    Table_map: `finance_fixin_cgb_gateway`.`t_cgb_receive_message` mapped to number 2056756
# at 538
#181101 10:12:26 server id 5403005  end_log_pos 740 CRC32 0x42756468    Write_rows: table id 2056756 flags: STMT_END_F
### INSERT INTO 
### SET
###   @1=5300431
###   @2='PA0052'
###   @3='b0009201811010033433728'
###   @4='10.201.5.129'
###   @5=2433
###   @6='128'
###   @7='0009a201811010143659612590571520'
###   @8='000920180907OR23805082566791168'
###   @9='0'
###   @10='0000'
###   @11='0000-操作成功。'
###   @12='2018-11-01 10:12:26'
###   @13='2018-11-01 10:12:26'
# at 740
#181101 10:12:26 server id 5403005  end_log_pos 771 CRC32 0xf5516de4    Xid = 12837058229
COMMIT/*!*/;

對比之後發現,線上的binlog格式是ROW格式,並且通過檢視insert語句發現在insert插入的時候沒有預設值會預設設定為' ',所以slave是不會報錯的。

解決問題

這個時候提出了倆個方案:

  • 更改表結構,允許為NULL
  • 設定master的binlog為ROW格式

最後和開發溝通之後還是選擇了第一個方案,修改表結構,主要是binlog格式的修改,必須先要斷開所有的連結,所以綜合考慮直接修改了表結構。

補充:

在binlog為statement的時候解析了發現了一條SET TIMESTAMP=1539922380語句,之前一直認為在statement格式下面使用時間函式會導致主從資料不一致,現在看到這個SQL,發現並不出現這種情況。