關(guān)于pt-archiver和自增主鍵的那些事
本文Percona Blog 的譯文,原文移步文章末尾的 閱讀原文。
前言
pt-archiver 是一款常見的 表清理或者歸檔工具。
MySQL 中刪除大表之前可以使用 pt-archiver 批量刪除所有記錄。這樣助于避免在某些情況下您的服務(wù)器可能會意外的情況,比如磁盤 IO 滿導(dǎo)致數(shù)據(jù)庫hang或者影響正常 SQL 慢查。
筆者最近遇到一個(gè)案例 ,有客戶反饋 "使用 pt-archiver 刪除數(shù)據(jù)時(shí),最后一行數(shù)據(jù)未被刪除。這個(gè)是不是bug?"
分析
在解決客戶的問題之前,我們需要解釋為什么在刪除大表之前使用 pt-archiver 當(dāng)我們在 MySQL 中刪除一個(gè)表時(shí), MySQL 系統(tǒng)會做如下動作:
刪除表數(shù)據(jù)/索引 (ibd) 和定義 (frm) 文件。
刪除觸發(fā)器。
通過刪除要刪除的表來更新表定義緩存。
掃描 InnoDB 緩沖池以查找關(guān)聯(lián)頁面以使其無效。--內(nèi)存到的表會遇到系統(tǒng)hang。
需要注意的是,DROP 是一個(gè) DDL 語句,它需要持有元數(shù)據(jù)鎖 (MDL) 才能完成,這樣會導(dǎo)致所有其他線程必須等待DDL完成,清除表相關(guān)的大量數(shù)據(jù)頁會對緩沖池產(chǎn)生額外的壓力。
最后,table_definition_cache 操作需要 LOCK_open mutex 來清理,這會導(dǎo)致所有其他線程等待直到刪除完成。
為了降低此操作的嚴(yán)重性,我們可以使用 pt-archiver 通過批量的形式刪除大量數(shù)據(jù),從而顯著降低表大小。一旦我們從大表中刪除了記錄,DROP 操作就會快速進(jìn)行而不會對系統(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 進(jìn)行數(shù)據(jù)歸檔時(shí),也會發(fā)生同樣的情況。我們的工具 pt-archiver 似乎沒有將最大值復(fù)制到目標(biāo)表。
將表從?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 文檔,有一個(gè)選項(xiàng) –[no]safe-auto-increment 描述了用法:“不要使用 max AUTO_INCREMENT 歸檔行。”
這意味著,選項(xiàng) –safe-auto-increment(默認(rèn))添加了一個(gè)額外的 WHERE 子句,以防止 pt-archiver 在提升單列 AUTO_INCREMENT 時(shí)刪除最新的行,如下面的代碼部分所示:
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)?.?")"; ???}
讓我們通過空運(yùn)行輸出看看這兩個(gè)命令之間的區(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ù)器重新啟動,–no-safe-auto-increment 的這個(gè)選項(xiàng)可以防止重新使用 AUTO_INCREMENT 值。請注意,額外的 WHERE 子句包含自歸檔或清除作業(yè)開始時(shí)自增列的最大值。如果在 pt-archiver 運(yùn)行時(shí)插入新行,pt-archiver 將看不到它們。
好吧,現(xiàn)在我們知道了為什么沒有刪除干凈的“原因”,但為什么呢?AUTO_INCREMENT 的安全問題是什么?
AUTO_INCREMENT 計(jì)數(shù)器存儲在內(nèi)存中,當(dāng) MySQL 8.0之前的版本 重新啟動(崩潰或其他)時(shí),計(jì)數(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é)果告訴我們: 這里的問題實(shí)際上并不在于 pt-archiver,而在于參數(shù)選項(xiàng)。在處理 AUTO_INCREMENT 列時(shí)使用 pt-archiver 時(shí),了解使用 –no-safe-auto-increment 選項(xiàng)很重要。
讓我們用我們的實(shí)驗(yàn)室數(shù)據(jù)來驗(yàn)證它。
#?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)
使用 –no-delete 選項(xiàng)的復(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)
通過上面的代碼和實(shí)際測試,我們知道了 pt-archiver 的 -[no]safe-auto-increment 選項(xiàng)的原理和作用 。在我們得出一切都很好的結(jié)論之前,讓我們多考慮一下選項(xiàng)本身存在的意義。
默認(rèn)情況下,–no-delete 操作應(yīng)包含 –no-safe-auto-increment 選項(xiàng)。目前,safe-auto-increment 是默認(rèn)行為。當(dāng)我們使用 pt-archiver 的 --no-delete 選項(xiàng)時(shí),沒有刪除操作。這意味著 safe-auto-increment 不應(yīng)成為關(guān)注的原因。
對于 MySQL 8.0,不需要 safe-auto-increment 選項(xiàng)。因?yàn)?MySQL 8.0 開始,自增的值是持久化的,并且在實(shí)例重新啟動或崩潰后自增的最大值不變。參考:MySQL 工作日志 https://dev.mysql.com/worklog/task/?id=6204
而且由于 MySQL 8.0 auto-increment 是通過重做日志持久化的,這使得它們成為pt-archiver 不關(guān)心的一個(gè)原因。因此,我們根本不需要 safe-auto-increment 選項(xiàng)。
結(jié)論
pt-archiver 是歸檔 MySQL 數(shù)據(jù)的好工具,重要的是要了解所有選項(xiàng)以完全控制我們想要使用它實(shí)現(xiàn)的目標(biāo)。
以后需要根據(jù)自增id進(jìn)行歸檔的場景,pt-archiver 默認(rèn)最大的id不會進(jìn)行歸檔,需要添加參數(shù):--no-safe-auto-increment 才能對最大id進(jìn)行處理。
到此這篇關(guān)于pt-archiver和自增主鍵的文章就介紹到這了,更多相關(guān)pt-archiver和自增主鍵內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql decimal數(shù)據(jù)類型轉(zhuǎn)換的實(shí)現(xiàn)
這篇文章主要介紹了mysql decimal數(shù)據(jù)類型轉(zhuǎn)換的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02如何解決MySQL5升級為MySQL8遇到的問題my.ini
這篇文章主要介紹了如何解決MySQL5升級為MySQL8遇到的問題my.ini,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12mysql中g(shù)eneral_log日志知識點(diǎn)介紹
這篇文章主要介紹了mysql中g(shù)eneral_log日志知識點(diǎn)的介紹以及其他相關(guān)內(nèi)容,以后興趣的朋友們學(xué)習(xí)下。2019-08-08- mysql從4.1版開始支持子查詢功能,在此版本前,可以用join寫連表查詢來進(jìn)行替代,但不推薦這么寫,相當(dāng)?shù)穆闊?/div> 2014-04-04
MySQL函數(shù)date_format()日期格式轉(zhuǎn)換的實(shí)現(xiàn)
本文主要介紹了MySQL函數(shù)date_format()日期格式轉(zhuǎn)換的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08mysql中union和union?all的使用及注意事項(xiàng)
這篇文章主要給大家介紹了關(guān)于mysql中union和union?all的使用及注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-08-08mysql創(chuàng)建表添加字段注釋的實(shí)現(xiàn)方法
這篇文章主要介紹了mysql創(chuàng)建表添加字段注釋的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03利用Prometheus與Grafana對Mysql服務(wù)器的性能監(jiān)控詳解
Prometheus是源于 Google Borgmon的一個(gè)開源監(jiān)控系統(tǒng),用 Golang開發(fā)。被很多人稱為下一代監(jiān)控系統(tǒng)。Grafana是一個(gè)開源的圖表可視化系統(tǒng),簡單說圖表配置比較方便、生成的圖表比較漂亮。下面就介紹了利用Prometheus與Grafana對Mysql服務(wù)器性能監(jiān)控的方法。2017-03-03最新評論