參數(shù)
1)innodb_file_per_table = OFF? ?表示的是表的數(shù)據(jù)放在系統(tǒng)共享表空間
2)innodb_file_per_table =?ON??每個(gè) InnoDB 表數(shù)據(jù)存儲在一個(gè)以 .ibd 為后綴的文件
中宪塔。從 MySQL 5.6.6 版本開始磁奖,它的默認(rèn)值就是 ON 了。
不論使用 MySQL 的哪個(gè)版本某筐,都將這個(gè)值設(shè)置為 ON比搭。因?yàn)椋粋€(gè)表單獨(dú)存儲為一個(gè)文件更容易管理南誊,而且在你不需要這個(gè)表的時(shí)候身诺,通過 drop table 命令,系統(tǒng)就會直接刪除這個(gè)文件抄囚。而如果是放在共享表空間中霉赡,即使表刪掉了,空間也是不會回收的幔托。
在刪除整個(gè)表的時(shí)候穴亏,可以使用 drop table 命令回收表空間。
數(shù)據(jù)刪除流程:
InnoDB 里的數(shù)據(jù)都是用 B+ 樹的結(jié)構(gòu)組織的。
假設(shè)嗓化,我們要刪掉 R4 這個(gè)記錄锅劝,InnoDB 引擎只會把 R4 這個(gè)記錄標(biāo)記為刪除。如果之后要再插入一個(gè) ID 在 300 和 600 之間的記錄時(shí)蟆湖,可能會復(fù)用這個(gè)位置故爵。但是,磁盤文件的大小并不會縮小隅津。
InnoDB 的數(shù)據(jù)是按頁存儲的诬垂,那么如果我們刪掉了一個(gè)數(shù)據(jù)頁上的所有記錄,整個(gè)數(shù)據(jù)頁就可以被復(fù)用了伦仍。
如果相鄰的兩個(gè)數(shù)據(jù)頁利用率都很小结窘,系統(tǒng)就會把這兩個(gè)頁上的數(shù)據(jù)合到其中一個(gè)頁上,另外一個(gè)數(shù)據(jù)頁就被標(biāo)記為可復(fù)用充蓝。
如果我們用 delete 命令把整個(gè)表的數(shù)據(jù)刪除呢隧枫?結(jié)果就是,所有的數(shù)據(jù)頁都會被標(biāo)記為可復(fù)用谓苟。但是磁盤上官脓,文件不會變小。
delete 命令其實(shí)只是把記錄的位置涝焙,或者數(shù)據(jù)頁標(biāo)記為了“可復(fù)用”卑笨,但磁盤文件的大小是不會變的。也就是說仑撞,通過 delete 命令是不能回收表空間的赤兴。這些可以復(fù)用,而沒有被使用的空間隧哮,看起來就像是“空洞”桶良。
實(shí)際上,不止是刪除數(shù)據(jù)會造成空洞沮翔,插入數(shù)據(jù)也會陨帆。
如果數(shù)據(jù)是按照索引遞增順序插入的,那么索引是緊湊的鉴竭。但如果數(shù)據(jù)是隨機(jī)插入的歧譬,就可能造成索引的數(shù)據(jù)頁分裂。
可以看到搏存,由于 page A 滿了瑰步,再插入一個(gè) ID 是 550 的數(shù)據(jù)時(shí),就不得不再申請一個(gè)新的頁面 page B 來保存數(shù)據(jù)了璧眠。頁分裂完成后缩焦,page A 的末尾就留下了空洞读虏。
更新索引上的值,可以理解為刪除一個(gè)舊的值袁滥,再插入一個(gè)新值盖桥。不難理解,這也是會造成空洞的题翻。
經(jīng)過大量增刪改的表揩徊,都是可能是存在空洞的。所以嵌赠,如果能夠把這些空洞去掉塑荒,就能達(dá)到收縮表空間的目的。而重建表姜挺,就可以達(dá)到這樣的目的齿税。
重建表
花時(shí)間最多的步驟是往臨時(shí)表插入數(shù)據(jù)的過程,如果在這個(gè)過程中炊豪,有新的數(shù)據(jù)要寫入到表 A 的話凌箕,就會造成數(shù)據(jù)丟失。因此词渤,在整個(gè) DDL 過程中牵舱,表 A 中不能有更新。也就是說掖肋,這個(gè) DDL 不是 Online 的仆葡。
MySQL 5.6 版本開始引入的 Online DDL,重建表的大概流程如下:
1.?建立一個(gè)臨時(shí)文件志笼,掃描表 A 主鍵的所有數(shù)據(jù)頁;
2.?用數(shù)據(jù)頁中表 A 的記錄生成 B+ 樹把篓,存儲到臨時(shí)文件中纫溃;
3. 生成臨時(shí)文件的過程中,將所有對 A 的操作記錄在一個(gè)日志文件(row log)中韧掩,對應(yīng)
的是圖中 state2 的狀態(tài)紊浩;
4. 臨時(shí)文件生成后,將日志文件中的操作應(yīng)用到臨時(shí)文件疗锐,得到一個(gè)邏輯數(shù)據(jù)上與表 A 相
同的數(shù)據(jù)文件坊谁,對應(yīng)的就是圖中 state3 的狀態(tài);
5. 用臨時(shí)文件替換表 A 的數(shù)據(jù)文件滑臊。
可以看到口芍,與 改鎖表 DDL過程的不同之處在于,由于日志文件記錄和重放操作這個(gè)功能的存在雇卷,
這個(gè)方案在重建表的過程中鬓椭,允許對表 A 做增刪改操作颠猴。這也就是 Online DDL 名字的來
源。
Online DDL小染,alter 語句在啟動的時(shí)候需要獲取 MDL 寫鎖翘瓮,但是這個(gè)寫鎖在真正
拷貝數(shù)據(jù)之前就退化成讀鎖了。為什么要退化呢裤翩?為了實(shí)現(xiàn) Online资盅,MDL 讀鎖不會阻塞增刪改操
作。
而對于一個(gè)大表來說踊赠,Online DDL 最耗時(shí)的過程就是拷貝數(shù)據(jù)到臨時(shí)表的過程呵扛,這個(gè)步
驟的執(zhí)行期間可以接受增刪改操作。所以臼疫,相對于整個(gè) DDL 過程來說择份,鎖的時(shí)間非常短。對業(yè)務(wù)來
說烫堤,就可以認(rèn)為是 Online 的荣赶。
需要補(bǔ)充說明的是,上述的這些重建方法都會掃描原表數(shù)據(jù)和構(gòu)建臨時(shí)文件鸽斟。對于很大的
表來說拔创,這個(gè)操作是很消耗 IO 和 CPU 資源的。因此富蓄,如果是線上服務(wù)剩燥,你要很小心地控
制操作時(shí)間。如果想要比較安全的操作的話立倍,我推薦你使用 GitHub 開源的 gh-ost 來
做灭红。
Online 和 inplace區(qū)別
改鎖表,我們把表 A 中的數(shù)據(jù)導(dǎo)出來的存放位置叫作 tmp_table口注。這是一個(gè)臨時(shí)表变擒,是在 server 層
創(chuàng)建的。
Online寝志,根據(jù)表 A 重建出來的數(shù)據(jù)是放在“tmp_file”里的娇斑,這個(gè)臨時(shí)文件是 InnoDB
在內(nèi)部創(chuàng)建出來的。整個(gè) DDL 過程都在 InnoDB 內(nèi)部完成材部。對于 server 層來說毫缆,沒有把
數(shù)據(jù)挪動到臨時(shí)表,是一個(gè)“原地”操作乐导,這就是“inplace”名稱的來源苦丁。
重建表的這個(gè)語句 alter table t engine=InnoDB,兽叮,其實(shí)隱含的意思是:
alter table t engine=innodb,ALGORITHM=inplace;
跟 inplace 對應(yīng)的就是拷貝表的方式了芬骄,用法是:
alter table t engine=innodb,ALGORITHM=copy;
當(dāng)你使用 ALGORITHM=copy 的時(shí)候猾愿,表示的是強(qiáng)制拷貝表,對應(yīng)的流程就是圖改鎖表 DDL?的操
作過程账阻。
inplace 跟 Online 是不是就是一個(gè)意思蒂秘?
其實(shí)不是的,只是在重建表這個(gè)邏輯中剛好是這樣而已淘太。
如果我要給 InnoDB 表的一個(gè)字段加全文索引姻僧,寫法是:
alter table t add FULLTEXT(field_name);
這個(gè)過程是 inplace 的,但會阻塞增刪改操作蒲牧,是非 Online 的撇贺。
如果說這兩個(gè)邏輯之間的關(guān)系是什么的話,可以概括為:
1)?DDL 過程如果是 Online 的冰抢,就一定是 inplace 的松嘶;
2)?反過來未必,也就是說 inplace 的 DDL挎扰,有可能不是 Online 的翠订。添加全文索引(FULLTEXT index)和空間索引 (SPATIAL index) 就屬于這種情況。
optimize table遵倦、analyze table 和 alter table 這三種方式重建表的區(qū)別:
1.?從 MySQL 5.6 版本開始尽超,alter table t engine = InnoDB(也就是 recreate)默認(rèn)是online DDL
2.?analyze table t 其實(shí)不是重建表,只是對表的索引信息做重新統(tǒng)計(jì)梧躺,沒有修改數(shù)據(jù)似谁,這個(gè)過程中加了 MDL 讀鎖;
3.?optimize table t 等于 recreate+analyze掠哥。