* DB架构
master(5.1)
|
-------------------------------------
| |
slave A(5.1) slave B(5.6)
* 表结构
+-------+-----------------------------------------------------------------------------------------------------------------------------+
+-------+-----------------------------------------------------------------------------------------------------------------------------+
| abc | CREATE TABLE `abc` (
`id` int(11) DEFAULT NULL,
`id2` int(11) NOT NULL DEFAULT '6'
+-------+-----------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
* 核心参数: master 和 slave A,B 的sql_mode 都是 '';
* 症状:在master上执行一条SQL语句 insert into abc values(1,0),(1,null);
结果 Slave A 正常, Slave B 报error 1048,Error 'Column 'id2' cannot be null' on query, 这是为什么呢?
Question1:为什么insert into abc values(1,null)失败?insert into abc values(1,0),(1,null);成功?
Question2:为什么5.1 slave可以,5.6slave 不行?
Question3:手动去slave B上执行同样的insert,为什么可以执行成功?
如果你已经知道为什么,可以忽略下面的分析。
* 分析:
细心的读者已经发现,第一个问题的答案已经在sql_mode链接中。接下来,测试过程中发现:insert into abc values(1,0),(1,null); 在sql_mode=''的时候,不管是5.1还是5.6都会成功执行。那么问题只有一个,sql_mode出了问题。查看master binlog后发现:在insert语句之前,多了这个可以执行的注释:SET @@session.sql_mode=2097152。我们来看看:
dbadmin:abc> SET @@session.sql_mode=2097152;
Query OK, 0 rows affected (0.00 sec)
+---------------------+
+---------------------+
+---------------------+
1 row in set (0.00 sec)
这下,似乎发现了蛛丝马迹,那么问题又来了。
SET @@session.sql_mode=2097152; 从何而来?是程序写的?还是mysql自带的?
经过一番折腾,定位到此SQL来自java jdbc 。
以下代码摘自 java ConnectionIMPL.java
private void setupServerForTruncationChecks() throws SQLException {
if (getJdbcCompliantTruncation()) {
if (versionMeetsMinimum(5, 0, 2)) {
String currentSqlMode =
this.serverVariables.get("sql_mode");
boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1;
if (currentSqlMode == null ||
currentSqlMode.length() == 0 || !strictTransTablesIsSet) {
StringBuffer commandBuf = new StringBuffer("SET sql_mode='");
if (currentSqlMode != null && currentSqlMode.length() > 0) {
commandBuf.append(currentSqlMode);
commandBuf.append(",");
}
commandBuf.append("STRICT_TRANS_TABLES'");
execSQL(null, commandBuf.toString(), -1, null,
DEFAULT_RESULT_SET_TYPE,
DEFAULT_RESULT_SET_CONCURRENCY, false,
this.database, null, false);
setJdbcCompliantTruncation(false); // server's handling this for us now
} else if (strictTransTablesIsSet) {
// We didn't set it, but someone did, so we piggy back on it
setJdbcCompliantTruncation(false); // server's handling this for us now
}
}
}
}
大致的意思就是:如果sql_mode = ‘’,那么java会调高sql_mode的级别,commandBuf.append("STRICT_TRANS_TABLES'");
ok,这下我们已经知道此set来自java,那么问题又来了。即便设置STRICT_TRANS_TABLES,要出问题,master就会报错了,为啥master是好的,Slave A是好的,却Slave B 同步出错呢?
结果已经很明显,因为Slave B是5.6。说的明显一点就是:
在严格模式下,5.1中可以执行,但是5.6不行,这应该算是5.6安全方面的新特性么?
有兴趣的同学可以自己测试下。