當前位置: 妍妍網 > 碼農

同事使用 insert into select 遷移數據,上線後被公司開除!

2024-05-10碼農

來源:https://juejin.cn/post/6931890118538199048

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了255小節,累計40w+字,講解圖:1716張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,

血一般的教訓,請慎用 insert into select。同事套用之後,導致公司損失了近10w元,最終被公司開除。

事情的起因

公司的交易量比較大,使用的資料庫是mysql,每天的增量差不多在百萬左右,公司並沒有分庫分表,所以想維持這個表的效能只能考慮做數據遷移。

同事李某接到了這個任務,於是他想出了這兩個方案

  • 先透過程式查詢出來,然後插入歷史表,再刪除原表

  • 使用insert into select讓資料庫IO來完成所有操作

  • 第一個方案使用的時候發現一次性全部載入,系統直接就OOM了,但是分批次做就過多io和時間長,於是選用了第二種方案,測試的時候沒有任何問題,開開心心上線,然後被開除。

    到底發生了啥,我們復盤一下

    先來看第一個方案,先看虛擬碼

    // 1、查詢對應需要遷移的數據
    List<Object> list = selectData();
    // 2、將數據插入歷史表
    insertData(list);
    // 3、刪除原表數據
    deleteByIds(ids);

    我們可以從這段程式碼中看到,OOM的原因很簡單,我們直接將數據全部載入記憶體,記憶體不爆才怪。

    再來看看第二個方案,到底發生了啥。

    為了維持表的效能,同時保留有效數據,經過商量定了一個量,保留10天的數據,差不多要在表裏面保留1kw的數據。所以同事就做了一個時間篩選的操作,直接insert into select ... dateTime < (Ten days ago),爽極了,直接就避免了要去分頁查詢數據,這樣就不存在OOM啦。還簡化了很多的程式碼操作,減少了網路問題。

    為了測試,還特意建了1kw的數據來模擬,測試環境當然是沒有問題啦,順利透過。考慮到這個表是一個支付流水表,於是將這個任務做成定時任務,並且定在晚上8點執行。

    晚上量也不是很大,自然是沒有什麽問題,但是第二天公司財務上班,開始對賬,發現資金對不上,很多流水都沒有入庫。最終排查發現晚上8點之後,陸陸續續開始出現支付流水插入失敗的問題,很多數據因此遺失。

    最終定位到了是遷移任務引起的問題,剛開始還不明所以,白天沒有問題,然後想到晚上出現這樣的情況可能是晚上的任務出現了影響,最後停掉該任務的第二次上線,發現沒有了這樣的情況。

    復盤

    問題在哪裏?

    為什麽停掉遷移的任務之後就好了呢?這個insert into select操作到底做了什麽?我們來看看這個語句的explain。

    圖片

    我們不難從圖中看出,這個查詢語句直接走了全表掃描。這個時候,我們不難猜想到一點點問題。如果全表掃描,我們這個表這麽大,是不是意味著遷移的時間會很長?假若我們這個遷移時間為一個小時,那是不是意味著就解釋了我們白天沒有出現這樣問題的原因了。

    但是全表掃描是最根本的原因嗎?

    我們不妨試試,一邊遷移,一邊做些的操作,還原現場。最終還是會出現這樣的問題。這個時候,我們可以調整一下,大膽假設,如果不全表掃描,是不是就不會出現這樣的問題。當我們將條件修改之後,果然發現沒有走了全表掃描了。

    最終再次還原現場,問題解決了

    圖片

    得出結論:全表掃描導致了這次事故的發生。

    這樣做就解決了發生的問題,但是做為陸陸續續開始失敗這個就不好解釋了。

    原因

    在預設的事務隔離級別下:insert into a select b的操作a表示直接鎖表,b表是逐條加鎖。這也就解釋了為什麽出現陸續的失敗的原因。在逐條加鎖的時候,流水表由於多數是復合記錄,所以最終部份在掃描的時候被釘選,部份拿不到鎖,最終導致超時或者直接失敗,還有一些在這加鎖的過成功成功了。

    為什麽測試沒有問題?

    在測試的時候充分的使用了正式環境的數據來測試,但是別忽視一個問題,那就是測試環境畢竟是測試環境,在測試的時候,數據量真實並不代表就是真實的業務場景。比方說,這個情況裏面就少了一個遷移的時候,大量數據的插入這樣的情況。最終導致線上bug

    解決辦法

    既然我們避免全表掃描就可以解決,我們避免它就行了。想要避免全表掃描,對where後面的條件做索引,讓我們的select查詢都走索引即可。

    insert into還能用嗎?

    可以

    總結

    使用insert into select的時候請慎重,一定要做好索引。

    👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

    新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了255小節,累計40w+字,講解圖:1716張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,


    1. 

    2. 

    3. 

    4. 

    最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。

    獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。

    PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下在看,加個星標,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。

    「在看」支持小哈呀,謝謝啦