MySQL怎麼解決delete大量資料後空間不釋放的問題

2022-07-01 14:00:54
本篇文章給大家帶來了關於的相關知識,其中主要整理了解決delete大量資料後空間不釋放的相關問題,MySQL 中 insert 與 update 都可能導致頁分裂,這樣就存在碎片,下面一起來看一下,希望對大家有幫助。

推薦學習:

MySQL 的幾種碎片整理方案總結(解決delete大量資料後空間不釋放的問題)

1.背景知識?

1.1 為什麼會有碎片?

  1. MySQL 中 insert 與 update 都可能導致頁分裂,這樣就存在碎片。

    對於大量的UPDATE,也會產生檔案碎片化 , Innodb的最小物理儲存分配單位是頁(page),而UPDATE也可能導致頁分裂(page split),頻繁的頁分裂,頁會變得稀疏,並且被不規則的填充,所以最終資料會有碎片。

  2. delete 語句實際上只是給資料打個標記,並且記錄到一個連結串列中,這樣就形成了留白空間。

    在InnoDB中,刪除一些行,這些行只是被標記為「已刪除」,而不是真的從索引中物理刪除了,因而空間也沒有真的被釋放回收。InnoDB的Purge執行緒會非同步的來清理這些沒用的索引鍵和行。

  3. 當執行插入操作時,MySQL會嘗試使用空白空間,但如果某個空白空間一直沒有被大小合適的資料佔用,仍然無法將其徹底佔用,就形成了碎片;

  4. 總結:

    1. 表的增刪改操作,可能會造成資料空洞的,當對錶進行大量的增刪改操作後,資料空洞存在的可能性比較大。

    2. MySQL刪除資料幾種情況以及是否釋放磁碟空間:

      1. drop ,truncate 立刻釋放磁碟空間 ,不管是 Innodb和MyISAM ;
      • truncate table其實有點類似於drop table 然後creat,只不過這個create table 的過程做了優化,比如表結構檔案之前已經有了等等。所以速度上應該是接近drop table的速度;
      1. delete from table_name刪除表的全部資料,對於MyISAM 會立刻釋放磁碟空間 (應該是做了特別處理,也比較合理),InnoDB 不會釋放磁碟空間;
      2. 對於 delete from table_name where xxx; 帶條件的刪除, 不管是innodb還是MyISAM都不會釋放磁碟空間;
      3. delete操作以後使用optimize table table_name 會立刻釋放磁碟空間。不管是innodb還是myisam 。所以要想達到釋放磁碟空間的目的,delete以後執行optimize table 操作。
      4. delete from表以後雖然未釋放磁碟空間,但是下次插入資料的時候,仍然可以使用這部分空間。

1.2 碎片帶來的問題

  • 當MySQL對資料進行掃描時,它掃描的物件實際是列表的容量需求上限,也就是資料被寫入的區域中處於峰值位置的部分;

  • MySQL資料庫中的表在進行了多次delete、update和insert後,表空間會出現碎片。定期進行表空間整理,消除碎片可以提高存取表空間的效能。

  • 這種碎片不僅額外增加了儲存代價,同時也因為資料碎片化降低了表的掃描效率。

  • 碎片若不整理,那麼可能會長期佔據磁碟空間,導致磁碟使用率越來越高。

2. 如何清理碎片?

修復問題的前提是要先找到問題,這樣才能對症下藥。

2.1. 檢視表的碎片情況

  1. 檢視資料庫中每個存在碎片的表

    mysql> select concat('optimize table ',table_schema,'.',table_name,';'),data_free,engine from information_schema.tables where data_free>0 and engine !='MEMORY';
    +-----------------------------------------------------------+-----------+--------+
    | concat('optimize table ',table_schema,'.',table_name,';') | DATA_FREE | ENGINE |
    +-----------------------------------------------------------+-----------+--------+
    | optimize table abc.t_user_answer;                    		|   2097152 | InnoDB |
    | optimize table mysql.time_zone_transition;                |   4194304 | InnoDB |
    | optimize table mysql.time_zone_transition_type;           |   4194304 | InnoDB |
    | optimize table mysql.user;                                |   4194304 | InnoDB |
    。。。。
  2. 檢視指定表的碎片情況

     mysql> show table status like 't_user'\G
     *************************** 1. row ***************************
                Name: t_user
              Engine: InnoDB
             Version: 10
          Row_format: Dynamic
                Rows: 4333
      Avg_row_length: 589
         Data_length: 2555904
     Max_data_length: 0
        Index_length: 2719744
           Data_free: 4194304
      Auto_increment: NULL
         Create_time: 2021-11-19 10:13:31
         Update_time: 2022-04-20 14:28:42
          Check_time: NULL
           Collation: utf8mb4_general_ci
            Checksum: NULL
      Create_options:
             Comment:
     1 row in set (0.00 sec)

    Data_free: 4194304 就代表碎片的byte數。如果經常刪改資料表,會造成大量的Data_free 頻繁 刪除記錄 或修改有可變長度欄位的表。

  3. 找到碎片化最嚴重的表

    SELECT table_schema, TABLE_NAME, concat(data_free/1024/1024, 'M') as data_free
    FROM `information_schema`.tables
    WHERE data_free > 3 * 1024 * 1024
    	AND ENGINE = 'innodb'
    ORDER BY data_free DESC

2.2 清理碎片(回收空間)的方法

官方檔案參考
在這裡插入圖片描述

通常有這幾種做法

  1. alter table tb_test engine=innodb; (本質上是 recreate)
  2. optimize table tb_test; (本質上是 recreate,但是在不同建立下會有區別)
  3. ALTER TABLE tablename FORCE (在InnoDB表中等價於 alter table tb_test engine=innodb; )
  4. mysqlcheck 批次表空間優化
  5. gh-ost/pt-osc
  6. pt-online-schema-change (本質上也是 先備份舊錶資料,然後 truncate 舊錶)

1. alter table tb_test engine=innodb 原理介紹

這其實是一個NULL操作,表面上看什麼也不做,實際上重新整理碎片了.當執行優化操作時,實際執行的是一個空的 ALTER 命令,但是這個命令也會起到優化的作用,它會重建整個表,刪掉未使用的空白空間.

Running ALTER TABLE tbl_name ENGINE=INNODB on an existing InnoDB table performs a 「null」 ALTER TABLE operation, which can be used to defragment an InnoDB table, as described in Section 15.11.4, 「Defragmenting a Table」. Running ALTER TABLE tbl_name FORCE on an InnoDB table performs the same function.

    MySQL5.6 開始採用 Inplace 方式重建表,Alter 期間,支援 DML 查詢和更新操作,語句為 alter table t engine=innodb, ALGORITHM=inplace;之所以支援 DML 更新操作,是因為資料拷貝期間會將 DML 更新操作記錄到 Row log 中。

    重建過程中最耗時的就是拷貝資料的過程,這個過程中支援 DML 查詢和更新操作,對於整個 DDL 來說,鎖時間很短,就可以近似認為是 Online DDL。

    執行過程:

    1、獲取 MDL(Meta Data Lock)寫鎖,innodb 內部建立與原表結構相同的臨時檔案

    2、拷貝資料之前,MDL 寫鎖退化成 MDL 讀鎖,支援 DML 更新操作

    3、根據主鍵遞增順序,將一行一行的資料讀出並寫入到臨時檔案,直至全部寫入完成。並且,會將拷貝期間的 DML 更新操作記錄到 Row log 中

    4、上鎖,再將 Row log 中的資料應用到臨時檔案

    5、互換原表和臨時表表名

    6、刪除臨時表

2. optimize table xxx;

OPTIMIZE TABLE語句可以重新組織表、索引的物理儲存,減少儲存空間,提高存取的I/O效率。類似於碎片整理功能。

MySQL可以通過optimize table語句釋放表空間,重組表資料和索引的物理頁,減少表所佔空間和優化讀寫效能

  1. 使用語法

    OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_n說ame [, tbl_name] …

    • 對於主從架構, LOCAL 引數可以讓這個過程不寫入 binlog ,這樣在主庫上執行時就不會同步給從庫了
    • 預設情況下,MySQL將OPTIMIZE TABLE語句寫入二進位制紀錄檔,以便它們複製到slave伺服器。如果不想寫二進位制紀錄檔,使用命令時加上NO_WRITE_To_BINLOG或LOCAL關鍵字即可。
    • 使用這個語句需要具有對目標表的SELECT、INSERT許可權。
  2. 注意:

    1. 需要有足夠的空間才能進行OPTIMIZE TABLE。 (剩餘空間必須 > 被 OPTIMIZE 的表的大小)

    2. OPTIMIZE 只對獨立表空間(innodb_file_per_table=1)才有用,對共用表空間不起作用。

      對於共用表空間,如果需要瘦身: 必須將資料匯出,刪除ibdata1,然後將 innodb_file_per_table 設定為獨立表空間, 然後將資料匯入進來。

    3. 對於InnoDB的表,OPTIMIZE TABLE 的工作原理如下

      對於InnoDB表, OPTIMIZE TABLE對映到ALTER TABLE … FORCE(或者這樣翻譯:在InnoDB表中等價 ALTER TABLE … FORCE),它重建表以更新索引統計資訊並釋放聚簇索引中未使用的空間。

      當您在InnoDB表上執行時,它會顯示在OPTIMIZE TABLE的輸出中,如下所示:
      mysql> OPTIMIZE TABLE foo; 
      +----------+----------+----------+---------------------------------------+ 
      | Table    | Op       | Msg_type | Msg_text                                                          | 
      +----------+----------+----------+---------------------------------------+ 
      | test.foo | optimize | note     | Table does not support optimize, doing recreate + analyze instead | 
      | test.foo | optimize | status   | OK                                                                | 
      +----------+----------+----------+---------------------------------------+ 
      
      # 但這個提示語可以忽略,從嚴格的意義講,說InnoDB不支援optimize table,其實不太準確。 因為 MYSQL的檔案說明了,當INNODB 的表,MYSQL會以 ALTER TABLE force  +  analyze 去執行這個命令(相當於做了recreate和analyze)。 所以最終還是會看到 OK 的狀態。 
      # https://stackoverflow.com/questions/30635603/what-does-table-does-not-support-optimize-doing-recreate-analyze-instead-me
    4. 對於MYISAM表,OPTIMIZE TABLE 的工作原理:
      1. 如果表已刪除或分隔行,就修復該表。
      2. 如果索引頁沒有排序,就排序它們。
      3. 如果表的統計資訊不是最新的(而且修復不能通過對索引進行排序),就更新它們。

    5. **執行時也可以發現報錯: Temporary file write failure. **

      建議參考這片文章:
      Mysql optimize table 時報錯 Temporary file write failure. 的解決方案

  3. optimize 語句的官網介紹

    • 如果您已經刪除了表的一大部分,或者如果您已經對含有可變長度行的表(含有VARCHAR, BLOB或TEXT列的表)進行了很多更改,則應使用 OPTIMIZE TABLE。

    • 被刪除的記錄被保持在連結清單中,後續的INSERT操作會重新使用舊的記錄位置。您可以使用OPTIMIZE TABLE來重新利用未使用的空間,並整理資料檔案的碎片。

    • 在多數的設定中,您根本不需要執行OPTIMIZE TABLE。即使您對可變長度的行進行了大量的更新,您也不需要經常執行,每週一次或每月一次 即可,只對特定的表執行。

  4. Mysql 5.6 之前 在OPTIMIZE TABLE執行過程中,MySQL會鎖定表,5.6之後有了 Online DDL 則大大減少了鎖表時間。

3. alter table、analyze table和optimize table區別

  • alter table tb_test engine = innodb;

    • (也就是 recreate)MySQL 5.5以前用Offline的方式重建表,5.6以後用Online的方式重建表;
  • analyze table tb_test ;

    • 重新統計表的索引資訊,不會修改資料,不會重建表,整個過程加MDL讀
  • optimize table tb_test ;

    • 是 alter table xxx = innodb; + analyze table xxx; 的過程。

4. OPTIMIZE TABLE 和ALTER TABLE xxxx ENGINE= INNODB哪個更好

  • OPTIMIZE TABLE 還是ALTER TABLE xxxx ENGINE= INNODB 基本上是一樣的。但是在有些情況下,ALTER TABLE xxxx ENGINE= INNODB更好。
    • 例如: old_alter_table 系統變數沒有啟用等等。
  • 另外: 對於MyISAM型別表,使用ALTER TABLE xxxx ENGINE= INNODB 是明顯要優於 OPTIMIZE TABLE這種方法的。

2.3 官方建議

MySQL官方建議不要經常(每小時或每天)進行碎片整理,一般根據實際情況,只需要每週或者每月整理一次即可(我們現在是每月凌晨4點清理mysql所有範例下的表碎片)

推薦學習:

以上就是MySQL怎麼解決delete大量資料後空間不釋放的問題的詳細內容,更多請關注TW511.COM其它相關文章!