1. 程式人生 > >mysql刪除大表更快的drop table

mysql刪除大表更快的drop table

利用硬連結和truncate降低drop table對線上環境的影響

在DROP TABLE的時候,所有程序不管是DDL還是DML都被HANG起;直到DROP結束才繼續執行;
這是因為INNODB會維護一個全域性獨佔鎖(在table cache上面),直到DROP TABLE完成才釋放。
在我們常用的ext3,ext4,ntfs檔案系統,要刪除一個大檔案(幾十G,甚至幾百G)還是需要點時間的。
下面我們介紹一個快速DROP table 的方法; 不管多大的表,INNODB 都可以很快返回,表刪除完成;

嘗試5:又找了另一位大牛,這下得解救了,翻身農奴了,拜啊,三柱香,牛!想知道是怎麼做的嗎?我知道你想,嘻嘻,彆著急,是這樣的,建硬連結           在DB server上,找到mytable表對應的檔案,我的是/data/mysql3306/data/mydatabase/。           在這個目錄下,我們可以看到以下記錄,真的是390G!嚇死銀呀!            -rw------- 1
 oracle oinstall  46672 Aug 30 15:42 mytable.frm 
          -rw------- 1 oracle oinstall 391466975232 Aug 30 15:42 mytable.ibd            以上記錄中,1表示該檔案只有一個連結(沒有另外的人鏈接到它,要刪就是真的刪檔案本身了哦),怪不得我執行truncate/drop這麼慢,原來背後就是在刪這個東東呀!
          那怎麼辦呢?能不能繞過,不刪檔案本身,先快速把表drop掉該多好呀。那麼建硬連結ln可以完成。          ln mytable.ibd  mytable.ibd.h          ln mytable.frm  mytable.frm.h          相當於一個檔案被兩個索引連結著,要刪就是隻刪連結,而不是刪檔案本身了,直到只有一個人連結它,才會是真的刪呢。           這時再執行drop表的動作,別提多快了,oh my god,快到驚人,mytable.ibd瞬間它不見了,不見了!           最後別忘了把那個大大的“真檔案”手工刪刪掉           rm -f mytable.ibd.h          rm -f mytable.frm.h             所以,同學們,鄉親們,最最親愛的屌絲們,我痛完了,也絮叨完了,希望你疼的時候能看到這篇部落格,幫你節約哪怕一杯茶的時間,也值了。共勉!




眾所周知drop table會嚴重的消耗伺服器IO效能,如果被drop的表容量較大,甚至會影響到線上的正常。

首先,我們看一下為什麼降容量大的表會影響線上服務


  直接執行drop table,MySQL會將表定義和表資料全都刪除,包括磁碟上的物理檔案,也包括緩衝池中的記憶體資料。

  這麼分兩步,第一步從緩衝池中刪除,這會涉及到table_cache的鎖,如果持有table_cache的鎖,這將導致其他查詢都無法執行。這種情況在沒有innodb_per_table之前尤為嚴重。另外,mysql5.5.23之後新增lazy drop table功能,這個功能用來解決互斥的LRU列表。其中心思想就是加鎖,找到需要被刪除的頁面,刪除1024個頁之後釋放鎖讓其他執行緒工作,之後迴圈而percona的lazy drop處理起來更優雅一些,其會先加鎖,然後找到需要被刪除的頁面,標記,釋放鎖,後臺慢慢刪除。

  之後就是第二步,這步在大容量表的時候更為消耗時間,那就是在OS上刪除物理檔案。大家都知道在EXT3上RM一個200G的檔案會非常耗時,這是由於EXT3儲存資料的結構導致,如果一個很大的檔案,EXT3的i_block無法直接存放,需要多層巢狀才能完全儲存下,在這種情況下由於對映的層次多,並且由於多層對映也不會是順序儲存的,就導致了很大的隨機IO,這就導致了刪除物理檔案非常慢的現象。在這種情況下,建議升級到EXT4,這是由於EXT4比ext3的使用程度分配儲存空間,其最大的優勢就是順序儲存。

EXT3:

EXT4:

 

知道了原因,我們來說說如何解決具體步驟如下:


 1,建立硬連結。

ln table.ibd table.idb.hdlk

  2,mysql執行drop table操作。

drop  table  if  exists tablename;

  3,使用截斷刪除物理檔案。

truncate -s 1024 * 1024 * 4 filename

 

  其實硬連結和drop table就不用多說了,在建立硬連結之後,mysql會認為rm了硬連結檔案之後就算操作完畢,不會真正去刪除物理檔案從而提高了速度。但是對於伺服器來說,實際的物理檔案還在,如果手動RM,還是會產生很多的IO影響,這時候就用到了截斷這個工具。這個工具會根據指定的尺寸大小進行逐步刪除,會將對IO造成的影響降到最低。

複製程式碼
用法:truncate OPTION ... FILE ...
縮小或擴充套件每個FILE的大小到指定的大小

建立不存在的FILE引數。

如果檔案大於指定的大小,則額外的資料將丟失。
如果檔案較短,則延長部分(孔)
讀為零位元組。
長期期權的
強制性論據
適用於 短期期權。
-c,–no建立 不建立任何檔案
-o,–io- 塊對待SIZE作為IO塊而不是位元組數
-r,–reference = 上的RFile的RFile基底大小
-s,–size = SIZE通過SIZE設定或調整檔案大小
- 幫助顯示此幫助和退出
- 版本輸出版本資訊並退出

SIZE可以是(或可以是可選的後面的整數)以下之一:對於G,T,P,E,Z,Y,
KB
1000,K 1024,MB 1000 * 1000,M 1024 * 1024

SIZE也可以字首為以下修改字元之一:
</span> + <span style="color:rgb(128,0,0);line-height:1.5 !important;">' </span><span style="color:rgb(128,0,0);line-height:1.5 !important;">延伸, - 減少,&lt; <span style="color:rgb(128,0,0);line-height:1.5 !important;">' </span><span style="color:rgb(128,0,0);line-height:1.5 !important;">至多,> 至少,
`
/ 迴圈到多個,’%輪到多個。

報告截斷bug - [email protected]
GNU coreutils主頁:
http:// www.gnu.org/software/coreutils/>
一般幫助使用GNU軟體:<http:// www.gnu.org/gethelp/>
有關完整的文件,請執行:info coreutils 截斷呼叫






[email protected] : test 21:39:34> drop table tt ;
Query OK, 0 rows affected (25.01 sec)

刪除一個11G的表用時25秒左右(硬體不同,時間不同);

下面我們來對另一個更大的表進行刪除;
但之前,我們需要對這個表的資料檔案做一個硬連線:

[email protected] # ln stock.ibd stock.id.hdlk
[email protected] # ls stock.* -l
-rw-rw—- 1 MySQL mysql        9196 Apr 14 23:03 stock.frm
-rw-r–r– 2 mysql mysql 19096666112 Apr 15 09:55 stock.ibd
-rw-r–r– 2 mysql mysql 19096666112 Apr 15 09:55 stock.id.hdlk

你會發現stock.ibd的INODES屬性變成了2;

[email protected] : test 21:39:34> drop table stock ;
Query OK, 0 rows affected (0.99 sec)

1秒不到就刪除完成; 也就是DROP TABLE不用再HANG這麼久了。
但table是刪除了,資料檔案還在,所以你還需要最後資料檔案給刪除。

root # ll
total 19096666112
-rw-r–r– 2 mysql mysql 19096666112 Apr 15 09:55 stock.id.hdlk
root # rm stock.id.hdlk
雖然DROP TABLE 多繞了幾步。(如果你有一個比較可靠的自執行程式(自動為大表建立硬連結,並會自動刪除過期的硬連結檔案),就會顯得不那麼繁瑣。)
這樣做能大大減少MYSQL HANG住的時間; 相信還是值得的。

至於原理: 就是利用OS HARD LINK的原理,
當多個檔名同時指向同一個INODE時,這個INODE的引用數N>1, 刪除其中任何一個檔名都會很快.
因為其直接的物理檔案塊沒有被刪除.只是刪除了一個指標而已;
當INODE的引用數N=1時, 刪除檔案需要去把這個檔案相關的所有資料塊清除,所以會比較耗時;



				<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									if(typeof window.localStorage === "object" && typeof window.csdn.anonymousUserLimit === "object"){
										if(!window.csdn.anonymousUserLimit.judgment()){
											window.csdn.anonymousUserLimit.Jumplogin();
											return false;
										}else if(!currentUserName){
											window.csdn.anonymousUserLimit.updata();
										}
									}
									
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>