1. 程式人生 > 其它 >2022春每日一題:Day 40

2022春每日一題:Day 40

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

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

  從 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/212785

轉自:https://www.cnblogs.com/better-farther-world2099/articles/14713523.html