MySQL Drop 大表的解決方案

發表於2015-11-10

一 引子

在生產環境中,刪除一個大檔案,比如一個數十 G 或者上百 G 的檔案是很耗時的。

本文介紹一個快速 DROP TABLE 的方法。使用本文提供的方法,不管該表資料量、佔用空間有多大,都可以快速的刪除。

二 演示

下面做一個演示。

2.1 環境

首先說明環境:

環境

2.2 新增 innodb_file_per_table 引數

由於我使用 mysql_multi 的形式啟動 MySQL。所以我們需要在 MySQL 的配置檔案 my.cnf 中加入 innodb_file_per_table 引數。

我的 my.cnf 配置如下:

[mysqld_multi]
mysqld = /usr/local/mysql/mysql-5.1.73-osx10.6-x86_64/bin/mysqld_safe
mysqladmin = /usr/local/mysql/mysql-5.1.73-osx10.6-x86_64/bin/mysqladmin
log = /var/log/mysqld_mutil.err
user = root

[mysqld5173]
port=5173
socket=/tmp/mysql_5173.sock
basedir=/usr/local/mysql/mysql-5.1.73-osx10.6-x86_64
datadir=/usr/local/mysql/data/5.1
user=_mysql
log-error=/var/log/mysqld_5173.log
pid-file=/tmp/mysqld_5173.pid
innodb_file_per_table

[mysqld5540]
port=5540
socket=/tmp/mysql_5540.sock
basedir=/usr/local/mysql/mysql-5.5.40-osx10.6-x86_64
datadir=/usr/local/mysql/data/5.5
user=_mysql
log-error=/var/log/mysqld_5540.log
pid-file=/tmp/mysqld_5540.pid
innodb_file_per_table

[mysqld5612]
port=5612
socket=/tmp/mysql_5612.sock
basedir=/usr/local/mysql/mysql-5.6.21-osx10.8-x86_64
datadir=/usr/local/mysql/data/5.6
user=_mysql
log-error=/var/log/mysqld_5612.log
pid-file=/tmp/mysqld_5612.pid
innodb_file_per_table

2.3 匯入資料

接著登入到 MySQL。

建立測試表。

說明:實驗主要使用 city 表。user 表只是用於測試 LOAD DATA INFILE 的速度。

建立資料文字。

該檔案包括 100W 行資料。內容如下:

1 “robin”,19,”M”,”GuangZhou”,”DBA”
……
1000000 “robin”,19,”M”,”GuangZhou”,”DBA”

該檔案包括 1000W 行資料。內容如下:

1 “GuangZhou”,”GuangDong”,”GZ”,”Wechat”,”Netease”
……
10000000 “GuangZhou”,”GuangDong”,”GZ”,”Wechat”,”Netease”

編輯匯入資料指令碼。

該檔案包括 10 行相同的匯入資料命令。成功匯入到 user 表後,會有 1000W 的資料。內容如下:

1 LOAD DATA INFILE ‘/tmp/user.txt’ \
INTO TABLE user \
FIELDS TERMINATED BY ‘,’ \
LINES TERMINATED BY ‘\n’;
……
10 LOAD DATA INFILE ‘/tmp/user.txt’ \
INTO TABLE user \
FIELDS TERMINATED BY ‘,’ \
LINES TERMINATED BY ‘\n’;

匯入到 city 表的操作類似。

該檔案包括 20 行相同的匯入資料命令。成功匯入到 city 表後,會有兩億條資料。內容如下:

1 LOAD DATA INFILE ‘/tmp/city.txt’ \
INTO TABLE city FIELDS \
TERMINATED BY ‘,’ \
LINES TERMINATED BY ‘\n’;
……
20 LOAD DATA INFILE ‘/tmp/city.txt’ \
INTO TABLE city FIELDS \
TERMINATED BY ‘,’ \
LINES TERMINATED BY ‘\n’;

匯入資料到 MySQL。

其中匯入到 user 表共耗時 84.63 秒。

接著匯入資料到 city 表。

總共耗時:

共計 2394.30 秒,亦即 39.905 分鐘。

2.4 第一次刪除表

我們檢視資料目錄,可以看到該表佔用空間為 15G。

total 15699980
-rw-rw—- 1 _mysql _mysql 8.5K Jan 15 16:46 city.frm
-rw-rw—- 1 _mysql _mysql 15G Jan 15 17:33 city.ibd

刪除表,耗時 1.08 秒。當然,這裡資料量還不夠大,所以速度還是挺快。

接下來,我們重新建立表,匯入資料。

匯入資料耗時跟之前相差不多,不做計算。

2.5 第二次刪除表,使用硬連結

建立硬連結。

total 15699980
-rw-rw—- 1 _mysql _mysql 8.5K Jan 15 17:35 city.frm
-rw-rw—- 1 _mysql _mysql 15G Jan 15 18:13 city.ibd

total 31399948
-rw-rw—- 1 _mysql _mysql 8.5K Jan 15 17:35 city.frm
-rw-rw—- 2 _mysql _mysql 15G Jan 15 18:13 city.ibd
-rw-rw—- 2 _mysql _mysql 15G Jan 15 18:13 city.ibd.hl

可以看到,iNode 由 1 變為 2。

再次刪除。

最後,把硬連結檔案刪除。

total 15699968
-rw-rw—- 1 _mysql _mysql 15G Jan 15 18:13 city.ibd.hl

三 實驗結果

第一次刪除,耗時 1.08 秒。第二次,建立硬連結後,刪除表耗時 0.90 秒。兩次刪除表耗時差異不是太明顯,那是因為我的資料只有 15 G。如果在生產環境中,資料量達到數十 G、上百 G、甚至 T 級,就會顯示這種方法的威力了。本來打算模擬出 100 G 的資料,但由於機器配置和時間關係,就沒有做了。

PS:兩次插入資料,每次 兩億,已經耗去我 1 個多小時的時間。時間寶貴啊,不在這裡浪費了。

四 原理分析

本文中快速 DROP TABLE 利用了作業系統的 Hard Link(硬連結) 的原理。當多個檔名同時指向同一個 iNode 時,這個 iNode 的引用數 N > 1,刪除其中任何一個檔名都會很快。因為其直接的物理檔案塊沒有被刪除,只是刪除了一個指標而已;當 iNode 的引用數 N = 1 時,刪除檔案需要去把這個檔案相關的所有資料塊清除,所以會比較耗時。

最後,吐槽下 Windows。這次測試環境為 Mac OS X 10.9.5,i5,8G 記憶體。vim 開啟一個 458 M 的文字,只需要數秒(N

截個圖給讀者欣賞欣賞。

2015-01-15-solution-of-droping-large-table-under-mysql

Enjoy!

五 參考

–EOF–

相關文章