第一次線上 OOM 事故,竟和 where 1 = 1 有關

勇哥编程游记發表於2024-06-02

這篇文章,聊聊一個大家經常使用的程式設計模式 :Mybatis +「where 1 = 1 」。

筆者人生第一次重大的線上事故 ,就是和使用了類似的程式設計模式 相關,所以印象極其深刻。

這幾天在除錯一段業務程式碼時,又遇到類似的問題,所以筆者覺得非常要必要和大家絮叨絮叨。

1 OOM 事故

筆者曾服務一家電商公司的使用者中心,使用者中心提供使用者註冊,查詢,修改等基礎功能 。使用者中心有一個介面 getUserByConditions ,該介面支援透過 「使用者名稱」、「暱稱」、「手機號」、「使用者編號」查詢使用者基本資訊。

我們使用的是 ibatis (mybatis 的前身), SQLMap 見上圖 。當構建動態 SQL 查詢時,條件通常會追加到 WHERE 子句後,而以 WHERE 1 = 1 開頭,可以輕鬆地使用 AND 追加其他條件。

但使用者中心在上線後,竟然每隔三四個小時就發生了記憶體溢位問題 ,經過透過和 DBA 溝通,發現高頻次出現全表查詢使用者表,執行 SQL 變成 :

檢視日誌後,發現前端傳遞的引數出現了空字串,筆者在程式碼中並沒有做引數校驗,所以才出現全表查詢 ,當時使用者表的資料是 1000萬 ,呼叫幾次,使用者中心服務就 OOM 了。

筆者在使用者中心服務新增介面引數校驗 ,即:「使用者名稱」、「暱稱」、「手機號」、「使用者編號」,修改之後就再也沒有產生這種問題了。

2 思維進化

1、前後端同時做介面引數校驗

為了提升開發效率,我們人為的將系統分為前端、後端,分別由兩撥不同的人員開發 ,經常出現系統問題時,兩撥人都非常不服氣,相互指責。

有的時候,筆者會覺得很搞笑,因為這個本質是個規約問題。

要想系統健壯,前後端應該同時做介面引數校驗 ,當大家都遵循這個規約時,出現系統問題的風險大大減少。

2、複用和專用要做平衡

筆者寫的這個介面 getUserByConditions ,支援四種不同引數的查詢,但是因為程式碼不夠嚴謹,導致系統出現 OOM 。

其實,在業務非常明確的場景,我們可以將複用介面,拆分成四個更細粒度的介面 :

  • 按照使用者 ID 查詢使用者資訊
  • 按照使用者暱稱查詢使用者資訊
  • 按照手機號查詢使用者資訊
  • 按照使用者名稱查詢使用者資訊

比如按照使用者 ID 查詢使用者資訊 , SQLMAP 就簡化為:

透過這樣的拆分,我們的介面設計更加細粒度,也更容易維護 , 同時也可以規避 where 1 =1 產生的問題。

有的同學會有疑問:假如拆分得太細,會不會增加我編寫 介面和 SQLMap 的工作量 ?

筆者的思路是:透過程式碼生成器動態生成,是絕對可以做到的 ,只不過需要做一丟丟的定製。

3、編寫程式碼時,需要考慮資源佔用量,做好預防性程式設計

筆者剛入行的時候,只是機械性的完成任務,並沒有思考程式碼後面的資源佔用,以及有沒有可能產生惡劣的影響。

隨著見識更多的系統,學習開源專案,筆者慢慢培養了一種習慣:

  • 這段程式碼會佔用多少系統資源
  • 如何規避風險 ,做好預防性程式設計。

其實,這和玩遊戲差不多 ,在玩遊戲的時,我們經常說一個詞,那就是意識。

上圖,後裔跟墨子在壓對面馬可蔡文姬,看到小地圖中路鎧跟小喬的視野,方向是往下路來的,這時候我們就得到了一個資訊。

知道對面的人要來抓,或者是協防,這種情況我們只有兩個人,其他的隊友都不在,只能選擇避戰,強打只會損失兩名“大將”。

透過小地圖的資訊,並且想出應對方法,就是叫做“猜測意識”。

程式設計也是一樣的,我們思考程式碼可能產生的系統資源佔用,以及可能存在的風險,並做好防禦性程式設計,就是程式設計的意識

4 寫到最後

當我們在使用 :Mybatis +「where 1 = 1 」程式設計模式時,需要如下三點:

  1. 前後端同時做好介面引數校驗 ;
  2. 複用和專用要做平衡,條件允許情況下將複用 SQLMap 拆分成更細粒度的 SQLMap ;
  3. 編寫程式碼時,需要考慮資源佔用量,做好預防性程式設計 ;

文章片段推薦:

生命就是這樣一個過程,一個不斷超越自身侷限的過程,這就是命運,任何人都是一樣,在這過程中我們遭遇痛苦、超越侷限、從而感受幸福。

所以一切人都是平等的,我們毫不特殊。

--- 史鐵生


如果我的文章對你有所幫助,還請幫忙點贊、在看、轉發一下,你的支援會激勵我輸出更高質量的文章,非常感謝!

相關文章