作者:Agate Li
愛可生研發團隊成員,負責資料庫管理平臺相關項目,.Net 技術愛好者,長期潛水於技術圈。
本文來源:原創投稿
*愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。
背景
1. 創建測試表
CREATE TABLE test (`id` INT(3) NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=`InnoDB`;
INSERT INTO test VALUES(),(),(),(),(),(),(),(),(),();
3. 關鍵來了,執行幾次下面這條 SQL
SELECT sub.rnd FROM (SELECT FLOOR(RAND()*10) rnd FROM test) sub WHERE sub.rnd<3;
明明指定了篩選內層 sub.rnd 小於 3 的條件,輸出出來的結果卻完全不對。4. 接下來排查問題的觸發條件
由於直接使用 RAND() 函數輸出出來的結果是隨機的,首先要做的就是指定一枚固定的種子,一是以免幹擾後續排查,二是可以讓大家自行精確復現。5. 為第三步中的 SQL 指定種子:
SELECT sub.rnd FROM (SELECT FLOOR(RAND(100)*10) rnd FROM test) sub WHERE sub.rnd<3;
熟悉的味道出現了,刺激的感覺回來了…… EXPLAIN 一波6. 去掉第三步中的 test 表再試
SELECT sub.rnd FROM (SELECT FLOOR(RAND(100)*10) rnd) sub WHERE sub.rnd<3;
7. 再改改第三步中的 SQL
SELECT sub.rnd FROM (SELECT FLOOR(RAND(100)*10) rnd FROM test LIMIT 10000) sub WHERE sub.rnd<3;
8. 再驗證一次,把第三步中的 SQL 拉平
SELECT FLOOR(RAND(100)*10) rnd FROM test HAVING rnd<3;
9. 這時候可以推測,大概率是在派生表未物化的情況下 RAND() 在外層重算了……
拿著推測,去 google 一波,立刻找到了一個相關 Bug:https://bugs.mysql.com/bug.php?id=86624
嗯,2017 年年中就有人報過的 Bug,再看看 Bug 狀態,噢,「嘻嘻,我們驗證了但不打算修」……好在官方還是給出了解決方法:
對於5.7,跟我們的做法一樣,加上 LIMIT <一個很大的數>;點一下「閱讀原文」了解更多資訊