億級大表在線不鎖表變動字段與索引

背景

你們在平常工做中,每每須要對數據庫的表結構作變動,通常涉及到增刪字段,修改字段屬性等ALTER的操做。然而,在大表場景下,特別是千萬級、億級的大表,若是處理不當。這些操做每每會引起鎖表的巨大隱患,特別是在生產環境中,一旦在變動表結構過程當中,出現了長時間鎖表,會致使用戶產生的數據長時間沒法正常變動到表中,進而致使服務功能異常,結果將是災難性的。linux

通常執行這種Alter類型的變動,咱們可能有如下的想法:sql

一、停服,在停服期間作表結構的變動,天然就能夠防止對用戶產生影響。可是,不少場景是不容許停服的。而且若是表的數據量達到上億,那麼須要停服時間可能須要十幾個小時,甚至更長,這是極不現實的;數據庫

二、凌晨執行,在用戶較少的時間段內,作變動,儘可能減小對用戶產生影響。可是若是出現鎖表的話,萬一有用戶使用服務,服務將不可用;segmentfault

三、使用換表,可是缺點是複製數據到新表期間,若是用戶在這期間作了update或delete操做,且數據發生在已經複製完成的部分,那麼將沒法感知到這部分數據,致使丟失掉用戶的操做數據,風險太大;安全

四、使用存儲過程,缺點是執行時間會好久,且有可能影響到用戶的DDL操做。由於爲了防止每次循環修改時,鎖住太多數據行,咱們須要控制每次更新數據的行數,粒度不能太大,不然頗有可能會鎖住用戶正在操做的數據行。app

那麼針對以上實際的需求,就沒有一個很好的工具,來解決咱們的痛點嗎?其實在業界中,就有一個比較成熟的工具,針對大表的場景,能夠在線進行Alter變動,且不會出現鎖表的風險。除此以外,它還有其餘的一些優勢,讓咱們開始探索吧。工具

1、pt-osc是什麼

pt-online-schema-change是Percona-toolkit一員,經過改進原生ddl的方式,達到不鎖表在線修改表結構的效果。在Percona的官網中,關於pt-osc工具,也特別提到了ALTER表不會出現鎖表的特性。性能

針對上面談到的避免鎖表、感知用戶更新刪除動做等,ps-osc工具是怎麼解決的呢?測試

pt-osc主要執行步驟以下:ui

一、建立一個跟原表如出一轍的新表,命名方式爲'_正式表名_new';

二、使用alter語句將要變動的內容在新建立的新表上作變動,避免了對原表的alter操做;

三、在原表中建立3個觸發器,分別是insert、update和delete,主要是用於原表在往新表複製數據時,若是用戶有DDL操做,觸發器可以將在這期間出現的DDL操做數據也寫入到新表中,確保新表的數據是最新的,不會丟失掉用戶的新操做數據;

四、按塊拷貝數據到新表,拷貝過程對數據行持有S鎖;

五、重命名,將原表重命名爲老表,命名爲「_正式表名_old」,將新表重命名爲正式表,可經過配置決定執行完成後是否刪除掉老表;

六、刪除3個觸發器;

2、pt-osc的安裝

在linux系統中安裝步驟:

--下載安裝包
wget  http://szxge1-sw.artifactory.cd-cloud-artifact.tools.huawei.com/artifactory/CommonComponent/common/tool/percona-toolkit-3.1.0.tar.gz
--解壓安裝包
tar -zxvf percona-toolkit-3.1.0.tar.gz
--安裝依賴環境
yum install perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker
yum -y install perl-Digest-MD5
cd percona-toolkit-3.1.0
perl Makefile.PL
--編譯
make
make install
yum install mariadb
--安裝Mysql
yum install perl-DBD-MySQL

3、pt-osc的使用

pt-osc工具使用起來很簡單,直接在linux命令行輸入pt-osc格式的命令,便可直接執行。

以Mysql數據庫增長一個名字是MARK的字段爲例:

pt-online-schema-change --user="root" --password="*****" --host="數據庫IP" --port=3306 --alter "ADD COLUMN MARK TINYINT NULL DEFAULT 1 COMMENT 'mark source region is 1';" D=my_test,t=t_test --no-drop-old-table --execute --print --no-check-replication-filters --charset=utf8 --no-check-unique-key-change --max-load="Threads_running=100" --critical-load="Threads_running=300" --recursion-method=none;

在上面的語句中:

一、user和password分別爲數據庫執行變動操做的用戶名、密碼,須要高權限;

二、host爲數據庫的IP地址;

三、port爲數據庫的端口號;

四、alter後面跟上具體的alter語句;

五、D爲database名字;

六、t爲要執行變動的表名;

七、no-drop-old-table就是不要刪除

八、charset,字符集,使用utf8;

九、max-load,在複製數據時,工具會監控數據庫中正在運行的線程數,若是大於配置的Threads_running值,那麼會暫停複製,直到小於該值。以此防止對數據庫形成較大壓力,影響現網業務正常使用;

十、critical-load,默認爲50,在每一個塊以後檢查SHOW GLOBAL STATUS,與max-load不一樣的是,若是負載過高,,直接停止,而不是暫停。可根據本身數據庫狀況斟酌配置閾值;

注意:在--alter後面跟着的變動語句中,列名不能夠加符號,不然會出現報錯。如--alter "ADD COLUMN MARK TINYINT NULL DEFAULT 1 COMMENT 'mark source region is 1';",MARK字段加了符號,就會出現錯誤,COMMENT後面有`符號無影響。

下面是使用pt-osc工具,實際執行一個做業時,打印出來的信息。爲了安全起見,部分日誌信息作了隱藏忽略。

image

[root@ttt ~]#  `pt-online-schema-change --user="root" --password="*****" --host="數據庫IP" --port=3306 --alter "ADD COLUMN MARK TINYINT NULL DEFAULT 1 COMMENT 'mark source region is 1';" D=my_test,t=t_test --no-drop-old-table --execute --print --no-check-replication-filters --charset=utf8 --no-check-unique-key-change --max-load="Threads_running=100" --critical-load="Threads_running=300" --recursion-method=none;`
No slaves found.  See --recursion-method if host EulerOS-BaseTemplate 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
Altering `my_test`.`t_test`...
Creating new table...
CREATE TABLE `my_test`.`_t_test_new` (
      `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '遞增ID',
      .............建表語句數據................
Created new table my_test._t_test_new OK.
Altering new table...
ALTER TABLE `my_test`.`_t_test_new` ADD COLUMN MARK TINYINT NULL DEFAULT 1 COMMENT 'mark source region is 1';
Altered `my_test`.`_t_test_new` OK.
2020-10-14T11:14:48 Creating triggers...
2020-10-14T11:14:48 Created triggers OK.
2020-10-14T11:14:48 Copying approximately 346697 rows...
INSERT LOW_PRIORITY IGNORE INTO `my_test`.`_t_test_new` (`id`, ..建表語句信息.... FROM `my_test`.`_t_test_new` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) AND ((`id` <= ?)) LOCK IN SHARE MODE /*pt-online-schema-change 31340 copy nibble*/
SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `my_test`.`t_test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) ORDER BY `id` LIMIT ?, 2 /*next chunk boundary*/
2020-10-14T11:14:53 Copied rows OK.
2020-10-14T11:14:53 Analyzing new table...
2020-10-14T11:14:53 Swapping tables...
RENAME TABLE `my_test`.`t_test` TO `my_test`.`_t_test_old`, `my_test`.`_t_test_new` TO `my_test`.`t_test`
2020-10-14T11:14:53 Swapped original and new tables OK.
Not dropping old table because --no-drop-old-table was specified.
2020-10-14T11:14:53 Dropping triggers...
DROP TRIGGER IF EXISTS `my_test`.`pt_osc_my_test_t_test_del`
DROP TRIGGER IF EXISTS `my_test`.`pt_osc_my_test_t_test_upd`
DROP TRIGGER IF EXISTS `my_test`.`pt_osc_my_test_t_test_ins`
2020-10-14T11:14:54 Dropped triggers OK.
Successfully altered `my_test`.`t_test`.

4、性能對比

前面介紹了不少pt-osc的優勢,以及良好的特性。那麼實際使用效果到底怎麼樣呢?在測試環境中,專門作了一個測試,讓你們有更加直觀的感覺。

在測試庫中,準備了一張1600萬數據的大表,目標爲對大表添加一個字段,分別使用存儲過程和pt-osc工具,進行測試。

4.1 使用存儲過程

首先使用存儲過程作測試,爲防止鎖表,每次只更新200行。整個變動從開始到完成,須要耗費90分鐘。其實,存儲過程在執行過程當中,若是剛好用戶也在DDL操做存儲過程正在變動的數據行,還有可能會鎖住用戶的數據,致使用戶不能變動成功。

4.2 使用pt-osc工具

pt-osc從開始執行到變動完成,耗時7分鐘左右,速度很是快。在執行的過程當中,測試環境的服務鏈接到該數據庫,並執行多個會操做該表的任務,整個過程當中,任務可以正常執行,未出現異常狀況。

五、結語

ps-osc的上述優勢,在現網環境的不停服等要求下,可以優雅地幫助咱們實施變動,且保證在變動期間,數據庫不會受到鎖表、過載等的影響,進而保證了業務可以正常運轉。

 

相關文章
相關標籤/搜索