MySQL的表空間是什么


今天我要跟你分享的話題是:“大家常說的表空間到底是什么?究竟什么又是數據表?”

這其實是一個概念性的知識點,當作拓展知識。涉及到的概念大家了解一下就好,涉及的參數,留個印象就好。

  從 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM