使用pt-online-schema-change在線修改大表

使用場景

在線對MYSQL數據庫的維護中,我們經常總會涉及到對數據表進行DDL操作(修改增加字段,索引)的情況,而這操作會對錶進行鎖表操作,在修改一些小表影響很小,而修改大表時,往往影響業務的正常運轉,如表數據量上百萬,上千萬時候

pt-online-schema-change介紹

工作流程原理

1、首先檢查表上各個參數,操作的表必須有主鍵或則唯一索引,並且不能有觸發器,否則報錯。
如果存在外鍵,根據alter-foreign-keys-method參數的值,檢測外鍵相關的表,做相應設置的處理。沒有使用 --alter-foreign-keys-method指定特定的值,該工具拒絕執行
2、創建一個和源表表結構一樣的臨時表,表名一般爲_tablename_new
3、執行alter修改臨時表你所指定的操作
4、在原表上創建3個DELETE/UPDATE/INSERT對應的觸發器(用於copy 數據的過程中,在原表中要執行的語句也在新表中執行)
5、從原表拷貝數據到臨時表,拷貝過程中在原表進行的寫操作都會更新到新建的臨時表
6、如果有外鍵,則修改外鍵相關的子表,根據修改後的數據,修改外鍵關聯的子表。
7、rename源數據表爲old表,把新表rename爲源表名,並將old表刪除。
8、刪除觸發器。

這裏刪除索引的執行SQL日誌

執行語句:
pt-online-schema-change --user=root --password=12345678 --socket=/data/mysql/data/mysql3306.sock D=percona_schema,t=mysql_slow_query_review_history --alter=‘drop key idx_hostname_max_ts_min’ --execute --print --max-lag=10 --check-interval=2 --recursion-method=“hosts” --alter-foreign-keys-method=rebuild_constraints --set-vars innodb_lock_wait_timeout=30000

日誌如下:

No slaves found.  See --recursion-method if host mysql1 has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
  analyze_table, 10, 1
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
No foreign keys reference `percona_schema`.`mysql_slow_query_review_history`; ignoring --alter-foreign-keys-method.
Altering `percona_schema`.`mysql_slow_query_review_history`...
Creating new table...
CREATE TABLE `percona_schema`.`_mysql_slow_query_review_history_new` (
  `host_max` varchar(64) DEFAULT NULL,
  `user_max` varchar(64) DEFAULT NULL,
  `db_max` varchar(64) DEFAULT NULL,
  `checksum` char(32) NOT NULL,
  `sample` text NOT NULL,
  `ts_min` datetime NOT NULL,
  `ts_max` datetime NOT NULL,
  `ts_cnt` float DEFAULT NULL,
  `Query_time_sum` float DEFAULT NULL,
  `Query_time_min` float DEFAULT NULL,
  `Query_time_max` float DEFAULT NULL,
  `Query_time_pct_95` float DEFAULT NULL,
  `Query_time_stddev` float DEFAULT NULL,
  `Query_time_median` float DEFAULT NULL,
  `Lock_time_sum` float DEFAULT NULL,
  `Lock_time_min` float DEFAULT NULL,
  `Lock_time_max` float DEFAULT NULL,
  `Lock_time_pct_95` float DEFAULT NULL,
  `Lock_time_stddev` float DEFAULT NULL,
  `Lock_time_median` float DEFAULT NULL,
  `Rows_sent_sum` float DEFAULT NULL,
  `Rows_sent_min` float DEFAULT NULL,
  `Rows_sent_max` float DEFAULT NULL,
  `Rows_sent_pct_95` float DEFAULT NULL,
  `Rows_sent_stddev` float DEFAULT NULL,
  `Rows_sent_median` float DEFAULT NULL,
  `Rows_examined_sum` float DEFAULT NULL,
  `Rows_examined_min` float DEFAULT NULL,
  `Rows_examined_max` float DEFAULT NULL,
  `Rows_examined_pct_95` float DEFAULT NULL,
  `Rows_examined_stddev` float DEFAULT NULL,
  `Rows_examined_median` float DEFAULT NULL,
  `Rows_affected_sum` float DEFAULT NULL,
  `Rows_affected_min` float DEFAULT NULL,
  `Rows_affected_max` float DEFAULT NULL,
  `Rows_affected_pct_95` float DEFAULT NULL,
  `Rows_affected_stddev` float DEFAULT NULL,
  `Rows_affected_median` float DEFAULT NULL,
  `Rows_read_sum` float DEFAULT NULL,
  `Rows_read_min` float DEFAULT NULL,
  `Rows_read_max` float DEFAULT NULL,
  `Rows_read_pct_95` float DEFAULT NULL,
  `Rows_read_stddev` float DEFAULT NULL,
  `Rows_read_median` float DEFAULT NULL,
  `Merge_passes_sum` float DEFAULT NULL,
  `Merge_passes_min` float DEFAULT NULL,
  `Merge_passes_max` float DEFAULT NULL,
  `Merge_passes_pct_95` float DEFAULT NULL,
  `Merge_passes_stddev` float DEFAULT NULL,
  `Merge_passes_median` float DEFAULT NULL,
  `InnoDB_IO_r_ops_min` float DEFAULT NULL,
  `InnoDB_IO_r_ops_max` float DEFAULT NULL,
  `InnoDB_IO_r_ops_pct_95` float DEFAULT NULL,
  `InnoDB_IO_r_ops_stddev` float DEFAULT NULL,
  `InnoDB_IO_r_ops_median` float DEFAULT NULL,
  `InnoDB_IO_r_bytes_min` float DEFAULT NULL,
  `InnoDB_IO_r_bytes_max` float DEFAULT NULL,
  `InnoDB_IO_r_bytes_pct_95` float DEFAULT NULL,
  `InnoDB_IO_r_bytes_stddev` float DEFAULT NULL,
  `InnoDB_IO_r_bytes_median` float DEFAULT NULL,
  `InnoDB_IO_r_wait_min` float DEFAULT NULL,
  `InnoDB_IO_r_wait_max` float DEFAULT NULL,
  `InnoDB_IO_r_wait_pct_95` float DEFAULT NULL,
  `InnoDB_IO_r_wait_stddev` float DEFAULT NULL,
  `InnoDB_IO_r_wait_median` float DEFAULT NULL,
  `InnoDB_rec_lock_wait_min` float DEFAULT NULL,
  `InnoDB_rec_lock_wait_max` float DEFAULT NULL,
  `InnoDB_rec_lock_wait_pct_95` float DEFAULT NULL,
  `InnoDB_rec_lock_wait_stddev` float DEFAULT NULL,
  `InnoDB_rec_lock_wait_median` float DEFAULT NULL,
  `InnoDB_queue_wait_min` float DEFAULT NULL,
  `InnoDB_queue_wait_max` float DEFAULT NULL,
  `InnoDB_queue_wait_pct_95` float DEFAULT NULL,
  `InnoDB_queue_wait_stddev` float DEFAULT NULL,
  `InnoDB_queue_wait_median` float DEFAULT NULL,
  `InnoDB_pages_distinct_min` float DEFAULT NULL,
  `InnoDB_pages_distinct_max` float DEFAULT NULL,
  `InnoDB_pages_distinct_pct_95` float DEFAULT NULL,
  `InnoDB_pages_distinct_stddev` float DEFAULT NULL,
  `InnoDB_pages_distinct_median` float DEFAULT NULL,
  `QC_Hit_cnt` float DEFAULT NULL,
  `QC_Hit_sum` float DEFAULT NULL,
  `Full_scan_cnt` float DEFAULT NULL,
  `Full_scan_sum` float DEFAULT NULL,
  `Full_join_cnt` float DEFAULT NULL,
  `Full_join_sum` float DEFAULT NULL,
  `Tmp_table_cnt` float DEFAULT NULL,
  `Tmp_table_sum` float DEFAULT NULL,
  `Tmp_table_on_disk_cnt` float DEFAULT NULL,
  `Tmp_table_on_disk_sum` float DEFAULT NULL,
  `Filesort_cnt` float DEFAULT NULL,
  `Filesort_sum` float DEFAULT NULL,
  `Filesort_on_disk_cnt` float DEFAULT NULL,
  `Filesort_on_disk_sum` float DEFAULT NULL,
  `Bytes_sum` float DEFAULT NULL,
  `Bytes_min` float DEFAULT NULL,
  `Bytes_max` float DEFAULT NULL,
  `Bytes_pct_95` float DEFAULT NULL,
  `Bytes_stddev` float DEFAULT NULL,
  `Bytes_median` float DEFAULT NULL,
  PRIMARY KEY (`checksum`,`ts_min`,`ts_max`),
  KEY `idx_hostname_max_ts_min` (`host_max`,`ts_min`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
Created new table percona_schema._mysql_slow_query_review_history_new OK.
Altering new table...
ALTER TABLE `percona_schema`.`_mysql_slow_query_review_history_new` drop key idx_hostname_max_ts_min
Altered `percona_schema`.`_mysql_slow_query_review_history_new` OK.
2020-06-04T17:22:33 Creating triggers...
2020-06-04T17:22:33 Created triggers OK.
2020-06-04T17:22:33 Copying approximately 12 rows...
INSERT LOW_PRIORITY IGNORE INTO `percona_schema`.`_mysql_slow_query_review_history_new` (`host_max`, `user_max`, `db_max`, `checksum`, `sample`, `ts_min`, `ts_max`, `ts_cnt`, `query_time_sum`, `query_time_min`, `query_time_max`, `query_time_pct_95`, `query_time_stddev`, `query_time_median`, `lock_time_sum`, `lock_time_min`, `lock_time_max`, `lock_time_pct_95`, `lock_time_stddev`, `lock_time_median`, `rows_sent_sum`, `rows_sent_min`, `rows_sent_max`, `rows_sent_pct_95`, `rows_sent_stddev`, `rows_sent_median`, `rows_examined_sum`, `rows_examined_min`, `rows_examined_max`, `rows_examined_pct_95`, `rows_examined_stddev`, `rows_examined_median`, `rows_affected_sum`, `rows_affected_min`, `rows_affected_max`, `rows_affected_pct_95`, `rows_affected_stddev`, `rows_affected_median`, `rows_read_sum`, `rows_read_min`, `rows_read_max`, `rows_read_pct_95`, `rows_read_stddev`, `rows_read_median`, `merge_passes_sum`, `merge_passes_min`, `merge_passes_max`, `merge_passes_pct_95`, `merge_passes_stddev`, `merge_passes_median`, `innodb_io_r_ops_min`, `innodb_io_r_ops_max`, `innodb_io_r_ops_pct_95`, `innodb_io_r_ops_stddev`, `innodb_io_r_ops_median`, `innodb_io_r_bytes_min`, `innodb_io_r_bytes_max`, `innodb_io_r_bytes_pct_95`, `innodb_io_r_bytes_stddev`, `innodb_io_r_bytes_median`, `innodb_io_r_wait_min`, `innodb_io_r_wait_max`, `innodb_io_r_wait_pct_95`, `innodb_io_r_wait_stddev`, `innodb_io_r_wait_median`, `innodb_rec_lock_wait_min`, `innodb_rec_lock_wait_max`, `innodb_rec_lock_wait_pct_95`, `innodb_rec_lock_wait_stddev`, `innodb_rec_lock_wait_median`, `innodb_queue_wait_min`, `innodb_queue_wait_max`, `innodb_queue_wait_pct_95`, `innodb_queue_wait_stddev`, `innodb_queue_wait_median`, `innodb_pages_distinct_min`, `innodb_pages_distinct_max`, `innodb_pages_distinct_pct_95`, `innodb_pages_distinct_stddev`, `innodb_pages_distinct_median`, `qc_hit_cnt`, `qc_hit_sum`, `full_scan_cnt`, `full_scan_sum`, `full_join_cnt`, `full_join_sum`, `tmp_table_cnt`, `tmp_table_sum`, `tmp_table_on_disk_cnt`, `tmp_table_on_disk_sum`, `filesort_cnt`, `filesort_sum`, `filesort_on_disk_cnt`, `filesort_on_disk_sum`, `bytes_sum`, `bytes_min`, `bytes_max`, `bytes_pct_95`, `bytes_stddev`, `bytes_median`) SELECT `host_max`, `user_max`, `db_max`, `checksum`, `sample`, `ts_min`, `ts_max`, `ts_cnt`, `query_time_sum`, `query_time_min`, `query_time_max`, `query_time_pct_95`, `query_time_stddev`, `query_time_median`, `lock_time_sum`, `lock_time_min`, `lock_time_max`, `lock_time_pct_95`, `lock_time_stddev`, `lock_time_median`, `rows_sent_sum`, `rows_sent_min`, `rows_sent_max`, `rows_sent_pct_95`, `rows_sent_stddev`, `rows_sent_median`, `rows_examined_sum`, `rows_examined_min`, `rows_examined_max`, `rows_examined_pct_95`, `rows_examined_stddev`, `rows_examined_median`, `rows_affected_sum`, `rows_affected_min`, `rows_affected_max`, `rows_affected_pct_95`, `rows_affected_stddev`, `rows_affected_median`, `rows_read_sum`, `rows_read_min`, `rows_read_max`, `rows_read_pct_95`, `rows_read_stddev`, `rows_read_median`, `merge_passes_sum`, `merge_passes_min`, `merge_passes_max`, `merge_passes_pct_95`, `merge_passes_stddev`, `merge_passes_median`, `innodb_io_r_ops_min`, `innodb_io_r_ops_max`, `innodb_io_r_ops_pct_95`, `innodb_io_r_ops_stddev`, `innodb_io_r_ops_median`, `innodb_io_r_bytes_min`, `innodb_io_r_bytes_max`, `innodb_io_r_bytes_pct_95`, `innodb_io_r_bytes_stddev`, `innodb_io_r_bytes_median`, `innodb_io_r_wait_min`, `innodb_io_r_wait_max`, `innodb_io_r_wait_pct_95`, `innodb_io_r_wait_stddev`, `innodb_io_r_wait_median`, `innodb_rec_lock_wait_min`, `innodb_rec_lock_wait_max`, `innodb_rec_lock_wait_pct_95`, `innodb_rec_lock_wait_stddev`, `innodb_rec_lock_wait_median`, `innodb_queue_wait_min`, `innodb_queue_wait_max`, `innodb_queue_wait_pct_95`, `innodb_queue_wait_stddev`, `innodb_queue_wait_median`, `innodb_pages_distinct_min`, `innodb_pages_distinct_max`, `innodb_pages_distinct_pct_95`, `innodb_pages_distinct_stddev`, `innodb_pages_distinct_median`, `qc_hit_cnt`, `qc_hit_sum`, `full_scan_cnt`, `full_scan_sum`, `full_join_cnt`, `full_join_sum`, `tmp_table_cnt`, `tmp_table_sum`, `tmp_table_on_disk_cnt`, `tmp_table_on_disk_sum`, `filesort_cnt`, `filesort_sum`, `filesort_on_disk_cnt`, `filesort_on_disk_sum`, `bytes_sum`, `bytes_min`, `bytes_max`, `bytes_pct_95`, `bytes_stddev`, `bytes_median` FROM `percona_schema`.`mysql_slow_query_review_history` LOCK IN SHARE MODE /*pt-online-schema-change 30969 copy table*/
2020-06-04T17:22:33 Copied rows OK.
2020-06-04T17:22:33 Analyzing new table...
2020-06-04T17:22:34 Swapping tables...
RENAME TABLE `percona_schema`.`mysql_slow_query_review_history` TO `percona_schema`.`_mysql_slow_query_review_history_old`, `percona_schema`.`_mysql_slow_query_review_history_new` TO `percona_schema`.`mysql_slow_query_review_history`
2020-06-04T17:22:34 Swapped original and new tables OK.
2020-06-04T17:22:34 Dropping old table...
DROP TABLE IF EXISTS `percona_schema`.`_mysql_slow_query_review_history_old`
2020-06-04T17:22:34 Dropped old table `percona_schema`.`_mysql_slow_query_review_history_old` OK.
2020-06-04T17:22:34 Dropping triggers...
DROP TRIGGER IF EXISTS `percona_schema`.`pt_osc_percona_schema_mysql_slow_query_review_history_del`
DROP TRIGGER IF EXISTS `percona_schema`.`pt_osc_percona_schema_mysql_slow_query_review_history_upd`
DROP TRIGGER IF EXISTS `percona_schema`.`pt_osc_percona_schema_mysql_slow_query_review_history_ins`
2020-06-04T17:22:34 Dropped triggers OK.
Successfully altered `percona_schema`.`mysql_slow_query_review_history`.

參數介紹

這裏介紹下上面我使用的幾個參數介紹,其他參數你們可以參考下percona官網

–execute 這個參數的作用必須指定此選項才能更改表(默認情況下未啓用),該工具支持多種其他措施來防止不必要的負載或其他問題,包括自動檢測副本,連接到副本以及安全檢查。注意:如果不加這個參數,該工具將僅執行一些安全檢查並退出。

–max-lag 這個參數默認值是1s,表示暫停數據複製,直到所有副本的延遲小於此值。在每次數據複製查詢(每個數據塊)之後,該工具使用Seconds_Behind_Master查看它連接到的所有副本的複製延遲。如果任何副本的滯後時間超過此選項的值,則該工具將休眠–check-interval幾秒鐘,然後再次檢查所有副本。如果指定–check slave lag,則該工具只檢查該服務器的延遲,而不是所有服務器。如果要精確控制工具監視的服務器,請使用DSN值–recursion-method。

–check-interval 這個參數默認值是1s表示–max-lag兩次檢查之間的睡眠時間

–recursion-method 這個參數默認值是processlist,hosts;下面是遞歸查詢方法。
在這裏插入圖片描述
processlist方法是默認方法,因爲SHOW SLAVE HOSTS不可靠。但是,如果服務器使用非標準端口(不是3306),hosts方法可以更好地工作。該工具通常會做正確的事情並查找所有副本,但您可以給出一個首選方法,它將首先使用。hosts方法要求使用report-host、report-port等配置副本。

–alter-foreign-keys-method 這個參數是用來當表上有外鍵時候,如何修改外鍵,以便它們引用新表。引用要更改的表的外鍵必須經過特殊處理,以確保它們繼續引用正確的表。當該工具重命名原始表以讓新表取代它時,外鍵將“跟隨”重命名的表,並且必須更改外鍵以引用新表。該工具自動確定使用哪種方法最好。如果可能,該工具會使用 rebuild_constraints,否則,將使用drop_swap。

–set-vars 這個參數用來設置mysql的變量值,默認情況下一般設置wait_timeout、innodb_lock_wait_timeout 、lock_wait_timeout 這個幾個,同時多個寫可以使用逗號隔開。

–check-slave-lag 這個參數在有主從環境使用會檢查主從延遲。參數說明是 暫停數據複製,直到此副本的滯後時間小於–max-lag。該值是一個DSN從連接選項繼承屬性(–port,–user等)。此選項將覆蓋在所有連接的副本上查找並持續監視複製滯後的正常行爲。


考慮從庫延遲情況 ,要注意這幾個選項的設置
–max-lag
–check-interval
–recursion-method
–check-slave-lag

下面寫下有幾個參數比較重要的,

–charset=utf8 連接到MySQL後運行SET NAMES UTF8 ,默認使用庫字符集

–dry-run 這個參數不建立觸發器,不拷貝數據,也不會替換原表。只是創建和更改新表。

–critical-load 默認值:Threads_running = 50
每次chunk操作前後,會根據show global status統計指定的狀態量的變化,默認是統計Thread_running。目的是爲了安全,防止原始表上的觸發器引起負載過高。這也是爲了防止在線DDL對線上的影響。超過設置的閥值,就會終止操作,在線DDL就會中斷。提示的異常如上報錯信息。該選項接受以逗號分隔的MySQL狀態變量和閾值列表。可選變量=MAX_VALUE(或:MAX_VALUE)可以跟在每個變量之後。如果未給出,該工具將通過在啓動時檢查當前值並將其加倍來確定閾值。

–max-load 默認值:Threads_running = 25
在每次chunk操作後,檢查show global status狀態值是否高於指定的閥值。該參數接受一個mysql status狀態變量以及一個閥值,如果沒有給定閥值,則定義一個閥值爲爲高於當前值的20%。注意這個參數不會像–critical-load終止操作,而只是暫停操作。當status值低於閥值時,則繼續往下操作。該選項接受以逗號分隔的MySQL狀態變量列表。可選變量=MAX_VALUE(或:MAX_VALUE)可以跟在每個變量之後。如果未給出,該工具將通過檢查當前值並將其增加20%來確定閾值。

例如,如果您希望該工具在Threads_connected變得太高時暫停,則可以指定“ Threads_connected”,該工具將在開始工作時檢查當前值並將該值加20%。如果當前值爲100,則當Threads_connected超過120時該工具將暫停,並在再次低於120時恢復工作。如果要指定一個顯式閾值,例如110,則可以使用“ Threads_connected:110”或“ Threads_connected = 110”。

此選項的目的是防止工具向服務器添加過多的負載。如果數據複製查詢是侵入性的,或者它們導致鎖定等待,則服務器上的其他查詢將傾向於阻塞和排隊。這通常會導致Threads_running增加,並且該工具可以通過在每個查詢完成後立即運行SHOW GLOBAL STATUS來檢測到該情況。如果爲此變量指定閾值,則可以指示該工具等待,直到查詢再次正常運行。但是,這不會阻止排隊。它只會使服務器有機會從隊列中恢復。如果您發現排隊,則最好減少組塊時間。

–check-replication-filters 檢查複製中是否設置了過濾條件,如果設置了,程序將退出
–nocheck-replication-filters 不檢查複製中是否設置了過濾條件

舉例

刪除索引
pt-online-schema-change --user=root–password=12345678 --socket=/data/mysql/data/mysql3306.sock D=percona_schema,t=mysql_slow_query_review_history --alter=‘drop key idx_hostname_max_ts_min’ --execute --print --max-lag=10 --check-interval=2 --recursion-method=“hosts” --alter-foreign-keys-method=rebuild_constraints --set-vars innodb_lock_wait_timeout=30000

添加索引
pt-online-schema-change --user=system --password=12345678 --socket=/data/mysql/data/mysql3306.sock D=percona_schema,t=mysql_slow_query_review_history --alter=‘add key idx_hostname_max_ts_min(host_max,ts_min)’ --execute --print --max-lag=10 --check-interval=2 --check-slave-lag=h=192.168.11.12,u=system,p=12345678,P=3306 --alter-foreign-keys-method=rebuild_constraints --set-vars innodb_lock_wait_timeout=30000

添加字段
pt-online-schema-change --user=root --password=12345678 --socket=/data/mysql/data/mysql3306.sock D=percona_schema,t=mysql_slow_query_review_history --alter=‘add column id bigint(20) AUTO_INCREMENT’ --execute --print --max-load=Threads_connected:800 --critical-load=Threads_running=700 --charset=utf8 --nocheck-replication-filters --max-lag=10 --check-interval=2 --recursion-method=“hosts” --alter-foreign-keys-method=rebuild_constraints --set-vars innodb_lock_wait_timeout=30000

刪除字段
pt-online-schema-change --user=root–password=12345678 --socket=/data/mysql/data/mysql3306.sock D=percona_schema,t=mysql_slow_query_review_history --alter='drop column id ’ --execute --print --max-load=Threads_connected:800 --critical-load=Threads_running=700 --charset=utf8 --nocheck-replication-filters --alter-foreign-keys-method=rebuild_constraints --set-vars innodb_lock_wait_timeout=30000

修改字段
pt-online-schema-change --user=root–password=12345678 --socket=/data/mysql/data/mysql3306.sock D=percona_schema,t=mysql_slow_query_review_history --alter=‘modify column id bigint(30) AUTO_INCREMENT’’ --execute --print --max-load=Threads_connected:800 --critical-load=Threads_running=700 --charset=utf8 --nocheck-replication-filters --alter-foreign-keys-method=rebuild_constraints --set-vars innodb_lock_wait_timeout=30000

pt-online-schema-change 好處

  1. 降低主從延時的風險
  2. 可以限速、限資源(CPU,線程數),避免操作時MySQL負載過高導致業務影響

最後建議大家最好還是在業務低峯期做,將影響降到最低。

(以上如有錯誤,歡迎指出,謝謝!)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章