【踩坑系列】MySql only_full_group_by配置,竟導致所有應用報錯?


1. 踩坑經歷

一個很平常的下午,大家都在埋頭認真寫bug呢,突然企業微信群里炸鍋了,好多應用都出現大量的Error日志,而且都報同一個錯誤,就是下面這個:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #4 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'online_saas.t.receive_amount' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

從異常信息可以看出,報錯的原因是因為Sql語句SELECT后面的列包含了group by后面沒有的列並且沒有使用聚合函數。

因為近幾天一直未發布,所以就問運維是不是改了MySql服務器的配置,打開了sql_mode里的only_full_group_by,導致原本執行正常的Sql通不過檢查而執行失敗。

最后運維說有台MySql服務器之前曾用Sql語句臨時關閉過 only_full_group_by , 而剛剛因為負載過高自動重啟了,導致sql_mode又使用了原有的默認值,而MySql 5.7以后sql_mode默認是開啟only_full_group_by的,導致了該錯誤。

最后運維修改了這台MySql服務器的my.cnf文件,將sql_mode里的only_full_group_by關閉了,重啟了MySql服務器和報錯的應用,事情得以最終解決。

為啥要重啟應用呢?是因為運維修改配置后,各個應用還是報錯,所以重啟了各個報錯的應用。

2. 原因分析

假設你安裝的是MySql 5.7以后的版本,比如5.7.21,默認情況下,sql_mode里的only_full_group_by是被打開的:

這個打開后,對Sql的語法檢查就會很嚴格,就比如上面報錯的Sql語句,就是因為使用GROUP BY不規范造成的。

正常情況下,我們使用GROUP BY語句都是下面這樣的:

SELECT語句后的列,要么是GROUP BY語句后面出現的列,要么是使用了聚合函數。

但如果有些地方寫的不規范,就會報錯,比如下面這樣:

因為age列既沒有出現在GROUP BY語句后,也沒有使用聚合函數。

但如果我們將sql_mode里的only_full_group_by關閉,上面報錯的語句就不報錯了:

SET @@GLOBAL.sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

注意事項:如果仍然報錯,請打開新會話執行查詢語句。

還要值得注意的是,上面關閉only_full_group_by的方式是臨時的,如果重啟了MySql服務器,only_full_group_by又被打開了,如下所示:

這也是為什么MySql服務器自動重啟后,我們的應用開始報錯的原因,因為運維之前確實改過,但是是臨時改的,重啟后又被覆蓋了。

3. 推薦解決方案

如果沒有歷史技術債,這個開關打開也挺好的,可以讓大家按規范寫Sql,如果不規范,就能及時發現。

但如果有歷史技術債,就需要關閉only_full_group_by了,而且要永久性的關閉,避免MySql服務器重啟后配置又被覆蓋的情況。

可以通過在/etc/my.cnf文件添加以下內容,來永久關閉only_full_group_by

sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

修改完畢后,記得重啟MySql。

如果重啟后也不會生效,檢查下sql_mode的位置是不是不對(放在最后是不會生效的):



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM