最近在使用MySQL數據庫的過程中,遇到了一個很奇怪的現象:一個函數被執行了兩次。因為我使用的是InnoDB引擎,所以在一個事務中執行的所有查詢都應該是原子的。于是我花了很多時間在調試這個問題上,最終找到了解決方法。
首先我猜想是因為我在函數中使用了SELECT語句,但是沒有加鎖,導致在事務中出現了并發問題。于是我對函數中的SELECT語句加上了FOR UPDATE,但是問題仍然存在。接下來,我開始查閱官方文檔,發現可能是函數的嵌套調用導致了問題。
DELIMITER // CREATE FUNCTION myFunction(p1 INT) RETURNS INT BEGIN DECLARE result INT; SELECT COUNT(*) INTO result FROM myTable WHERE column1 = p1; RETURN result; END // CREATE FUNCTION myNestedFunction(p1 INT) RETURNS INT BEGIN DECLARE result INT; SET result = myFunction(p1); SET result = result * 2; -- 執行兩次 RETURN result; END // SELECT myNestedFunction(1);
上面的代碼實現了兩個函數,分別是myFunction和myNestedFunction。其中myFunction用來統計表myTable中column1等于p1的行數,myNestedFunction中則是調用了myFunction并將返回值乘以2。
我發現在執行myNestedFunction函數時,myFunction函數被執行了兩次。原因是我在調用myFunction時,將返回值賦值給了變量result,之后又將result的值修改了一次。因為函數中的變量是全局變量,所以這個修改對于整個函數體都是生效的。而且這個問題還和默認的隔離級別有關。如果使用的是REPEATABLE READ隔離級別,那么會出現數據不一致的問題。
解決這個問題的方法很簡單,只需要在調用myFunction時,將返回值直接用作myNestedFunction的返回值即可。
CREATE FUNCTION myNestedFunction(p1 INT) RETURNS INT BEGIN RETURN myFunction(p1) * 2; -- 只執行一次 END //
通過這個問題的解決,我再次體會到了在使用數據庫時,遵循規范和注意事項的重要性。
上一篇mysql 函數例子