溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

pt-archiver和自增主鍵的問題怎么解決

發(fā)布時間:2022-04-25 13:51:18 來源:億速云 閱讀:140 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下pt-archiver和自增主鍵的問題怎么解決的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

前言

pt-archiver 是一款常見的 表清理或者歸檔工具。

MySQL 中刪除大表之前可以使用 pt-archiver 批量刪除所有記錄。這樣助于避免在某些情況下您的服務(wù)器可能會意外的情況,比如磁盤 IO 滿導(dǎo)致數(shù)據(jù)庫hang或者影響正常 SQL 慢查。

問題 "使用 pt-archiver  刪除數(shù)據(jù)時,最后一行數(shù)據(jù)未被刪除。這個是不是bug?"

分析

在解決客戶的問題之前,我們需要解釋為什么在刪除大表之前使用 pt-archiver 當(dāng)我們在 MySQL 中刪除一個表時, MySQL 系統(tǒng)會做如下動作:

刪除表數(shù)據(jù)/索引 (ibd) 和定義 (frm) 文件。
刪除觸發(fā)器。
通過刪除要刪除的表來更新表定義緩存。
掃描 InnoDB 緩沖池以查找關(guān)聯(lián)頁面以使其無效。--內(nèi)存到的表會遇到系統(tǒng)hang。

需要注意的是,DROP 是一個 DDL 語句,它需要持有元數(shù)據(jù)鎖 (MDL) 才能完成,這樣會導(dǎo)致所有其他線程必須等待DDL完成,清除表相關(guān)的大量數(shù)據(jù)頁會對緩沖池產(chǎn)生額外的壓力。

最后,table_definition_cache 操作需要 LOCK_open mutex 來清理,這會導(dǎo)致所有其他線程等待直到刪除完成。

為了降低此操作的嚴重性,我們可以使用 pt-archiver 通過批量的形式刪除大量數(shù)據(jù),從而顯著降低表大小。一旦我們從大表中刪除了記錄,DROP 操作就會快速進行而不會對系統(tǒng)性能產(chǎn)生影響。

社區(qū)成員注意到此行為,在 pt-archiver 完成后,該表仍有一行待處理。

# Created table
mysql> CREATE TABLE `tt1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` char(5) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

# Poured random test data into it
mysql> call populate('test','att1',10000,'N');

# Purged data using pt-archiver
[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --purge --where "1=1"

# Verifying count (expected 0, got 1)
mysql> select count(*) from test.tt1;
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

當(dāng)我們使用帶有 --no-delete 參數(shù)的 pt-archiver 進行數(shù)據(jù)歸檔時,也會發(fā)生同樣的情況。我們的工具 pt-archiver 似乎沒有將最大值復(fù)制到目標表。

將表從 tt1 遷移到 tt2 
[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1"

mysql> select count(*) from tt2;
+----------+
| count(*) |
+----------+
|     5008 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from tt1;
+----------+
| count(*) |
+----------+
|     5009 |
+----------+
1 row in set (0.00 sec)

解析

通讀 pt-archiver 文檔,有一個選項 –[no]safe-auto-increment 描述了用法:“不要使用 max AUTO_INCREMENT 歸檔行?!?/p>

這意味著,選項 –safe-auto-increment(默認)添加了一個額外的 WHERE 子句,以防止 pt-archiver 在提升單列 AUTO_INCREMENT 時刪除最新的行,如下面的代碼部分所示:

https://github.com/percona/percona-toolkit/blob/3.x/bin/pt-archiver#L6449
   if ( $o->get('safe-auto-increment')
         && $sel_stmt->{index}
         && scalar(@{$src->{info}->{keys}->{$sel_stmt->{index}}->{cols}}) == 1
         && $src->{info}->{is_autoinc}->{
            $src->{info}->{keys}->{$sel_stmt->{index}}->{cols}->[0]
         }
   ) {
      my $col = $q->quote($sel_stmt->{scols}->[0]);
      my ($val) = $dbh->selectrow_array("SELECT MAX($col) FROM $src->{db_tbl}");
      $first_sql .= " AND ($col < " . $q->quote_val($val) . ")";
   }

讓我們通過空運行輸出看看這兩個命令之間的區(qū)別:

# With --no-safe-auto-increment
[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" <strong>--no-safe-auto-increment</strong> --dry-run
SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) ORDER BY `id` LIMIT 1
SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) AND ((`id` > ?)) ORDER BY `id` LIMIT 1
INSERT INTO `test`.`tt2`(`id`,`a`) VALUES (?,?)
# Without --no-safe-auto-increment (default)
[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" --dry-run
SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) <strong>AND (`id` < '5009')</strong> ORDER BY `id` LIMIT 1
SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) <strong>AND (`id` < '5009')</strong> AND ((`id` > ?)) ORDER BY `id` LIMIT 1
INSERT INTO `test`.`tt2`(`id`,`a`) VALUES (?,?)

注意到上面的附加子句 "AND ( id< '5009')" 了嗎?

如果服務(wù)器重新啟動,&ndash;no-safe-auto-increment 的這個選項可以防止重新使用 AUTO_INCREMENT 值。請注意,額外的 WHERE 子句包含自歸檔或清除作業(yè)開始時自增列的最大值。如果在 pt-archiver 運行時插入新行,pt-archiver 將看不到它們。

好吧,現(xiàn)在我們知道了為什么沒有刪除干凈的“原因”,但為什么呢?AUTO_INCREMENT 的安全問題是什么?

AUTO_INCREMENT 計數(shù)器存儲在內(nèi)存中,當(dāng) MySQL 8.0之前的版本 重新啟動(崩潰或其他)時,計數(shù)器將重置為最大值。如果發(fā)生這種情況并且表正在接受寫入,則 AUTO_INCREMENT 值將更改。

# deleting everything from table
mysql> delete from tt1;
...
mysql> show table status like 'tt1'\G
*************************** 1. row ***************************
           Name: tt1
         Engine: InnoDB
...
 Auto_increment: 10019
...

# Restarting MySQL
[root@centos_2 ~]# systemctl restart mysql

# Verifying auto-increment counter
[root@centos_2 ~]# mysql test -e "show table status like 'tt1'\G"
*************************** 1. row ***************************
           Name: tt1
         Engine: InnoDB
...
 Auto_increment: 1
...

上面的測試結(jié)果告訴我們: 這里的問題實際上并不在于 pt-archiver,而在于參數(shù)選項。在處理 AUTO_INCREMENT 列時使用 pt-archiver 時,了解使用 &ndash;no-safe-auto-increment 選項很重要。

讓我們用我們的實驗室數(shù)據(jù)來驗證它。

# Verifying the usage of –no-safe-auto-increment option
[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --purge --where "1=1" --no-safe-auto-increment

mysql> select count(*) from test.tt1;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

使用 &ndash;no-delete 選項的復(fù)制操作也是如此。

[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" --no-safe-auto-increment

mysql> select count(*) from tt1; select count(*) from tt2;
+----------+
| count(*) |
+----------+
|     5009 |
+----------+
1 row in set (0.00 sec)

+----------+
| count(*) |
+----------+
|     5009 |
+----------+
1 row in set (0.00 sec)

通過上面的代碼和實際測試,我們知道了 pt-archiver 的 -[no]safe-auto-increment 選項的原理和作用 。在我們得出一切都很好的結(jié)論之前,讓我們多考慮一下選項本身存在的意義。

  • 默認情況下,&ndash;no-delete 操作應(yīng)包含 &ndash;no-safe-auto-increment 選項。目前,safe-auto-increment 是默認行為。當(dāng)我們使用 pt-archiver 的 --no-delete 選項時,沒有刪除操作。這意味著 safe-auto-increment 不應(yīng)成為關(guān)注的原因。

  • 對于 MySQL 8.0,不需要 safe-auto-increment 選項。因為 MySQL 8.0 開始,自增的值是持久化的,并且在實例重新啟動或崩潰后自增的最大值不變。

而且由于 MySQL 8.0 auto-increment 是通過重做日志持久化的,這使得它們成為pt-archiver 不關(guān)心的一個原因。因此,我們根本不需要 safe-auto-increment 選項。

以上就是“pt-archiver和自增主鍵的問題怎么解決”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI