淺談MySQL如何優(yōu)雅的做大表刪除
隨著時(shí)間的推移或者業(yè)務(wù)量的增長(zhǎng),數(shù)據(jù)庫(kù)空間使用率也不斷的呈穩(wěn)定上升狀態(tài),當(dāng)數(shù)據(jù)庫(kù)空間將要達(dá)到瓶頸的時(shí)候,可能我們才會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)有那么一兩張的超級(jí)大表!他們堆積了從業(yè)務(wù)開始到現(xiàn)在的全部數(shù)據(jù),但是90%的數(shù)據(jù)都是沒有業(yè)務(wù)價(jià)值的,這時(shí)候該如何處理這些大表?
既然是沒有價(jià)值的數(shù)據(jù),我們通常一般會(huì)選擇直接刪除或者歸檔后刪除兩種,對(duì)于數(shù)據(jù)刪除的操作方式來(lái)說(shuō)又可分為兩大類:
- 通過(guò)truncate直接刪除表中全部數(shù)據(jù)
- 通過(guò)delete刪除表中滿足條件記錄
一、Truncate操作
從邏輯意義上來(lái)講,truncate操作就是刪除表中所有記錄行,但是又與delete from table_name wehre 1=1這種操作不一樣。MySQL為了提高刪除整張表數(shù)據(jù)的性能,truncate操作其本質(zhì)上其實(shí)是先drop table然后在re-create table。也真因如此,truncate操作是一個(gè)不可回滾的DDL操作。
1.1 MySQL truncate 都做了哪些操作?
- truncate操作實(shí)際上分為drop、re-create兩步
- drop操作的第一個(gè)階段,是對(duì)Buffer pool頁(yè)面進(jìn)行清除的過(guò)程,將表相關(guān)的數(shù)據(jù)頁(yè)從flush鏈中刪除,而不需要做flush操作。該步驟的瓶頸點(diǎn)主要在于flush隊(duì)列的刪除操作必須持有對(duì)應(yīng)buffer pool instance的鎖并進(jìn)行遍歷搜索,如果buffer pool instance比較大且flush鏈中需要?jiǎng)h除的數(shù)據(jù)頁(yè)很多,該操作會(huì)導(dǎo)致其他事務(wù)在獲取buffer pool instance的鎖時(shí)被阻塞,從而影響數(shù)據(jù)庫(kù)的性能
- drop操作的第二個(gè)階段,是刪除ibd磁盤文件的過(guò)程。刪除數(shù)據(jù)庫(kù)物理文件越大I/O資源消耗越大,刪除操作耗時(shí)越久
- re-create操作階段,只要?jiǎng)h除表的.frm文件完好無(wú)損,在drop table之后就可以按照原表結(jié)構(gòu)信息進(jìn)行重建,重建后表的auto_increment值會(huì)被重置
1.2 如何優(yōu)化truncate操作帶來(lái)的資源消耗?
- 對(duì)于truncate操作中的drop表第一階段,當(dāng)分配給MySQL實(shí)例的innodb_buffer_pool_size超過(guò)1GB時(shí),合理的設(shè)置innodb_buffer_pool_instances參數(shù),提高并發(fā)的同時(shí)也變相的減少掃描buffer pool instance時(shí)鎖資源占用耗時(shí)
- 對(duì)于truncate操作中的drop表第二階段,在刪除對(duì)應(yīng)表之前,先對(duì)改表的.ibd文件創(chuàng)建一個(gè)硬連接,加快MySQL層面的drop操作執(zhí)行效率,減少對(duì)數(shù)據(jù)庫(kù)層面的性能損耗。后續(xù)手動(dòng)對(duì)操作系統(tǒng)層面我們做的硬連接進(jìn)行清理
二、Delete操作
2.1 MySQL delete 都做了哪些操作?
- 根據(jù)where條件對(duì)刪除表進(jìn)行索引/全表掃描,檢查是否符合where條件,該階段會(huì)對(duì)掃描中所有行進(jìn)行加鎖。該階段是最大的資源消耗隱患,若表的數(shù)據(jù)量大且delete操作無(wú)法有效利用索引減少掃描數(shù)據(jù)量,該步驟對(duì)于數(shù)據(jù)庫(kù)帶來(lái)的鎖爭(zhēng)用、cpu/io資源的消耗都是巨大的
- 對(duì)不能夠被where條件匹配的行施加的鎖會(huì)在條件檢查后予以釋放,InnoDB僅鎖定需要?jiǎng)h除的行。這可以有效地降低鎖爭(zhēng)用,但是我們?nèi)孕枰P(guān)注的一點(diǎn)是,一次性刪除大批量的數(shù)據(jù),該操作將會(huì)產(chǎn)生巨大的binlog事務(wù)日志,這對(duì)于MySQL自身以及主從架構(gòu)中的從庫(kù)都是不友好的,可能帶來(lái)叫的復(fù)制延遲。
2.2 如何優(yōu)化delete操作?
- delete全表刪除操作需要謹(jǐn)慎,可考慮使用truncate操作
- delete … where … 中,where過(guò)濾條件盡量保證可有效利用索引減少數(shù)據(jù)掃描量,避免全表掃描
- 對(duì)于大批量數(shù)據(jù)刪除且where條件無(wú)索引的情況,delete操作可額外增加自增長(zhǎng)主鍵或者含索引的時(shí)間字段,進(jìn)行分批刪除操作,每次刪除少量數(shù)據(jù),分多批次執(zhí)行。
- 對(duì)于保留近期數(shù)據(jù)刪除歷史數(shù)據(jù)的經(jīng)典場(chǎng)景,可創(chuàng)建同結(jié)構(gòu)的xxx_tmp表并通過(guò)insert xxx_tmp select …操作將需要的數(shù)據(jù)保留至tmp表中、然后通過(guò)rename操作將當(dāng)前業(yè)務(wù)表xxx替換為xxx_bak表,xxx_tmp表替換為當(dāng)前業(yè)務(wù)表名xxx,后續(xù)手動(dòng)刪除無(wú)用的大表xxx_bak
2.3 delete常見的兩個(gè)場(chǎng)景
2.3.1 delete where條件無(wú)有效索引過(guò)濾
比較常見的一個(gè)場(chǎng)景是,業(yè)務(wù)上需要?jiǎng)h除t1 condition1=xxx的值,condition字段無(wú)法有效利用索引,這種情況下我們通常的做法是:
- 查看當(dāng)前表結(jié)構(gòu)中可有效利用的索引,盡量是表的自增長(zhǎng)主鍵或者時(shí)間索引字段
- 有效利用自增長(zhǎng)主鍵索引或者時(shí)間索引,將delete操作添加索引字段的范圍過(guò)濾,每次刪除少量數(shù)據(jù),分多批次執(zhí)行。具體分批需要根據(jù)業(yè)務(wù)實(shí)際進(jìn)行評(píng)估,避免一次性刪除大批量數(shù)據(jù)。
-- 利用自增長(zhǎng)主鍵索引 delete from t1 where condition1=xxx and id >=1 and id < 50000; delete from t1 where condition1=xxx and id >=50000 and id < 100000; -- 利用時(shí)間索引 delete from t1 where condition1=xxx and create_time >= '2021-01-01 00:00:00' and create_time < '2021-02-01 00:00:00'; delete from t1 where condition1=xxx and create_time >= '2021-02-01 00:00:00' and create_time < '2021-03-01 00:00:00';
2.3.2 保留近期數(shù)據(jù)刪除歷史數(shù)據(jù)
比較常見的一個(gè)場(chǎng)景是,需要僅保留t1表近3個(gè)月數(shù)據(jù),其余歷史數(shù)據(jù)刪除,我們通常的做法是:
創(chuàng)建一張t1_tmp表用來(lái)臨時(shí)存儲(chǔ)需要保留的數(shù)據(jù)
create table t1_tmp like t1;
根據(jù)有索引的時(shí)間字段,分批次的將需要保留的數(shù)據(jù)寫入t1_tmp表中,該步驟需要注意的是,最后一批次時(shí)間的操作可暫時(shí)不處理
-- 根據(jù)實(shí)例業(yè)務(wù)數(shù)量進(jìn)行分批,盡量每批次處理數(shù)據(jù)量不要太大 insert into t1_tmp select * from t1 where create_time >= '2021-01-01 00:00:00' and create_time < '2021-02-01 00:00:00'; insert into t1_tmp select * from t1 where create_time >= '2021-02-01 00:00:00' and create_time < '2021-03-01 00:00:00'; -- 當(dāng)前最后一批次數(shù)據(jù)先不操作 -- insert into t1_tmp select * from t1 where create_time >= '2021-03-01 00:00:00' and create_time < '2021-04-01 00:00:00';
通過(guò)rename操作將當(dāng)前業(yè)務(wù)表t1替換為t1_bak表,t1_tmp表替換為當(dāng)前業(yè)務(wù)表名t1,被刪除表若有頻繁的DML操作,該步驟會(huì)造成短暫的業(yè)務(wù)訪問(wèn)失敗
alter table t1 rename to t1_bak; alter table t1_tmp rename to t1;
將最后一批次數(shù)據(jù)寫入當(dāng)前業(yè)務(wù)表,該步驟的目的是為了減少變更操作流程中的數(shù)據(jù)丟失
insert into t1 select * from t1_bak where create_time >= '2021-03-01 00:00:00' and create_time < '2021-04-01 00:00:00';
在rename操作步驟中,還有一點(diǎn)我們需要關(guān)注的是,變更表主鍵是自增長(zhǎng)還是業(yè)務(wù)唯一的uuid,若為自增長(zhǎng)主鍵,我們還需要注意修改t1_tmp表的自增長(zhǎng)值,保證最終設(shè)置值包含變更期間數(shù)據(jù)寫入
alter table t1_tmp auto_increment={t1表當(dāng)前auto值}+{變更期間預(yù)估增長(zhǎng)值}
三、Truncate/Delete優(yōu)劣勢(shì)對(duì)比
| 操作類型 | 描述 | 優(yōu)勢(shì) | 劣勢(shì) |
|---|---|---|---|
| Truncate | 表的全量刪除操作 | 無(wú)需掃描表數(shù)據(jù),執(zhí)行效率高,直接進(jìn)行物理刪除,快速釋放空間占用 | DDL操作無(wú)法進(jìn)行回滾,無(wú)法按條件進(jìn)行刪除 |
| Delete | 根據(jù)指定條件進(jìn)行過(guò)濾刪除操作 | 可根據(jù)指定條件進(jìn)行過(guò)濾刪除 | 刪除效率依賴where條件的編寫,大表刪除會(huì)產(chǎn)品大量的binlog且刪除效率低,刪除操作可能出現(xiàn)較多的碎片空間而不是直接釋放空間占用 |
到此這篇關(guān)于淺談MySQL如何優(yōu)雅的做大表刪除的文章就介紹到這了,更多相關(guān)MySQL 大表刪除內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mysql事項(xiàng),視圖,函數(shù),觸發(fā)器命令(詳解)
下面小編就為大家?guī)?lái)一篇Mysql事項(xiàng),視圖,函數(shù),觸發(fā)器命令(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
wampserver下mysql導(dǎo)入數(shù)據(jù)庫(kù)的步驟
這篇文章主要介紹了wampserver下mysql導(dǎo)入數(shù)據(jù)庫(kù)的步驟,需要的朋友可以參考下2016-08-08
MYSQL統(tǒng)計(jì)逗號(hào)分隔字段元素的個(gè)數(shù)
本文主要介紹了MYSQL統(tǒng)計(jì)逗號(hào)分隔字段元素的個(gè)數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
MySQL自動(dòng)為查詢數(shù)據(jù)結(jié)果加序號(hào)
這篇文章主要給大家介紹了關(guān)于MYSQL如何自動(dòng)為查詢數(shù)據(jù)的結(jié)果編上序號(hào)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用mysql具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧2022-12-12
MySQL數(shù)據(jù)表分區(qū)策略及優(yōu)缺點(diǎn)分析
項(xiàng)目開發(fā)中,隨著數(shù)據(jù)庫(kù)數(shù)據(jù)量越來(lái)越大,單個(gè)表中數(shù)據(jù)太多,從而導(dǎo)致查詢速度變慢,而且由于表的鎖機(jī)制導(dǎo)致應(yīng)用操作也受到嚴(yán)重影響,出現(xiàn)了數(shù)據(jù)庫(kù)性能瓶頸。因此我們需要考慮分表與分區(qū),MySQL分表分區(qū)就是為了解決大數(shù)據(jù)量導(dǎo)致MySQL性能低下的問(wèn)題。2021-05-05
將數(shù)據(jù)插入到MySQL表中的詳細(xì)教程
這篇文章主要介紹了將數(shù)據(jù)插入到MySQL表中的詳細(xì)教程,文中給出了在PHP腳本中操作的示例,是MySQL入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-05-05
MySQL表轉(zhuǎn)移數(shù)據(jù)的三種方式小結(jié)
本文主要介紹了MySQL表轉(zhuǎn)移數(shù)據(jù)的三種方式小結(jié),包括SQL命令,數(shù)據(jù)量不大的情況,直接使用SQL插入數(shù)據(jù)及使用Canal工具,具有一定的參考價(jià)值,感興趣的可以了解一下2025-02-02
mysql8報(bào)錯(cuò):ERROR?1410?(42000):?You?are?not?allowed?to?
電腦新裝的mysql,版本為8.0以上,分配權(quán)限時(shí)直接帶密碼和賬號(hào)會(huì)報(bào)錯(cuò),這篇文章主要給大家介紹了關(guān)于mysql8報(bào)錯(cuò):ERROR?1410?(42000):?You?are?not?allowed?to?create?a?user?with?GRANT的解決辦法,需要的朋友可以參考下2022-06-06

