1. 程式人生 > >防不勝防:一個空格在資料庫裡可能引發的N重血案

防不勝防:一個空格在資料庫裡可能引發的N重血案

編輯手記:Oracle DBA的職業生涯中,無數看似簡單的一個疏忽就能夠導致致命的故障和資料損失,一個空格看似很小,可是如果在指令碼執行環境中,就絕對不容輕視。

你可能還記得我們分享過的一個真實案例:一個空格引發的血案。

今天我們來看另外一個和空格有關的案例,基於11.2.0.3版本的測試:

資料庫

這是一個 11.2.0.3 的 sqlplus 客戶端,那麼以下這個簡單的查詢結果是什麼?

資料庫

好,我承認是在故弄玄虛,結果就是簡單到不能再簡單的1,那麼問題來了,上面第二個查詢的結果是什麼?

資料庫

不要扔磚,雖然結果還是意料之內的,但是見證奇蹟的時刻馬上就要到了,這第三個查詢的結果是什麼?

資料庫

這個結果是不是很2 ?

這個語句和上面第二個語句只是相差了一個空格,結果是完全不同的。對於第二個語句而言,註釋並沒有對語句產生任何的影響;而對於第三個語句,實際上 Oracle 並沒有把這個語句作為包含註釋的語句看待,實際上 sqlplus 執行的是/,也就是將快取中的語句再執行一次,而完全忽略了/之後的內容。

可能有些人認為這個 bug 對於系統的影響不大,而如果在資料庫中執行 .sql 檔案,或者通過 shell 呼叫 sql 指令碼,那麼這個問題出現的可能性就大大增加了。
考慮一下極端的情況,這個問題可能帶來哪些危害。最明顯的莫過於使得上一個執行的 SQL 重複執行。

資料庫

如果上一條是 SELECT,則顯然對系統影響最小(事實上這個影響也不小,因為當前需要執行的

SQL 被跳過了,這可能影響這個 SQL 指令碼的邏輯),而如果是 DELETE 語句,如上所示,那麼表中資料就會被多刪除一次。

也許有人會說,刪除也無所謂,可以進行回滾,並沒有資料的損失。事實上,對於 SHELL 指令碼方式或者編寫好的 SQL 指令碼而言,是沒有辦法對其進行控制的。

即使不在指令碼中執行,有些情況下也是沒有機會回滾的,比如:

資料庫

這種想要恢復就只能通過閃回了。而如果重複執行的是 DDL,那麼連閃回的機會都沒有了。

重複 DDL 的一個例子:

資料庫

雖然並不會真正造成什麼資料的損失,但是資料的載入以及分割槽 EXCHANGE 的工作就完全白做了。

上面幾個例子都比較極端,但是這是為了說明對於 SHELL 或 SQL 檔案中這種自動執行的指令碼,要小心這個 bug 帶來的不可預料的錯誤。
好在這個問題只是發生在 sqlplus 中,且 SQL 語句開頭是以/*方式的註釋開頭,註釋與後面的內容之間沒有空格的情況下,因此想要碰到這個錯誤也並不容易。可是不要忘記墨菲定律,可能發生故障的地方,終究會有人掉進坑裡。