套路的人心:MySQL使用delete刪除數據的正確套路!
為啥MySQL不建議用DELETE刪數據?說白了,你可能還真不配用它
阿里面試問這個問題,不是刁難你,是想看看你到底是CRUD民工,還是能把數據庫當朋友用的老炮兒。
一、DELETE聽起來很清爽,用起來往往“祭天”
首先,DELETE不是不能用,而是你得知道它的代價,你得知道它背后到底做了啥。很多人寫個SQL就是:
DELETE FROM user WHERE status = 'inactive';
看著清清爽爽,執行完了控制臺還給你來句“Query OK, 1000000 rows affected”,然后服務嘎的一聲宕機,QPS跌成狗,DBA頭發開始謝落,領導問你是不是手滑了。
這波操作,線上的鍋你得背,簡歷得更新,關鍵是你還不知道錯哪了。
二、MySQL DELETE底層到底干了啥?
MySQL的DELETE,其實背地里非常重,尤其是你刪的是InnoDB表,它做的事情比你以為的多得多:
1. DELETE不是物理刪除,而是行級刪除 + 日志記錄
InnoDB是支持事務的,它不能說刪就刪,得有回滾能力。所以它做了這些事:
- 標記要刪除的行
- 寫UNDO日志(為了事務回滾)
- 寫REDO日志(為了崩了還能恢復)
- 更新索引(主鍵+二級索引全部都得動手)
- 最后才是真正的標記行已刪除
你以為你在“清空”,其實你在走一整套事務機制,跟離婚一樣,得先申請、備案、調解、走流程,最后判離。全表DELETE就相當于辦集體離婚,政務大廳都能爆了。
2. 刪除的行不會馬上釋放空間
InnoDB做的是MVCC(多版本并發控制),DELETE之后的數據并不會立馬消失,舊版本還留著,事務沒提交前你還能查出來。
即便提交了,磁盤空間也不會釋放。你DELETE 100W 行,磁盤占用可能一個字節都不降。你得等它自己觸發PURGE流程,或者你主動做OPTIMIZE TABLE,才能回收。
大哥你刪了還得自己收拾戰場,這叫啥?這叫“你殺人你還得擦血跡”,你說累不累。
三、DELETE的幾個雷,踩了炸得你媽都不認識你
1. DELETE大表,直接鎖死線上系統
來,講個我親歷的事兒:
某年某月某日,一個實習生(不是我)要清除3個月前的訂單,寫了個:
DELETE FROM orders WHERE create_time < '2023-01-01';
然后按下執行,MySQL瞬間CPU飆升,表鎖死,所有查訂單的接口直接502。客服在工位上哭,運維在電話里罵,領導進會議室打我(雖然不是我干的)。
問題在哪?
- 沒分頁
- 沒用LIMIT
- 沒用索引(是的,create_time沒建索引)
- 沒歸檔策略
- 一條SQL,把整個庫懟癱了
DELETE默認是行鎖,但你刪太多行,它可能升級為表鎖,你那點小QPS根本扛不住。
2. DELETE會拖垮redo log
InnoDB寫redo log有緩沖區,有write-ahead機制,但DELETE一多,日志量陡增,緩存直接爆掉,IO跟不上,寫盤阻塞。
你以為是DELETE卡住了,其實是磁盤在瘋狂轉,binlog、undo log、redo log都擠在那排隊,宛如12306春運搶票頁面。
3. DELETE影響主從延遲
大廠主從架構都跑得飛快,但DELETE一跑,binlog刷一堆,從庫延遲直接上天。
主庫寫了一個小時,從庫還在回放那條SQL。你要是還有讀寫分離,那這時候讀到的數據都是舊的,訂單顯示“未支付”,其實人家早付款了,分分鐘投訴你虛假交易。
四、為啥阿里不建議用DELETE?人家是拿真實流量砸過來的
別說阿里,字節、騰訊、京東都一樣,對DELETE有明確要求。
像阿里的數據量,一張表動輒上億數據。你一句DELETE,全刪了,分庫分表的路都白走了,業務線可能一個月都恢復不過來。
所以在大廠,DELETE基本上有以下規定:
- 禁止在主庫DELETE超過N條記錄
- DELETE必須帶LIMIT,必須走索引
- 大表DELETE必須走歸檔邏輯,不允許直接刪
- 定時歸檔走獨立腳本 + 延遲消費
- 回收策略用DROP分表 + 重建表
而不是寫個DELETE FROM xxx WHERE xxx,那是作死速通路線。
五、那不DELETE,我咋清數據?盤他!
行,那我們來說替代方案。這才是重點,實戰部分來了。
? 1. DELETE + LIMIT + SLEEP 慢慢刪
這個是經典套路:
-- 分批刪除,每次只刪1000條
DELETE FROM orders WHERE create_time < '2023-01-01' LIMIT 1000;
-- 循環 + SLEEP 1s 避免IO暴沖
寫成存儲過程、腳本,跑定時任務,慢慢挪,你像倒垃圾一樣,別全往樓下扔,分批分類處理。
? 2. 歸檔轉移 + 表切換
這個大廠玩得飛:
- 新表 orders_archive
- insert into orders_archive select * from orders where create_time < xxx;
- insert成功后,DELETE or TRUNCATE or DROP老數據
這種歸檔方案,可以掛在Kafka消費層、可以異步拉數據,不影響主庫壓力,還能跑到其他磁盤上,便于做冷熱分離。
? 3. TRUNCATE + 表分區 / 分表策略
你想清空數據,直接TRUNCATE,比DELETE干凈利索。但條件是:
- 表不能有外鍵
- 你要敢直接扔整個表
那咋辦?用分表。
訂單表 orders_202406、orders_202405、orders_202404
清理2024年4月的訂單?直接DROP TABLE orders_202404,嘎嘣脆,不走DELETE邏輯,干凈直接。
六、再說一次,DELETE不是錯,錯的是你沒搞清楚你的庫值多少錢
兄弟,MySQL是你用的最久的朋友,卻也最容易被你輕視。你說DELETE就是刪除,那你可真把自己當CRUD Boy了。
你得問問自己:
- 這張表多大?
- 有多少行?
- delete操作是否走索引?
- 有沒有binlog回放壓力?
- 是主庫還是從庫執行?
- 是否有歸檔機制?
你不搞清楚這些,就動手刪?那跟不看紅綠燈上高速一個意思,遲早出事故。
七、收個尾巴:DELETE只是個按鈕,背后是命運的齒輪
阿里面試問這個問題,其實是想看看你對數據庫有沒有“敬畏之心”。不是說你不能刪,而是你得知道,你刪的不是數據,你刪的是穩定,是成本,是流量,是你同事的加班時間。
想清楚這個,再談什么SQL性能優化、什么數據治理,才有意義。
? 最后給個套路總結,面試裝X必背:
“MySQL DELETE并不推薦直接在大表上用,是因為InnoDB的事務機制、日志機制、MVCC機制決定了DELETE開銷大、性能差、延遲釋放空間、容易引發主從延遲、鎖表等問題。通常建議使用分批DELETE + LIMIT,歸檔策略、分表DROP、TRUNCATE替代,避免直接操作線上表,保障系統穩定性。”
拿去背,背完你就是面試場上的“數據庫王者”。

浙公網安備 33010602011771號