今天我要跟你分享的話題是:“大家常說的表空間到底是什么?究竟什么又是數據表?”
這其實是一個概念性的知識點,當作拓展知識。涉及到的概念大家了解一下就好,涉及的參數,留個印象就好。
從 InnoDB存儲引擎的邏輯存儲結構看,所有數據都被邏輯地存放在一個空間中,稱之為表空間( tablespace)。表空間又由段(segment)、區( extent)、頁(page)組成。頁在一些文檔中有時也稱為塊( block), InnoDB存儲引擎的邏輯存儲結構大致如圖所示。
一、什么是表?
但凡是用過MySQL都知道,直觀上看,MySQL的數據都存在數據表中。
比如一條Update SQL:
update user set username = '白日夢' where id = 999;
它將user這張數據表中id為1的記錄的username列修改成了‘白日夢'
這里的user其實就是數據表。當然這不是重點,重點是我想表達:數據表其實是邏輯上的概念。而下面要說的表空間是物理層面的概念。
二、什么是表空間?
不知道你有沒有看到過這句話:“在innodb存儲引擎中數據是按照表空間來組織存儲的”。其實有個潛台詞是:表空間是表空間文件是實際存在的物理文件。
大家不用糾結為啥它叫表空間、為啥表空間會對應着磁盤上的物理文件,因為MySQL就是這樣設計、設定的。直接接受這個概念就好了。
MySQL有很多種表空間,下面一起來了解一下。
三、sys表空間
你可以像下面這樣查看你的MySQL的系統表空間
Value部分的的組成是:name:size:attributes
默認情況下,MySQL會初始化一個大小為12MB,名為ibdata1文件,並且隨着數據的增多,它會自動擴容。
這個ibdata1文件是系統表空間,也是默認的表空間,也是默認的表空間物理文件,也是傳說中的共享表空間。
關於這個共享表空間,直觀上看,如果這個表空間能為multiple tables.存儲數據,那么它就可以被稱為共享表空間,所以你可以認為系統表空間是共享表空間。
四、配置sys表空間
系統表空間的數量和大小可以通過啟動參數:innodb_data_file_path
# my.cnf [mysqld] innodb_data_file_path=/dir1/ibdata1:2000M;/dir2/ibdata2:2000M:autoextend
五、file per table 表空間
如果你想讓每一個數據庫表都有一個單獨的表空間文件的話,可以通過參數innodb_file_per_table設置。
這個參數只有在MySQL5.6或者是更高的版本中才可以使用。
可以通過配置文件
[mysqld] innodb_file_per_table=ON
也可以通過命令
mysql> SET GLOBAL innodb_file_per_table=ON;
讓你將其設置為ON,那之后InnoDB存儲引擎產生的表都會自己獨立的表空間文件。
獨立的表空間文件命名規則:表名.ibd
注意:
獨立表空間文件中僅存放該表對應數據、索引、insert buffer bitmap。
其余的諸如:undo信息、insert buffer 索引頁、double write buffer 等信息依然放在默認表空間,也就是共享表空間中。
需要先了解即使你設置了innodb_file_per_table=ON 共享表空間的體量依然會不斷的增長,並且你即使你不斷的使用undo進行rollback,共享表空間大小也不會縮減就好了。
查看我的表空間文件:
最后再簡述一下這種file per table的優缺點:
優點:
- 提升容錯率,表A的表空間損壞后,其他表空間不會收到影響。s
- 使用MySQL Enterprise Backup快速備份或還原在每表文件表空間中創建的表,不會中斷其他InnoDB 表的使用
缺點:
對fsync系統調用來說不友好,如果使用一個表空間文件的話單次系統調用可以完成數據的落盤,但是如果你將表空間文件拆分成多個。原來的一次fsync可能會就變成針對涉及到的所有表空間文件分別執行一次fsync,增加fsync的次數。
六、臨時表空間
臨時表空間用於存放用戶創建的臨時表和磁盤內部臨時表。
參數innodb_temp_data_file_path定義了臨時表空間的一些名稱、大小、規格屬性如下圖:
查看臨時表空間文件存放的目錄
七、undo表空間
相信你肯定聽過說undolog,常見的當你的程序想要將事物rollback時,底層MySQL其實就是通過這些undo信息幫你回滾的。
在MySQL的設定中,有一個表空間可以專門用來存放undolog的日志文件。
然而,在MySQL的設定中,默認的會將undolog放置到系統表空間中。
如果你的MySQL是新安裝的,那你可以通過下面的命令看看你的MySQL undo表空間的使用情況:
大家可以看到,我的MySQL的undo log 表空間有兩個。
也就是我的undo從默認的系統表空間中轉移到了undo log專屬表空間中了。
那undo log到底是該使用默認的配置放在系統表空間呢?還是該放在undo表空間呢?
這其實取決服務器使用的存儲卷的類型。
如果是SSD存儲,那推薦將undo info存放在 undo表空間中。
八、mysql表碎片清理和表空間收縮
mysql表碎片清理和表空間收縮(即清理碎片后report_site_day.ibd文件磁盤空間減小,該方案基於獨立表空間存儲方式)
OPTIMIZETABLE [tablename],當然這種方式只適用於獨立表空間
清除碎片的優點:
降低訪問表時的IO,提高mysql性能,釋放表空間降低磁盤空間使用率。
OPTIMIZE TABLE ipvacloud.report_site_day;對myisam表有用 對innodb也有用,系統會自動把它轉成 ALTER TABLE report_site_day ENGINE = Innodb; 這是因為optimize table的本質,就是alter table
所以不管myisam引擎還是innodb引擎都可以使用OPTIMIZE TABLE回收表空間。
mysql innodb引擎 長時間使用后,數據文件遠大於實際數據量(即report_site_day.ibd文件越來越大),導致空間不足。
就是我的mysql服務器使用了很久之后,發現\data\ipvacloud\report_site_day.ibd 目錄的空間占滿了我系統的整個空間,馬上就要滿了。
MySQL5.5默認是共享表空間 ,5.6中默認是獨立表空間(表空間管理類型就這2種)
獨立表空間 就是采用和MyISAM 相同的方式, 每個表擁有一個獨立的數據文件( .idb )
1.每個表都有自已獨立的表空間。
2.每個表的數據和索引都會存在自已的表空間中。
3.可以實現單表在不同的數據庫中移動(將一個庫的表移動到另一個庫里,可以正常使用)。
4.drop table自動回收表空間 ,刪除大量數據后可以通過alter table XX engine = innodb;回收空間。
InnoDB引擎 frm ibd文件說明:
1.frm :描述表結構文件,字段長度等
2.ibd文件
a如果采用獨立表存儲模式(5.6),data\a中還會產生report_site_day.ibd文件(存儲數據信息和索引信息)D:\java\mysql5.6\data\ipvacloudreport_site_day.frm 和
D:\java\mysql5.6\data\ipvacloud\report_site_day.ibd
b如果采用共享存儲模式(5.5),數據信息和索引信息都存儲在ibdata1中
(其D:\java\mysql5.6\data\目錄下沒有.ibd文件,只有frm文件)
D:\java\mysql5.5\data\ipvacloudreport_site_day.frm
查看當前數據庫的表空間管理類型(on表示獨立表空間開啟,5.6默認開啟獨立) 腳本:show variables like "innodb_file_per_table"; mysql> show variables like "innodb_file_per_table";
1、小結
結合mysql官方網站的信息,個人是這樣理解的。當你刪除數據時,mysql並不會回收,被已刪除數據的占據的存儲空間,以及索引位。而是空在那里,而是等待新的數據來彌補這個空缺,這樣就有一個缺少,如果一時半會,沒有數據來填補這個空缺,那這樣就太浪費資源了。所以對於寫比較頻煩的表,要定期進行optimize,一個月一次,看實際情況而定了。
舉個例子來說吧。有100個php程序員辭職了,但是呢只是人走了,php的職位還在那里,這些職位不會撤銷,要等新的php程序來填補這些空位。招一個好的程序員,比較難。我想大部分時間會空在那里。哈哈。
當我們使用mysql進行delete數據,delete完以后,發現空間文件ibd並沒有減少,這是因為碎片空間的存在,舉個例子,一共公司有10號員工,10個座位,被開除了7個員工,但這些座位還是保留的,碎片整理就像,讓剩下的3個員工都靠邊坐,然后把剩下的7個作為給砸掉,這樣就能釋放出空間了
好處除了減少表數據與表索引的物理空間,還能降低訪問表時的IO,這個比較理解,整理之前,取數據需要跨越很多碎片空間,這時需要時間的,整理后,想要的數據都放在一起了,直接拿就拿到了,效率提高。
2、手冊中關於OPTIMIZE的一些用法(標紅的為應用場景)和描述
OPTIMIZE TABLE tbl_name [, tbl_name] ...
如果您已經刪除了表的一大部分,或者如果您已經對含有可變長度行的表(含有VARCHAR, BLOB或TEXT列的表)進行了很多更改,則應使用OPTIMIZE TABLE。被刪除的記錄被保持在鏈接清單中,后續的INSERT操作會重新使用舊的記錄位置。您可以使用OPTIMIZE TABLE來重新利用未使用的空間,並整理數據文件的碎片。
碎片產生的原因
(刪除時留白, 插入時嘗試使用留白空間 (當刪除后並未將所有留空的都插入數據,既可以認為未被立即使用的留空就是碎片)
(1)表的存儲會出現碎片化,每當刪除了一行內容,該段空間就會變為被留空,而在一段時間內的大量刪除操作,會使這種留空的空間變得比存儲列表內容所使用的空間更大;
(2)當執行插入操作時,MySQL會嘗試使用空白空間,但如果某個空白空間一直沒有被大小合適的數據占用,仍然無法將其徹底占用,就形成了碎片;
(3)當MySQL對數據進行掃描時,它掃描的對象實際是列表的容量需求上限,也就是數據被寫入的區域中處於峰值位置的部分;
一個表有1萬行,每行10字節,會占用10萬字節存儲空間,執行刪除操作,只留一行,實際內容只剩下10字節,但MySQL在讀取時,仍看做是10萬字節的表進行處理,所以,碎片越多,會降低訪問表時的IO,影響查詢性能。
3、備注:
1.MySQL官方建議不要經常(每小時或每天)進行碎片整理,一般根據實際情況,只需要每周或者每月整理一次即可。
2.OPTIMIZE TABLE只對MyISAM,BDB和InnoDB表起作用,尤其是MyISAM表的作用最為明顯。此外,並不是所有表都需要進行碎片整理,一般只需要對包含上述可變長度的文本數據類型的表進行整理即可。
3.在OPTIMIZE TABLE 運行過程中,MySQL會鎖定表。
4.默認情況下,直接對InnoDB引擎的數據表使用OPTIMIZE TABLE,可能會顯示「 Table does not support optimize, doing recreate + analyze instead」的提示信息。這個時候,我們可以用mysqld --skip-new或者mysqld --safe-mode命令來重啟MySQL,以便於讓其他引擎支持OPTIMIZE TABLE。
OPTIMIZE 操作會暫時鎖住表,而且數據量越大,耗費的時間也越長,它畢竟不是簡單查詢操作。
比較好的方式就是做個shell,定期檢查mysql中 information_schema.TABLES字段,查看 DATA_FREE 字段,大於0話,就表示有碎片。
問題產生: 例如你有1個表格里面有約10000000條,大概10G的數據,但是你手動刪除了5000000條數據,即約5G的數據,但是刪除后,你會發現系統的空間還是占用了10G,
解決方案: 表空間收縮即D:\java\mysql5.6\data\ipvacloud\report_site_day.ibd文件變小。
create database frag_test; use frag_test; create table frag_test (c1 varchar(64)); insert into frag_test values ('this is row 1'); insert into frag_test values ('this is row 2'); insert into frag_test values ('this is row 3'); insert into frag_test values ('this is row 4'); insert into frag_test values ('this is row 5'); SELECT * FROM frag_test; -- 碎片查看(即查看frag_test庫下所有表的狀態,1條記錄是1個表) frag_test是庫名 -- 需要注意的是,“data_free”一欄顯示出了我們刪除一行后所產生的留空空間 刪除前 Data_free: 0字節 刪除一條記錄后再查看碎片 Data_free: 20字節 -- 如果沒有及時插入,那么刪除一條記錄后,留空的20字節就變成碎片; 現在如果你將兩萬條記錄刪到只剩一行, -- 列表中有用的內容將只占二十字節,但MySQL在讀取中會仍然將其視同於一個容量為四十萬字節的列表進行處理,並且除二十字節以外,其它空間都被白白浪費了。 -- 現在我們刪除一行,並再次檢測: delete from frag_test where c1 = 'this is row 2'; -- 刪除一條記錄后再查看碎片 Data_free: 20字節 即留空了20字節 data_free 是碎片空間 show table status from frag_test; --字段解釋: --Data_length : 數據的大小。 --Index_length: 索引的大小。 --Data_free :數據在使用中的留存空間,如果經常刪改數據表,會造成大量的Data_free 頻繁 刪除記錄 或修改有可變長度字段的表 -- data_free碎片空間 TABLE_SCHEMA后等於表名 (data_length+index_length)數據和數據索引的之和的空間 data_free/data_length+index_length>0.30 的表認為是需要清理碎片的表 select table_schema db,table_name,engine,table_rows,data_free,data_length+index_length length from information_schema.tables where TABLE_SCHEMA='frag_test'; -- table_schema db, table_name, data_free, engine依次表示 數據庫名稱 表名稱 碎片所占字節空間 表引擎名稱 -- 列出所有已經產生碎片的表 ('information_schema', 'mysql'這兩個庫是mysql自帶的庫) select table_schema db, table_name, data_free, engine,table_rows,data_length+index_length length from information_schema.tables where table_schema not in ('information_schema', 'mysql') and data_free > 0; -- 庫名.表名 清理2個表的碎片(逗號隔開即可) OPTIMIZE TABLE ipvacloud.article,ipvacloud.aspnet_users_viewway; -- 存儲過程里的table_schema就是數據庫名稱 雖然提示 Table does not support optimize, doing recreate + analyze instead 該命令執行完畢后 返回命令,雖然提示不支持optimize,但是已經進行重建和分析,空間已經回收(即碎片得到整理,表空間得到回收)。 原來對於InnoDB 通過該命令還是有用的,OPTIMIZE TABLE ipvacloud.article; OPTIMIZE TABLE ipvacloud.article; -- 清除碎片操作會暫時鎖表,數據量越大,耗費的時間越長 可以做個腳本,例如每月凌晨3點,檢查DATA_FREE字段, -- 大於自己認為的警戒值(碎片空間占數據和數據索引空間之和的百分比>0.30)的話,就清理一次 /* 清理mysql下實例下表碎片(當碎片字節空間占 數據字節與索引字節空間 之和大於0.30時, 這些表的碎片都需要清理,使用游標遍歷清理) 定時任務事件 每月凌晨4點調用此清理表碎片的任務 table_schema是數據庫名 OPTIMIZE TABLE ipvacloud.article; */ DROP PROCEDURE IF EXISTS `optimize_table`; DELIMITER ;; CREATE PROCEDURE `optimize_table`() BEGIN DECLARE tableSchema VARCHAR(100); DECLARE tableName VARCHAR(100); DECLARE stopFlag INT DEFAULT 0; -- 大於30%碎片率的清理 DECLARE rs CURSOR FOR SELECT table_schema,table_name FROM information_schema.tables WHERE ((data_free/1024)/((data_length+index_length+data_free)/1024)) > 0.30; DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1; OPEN rs; WHILE stopFlag <> 1 DO FETCH NEXT FROM rs INTO tableSchema,tableName; IF stopFlag<>1 THEN -- SET @table_optimize = CONCAT('ALTER TABLE `',tableName,'` ENGINE = INNODB'); SET @table_optimize = CONCAT('OPTIMIZE TABLE `',table_schema,'`.`',tableName,'`'); PREPARE sql_optimize FROM @table_optimize; EXECUTE sql_optimize; END IF; END WHILE; CLOSE rs; END ;; DELIMITER ; /* 此定時任務 事件每月凌晨4點清理mysql實例下的表碎片 */ DROP EVENT IF EXISTS `event_optimize_table`; DELIMITER ;; CREATE EVENT `event_optimize_table` ON SCHEDULE EVERY 1 MONTH STARTS '2017-12-15 04:00:00' ON COMPLETION PRESERVE ENABLE DO CALL optimize_table() ;; DELIMITER ;
如何縮小共享表空間 optimize table xxx; 對共享表空間不起作用
如果不把數據分開存放的話,這個文件的大小很容易就上了G,甚至幾十G。對於某些應用來說,並不是太合適。因此要把此文件縮小。
無法自動收縮,必須數據導出,刪除ibdata1,然后數據導入(數據庫變為獨享表空間)
解決方法:數據文件單獨存放(共享表空間如何改為每個表獨立的表空間文件)。
本地mysql5.5創建一個ipvacloud庫,將其他服務的1張表數據傳輸到本地的ipvacloud
ibdata1(ibdata1存放數據和索引等)文件從18M增加到178M 其ipvacloud文件下只新增了frm文件 即D:\java\mysql5.5\data\ipvacloud\report_site_day.frm
導出數據(navicat導出或mysqldump)
關閉MySQL服務:
刪除ibdat1、ib_log*和應用數據庫目錄
更改myini文件(在最后一行添加innodb_file_per_table=1)
啟動mysql 使用此命令看(ibdata1又回到初始的18M,ipvacoud已是空的) 獨立表空間是否開啟成功(on表示開啟成功)show variables like '%per_table%';
導入數據庫 .sql文件(導入數據成功后ibdat1從18M增加到34M, 獨立表空間有ibd文件,來存放數據和索引信息)
將表由共享表空間遷移到了獨立表空間中,同時也是對共享表空間"瘦身"
>mysqldump -h192.168.2.227 -u root -p ipvacloud site_all_info > d:456.sql
參考文章:
https://www.jb51.net/article/200547.htm
https://blog.51cto.com/xiaocao13140/2127856