1. 程式人生 > >案例 - percona-online-schema-change各種坑

案例 - percona-online-schema-change各種坑

percona

線上環境復制使用ROW模式,對於上億的表,使用pt online schema change 在把數據從舊表拷貝到臨時表這步操作,會產生大量的binlog,這會導致主從延遲


在pt工具包2.1之前,pt-online-schema-change是不會打印binlog的,如果要在主從上加索引,需要分別在主庫執行一次,在從庫執行一次


它提供了一個--log-bin產生,並且默認是關閉的

  • --bin-log

  • Allow binary logging (SET SQL_LOG_BIN=1). By default binary logging is turned off because in most cases the --tmp-table

    does not need to be replicated.


而在pt工具2.2版本以後,會默認打binlog,好處是在不用分別在各個節點執行一次改表操作,只需要在主庫執行一次改表,就會通過binlog讓下面的從庫的表都被修改


pt工具3.0版本,有一個 --set-vars=‘sql_log_bin=0‘ 參數能取代 --bin-log=0 效果


剛好有一個1.5億表加索引的需求,就使用如下命令,1.5億生成的binlog預計會有20G,為了不產生binlog,準備在每個點執行一次,先在主庫執行

pt-online-schema-change                             
--host=主機                                   
--port=端口號                                        
--user=節點號                                        
--database=數據庫名                                 
t=t_room_impeach                                   
--alter="ADD INDEX idx_psr(A,B,C)"  
--set-vars=‘sql_log_bin=0‘                           
--execute

這條語句一下去,主庫下面的4個從庫同步都中斷了,show slave status報錯

Last_SQL_Errno: 1146
Last_SQL_Error: Error executing row event: ‘Table ‘live_oss._t_room_impeach_new‘ doesn‘t exist‘

報錯_t_room_impeach_new表存在,為什麽這張表要存在呢?


posc工具的原理是,先創建一個臨時表,表名是 _原來的表名_new,這張臨時表是已經加入了你想要的索引,不停把舊表的數據拷貝到這張臨時表,新插入,修改,刪除的舊表的數據,都會根據觸發器,同樣新插入,修改,刪除到臨時表,等拷貝數據,舊表和臨時表就是一模一樣了,這個時候把臨時表rename成為就表的名字,而實際的舊表就會被drop掉,在線完成


當主庫執行命令是會顯示創建臨時表,創建觸發器

Creating new table...
Created new table live_oss._t_room_impeach_new OK.
Altering new table...
Altered `live_oss`.`__t_room_impeach_new` OK.
2017-08-02T16:38:48 Creating triggers...
2017-08-02T16:38:48 Created triggers OK.
2017-08-02T16:38:48 Copying approximately 141559863 rows...

因為 --set-vars=‘sql_log_bin=0‘的原因,創建表的DDL語句,無法通過binlog在從庫建表,所以從庫是表不存在的,問題是從庫不需要存在臨時表啊,因為只操作主庫一個點就足夠了


這個是posc第一個坑,主庫因觸發器觸發器產生的數據,會產生binlog,從而同步到從庫,當從庫要執行這些數據時,發現表不存在,導致同步中斷


這時解決方法是在從庫,去建立同樣一張臨時表 _xxxx_new,好讓觸發器的數據,能夠順利插入到這張表,當建了以後可以看到從庫的臨時表有數據了,再次驗證sql_log_bin=0沒有效果

explain select count(*) from  __t_room_impeach_new;
+----+-------------+----------------------+-------+---------------+------+---------+------+----------+-------------+
| id | select_type | table                | type  | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+----------------------+-------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | __t_room_impeach_new | index | NULL          | uid  | 4       | NULL | 176| Using index |
+----+-------------+----------------------+-------+---------------+------+---------+------+----------+-------------+

幾個從庫都有176條數據,再看看主庫的臨時表,有差不多1億數據,因為除了觸發器還有來自舊表的


explain select count(*) from  __t_room_impeach_new;
+----+-------------+----------------------+-------+---------------+------+---------+------+----------+-------------+
| id | select_type | table                | type  | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+----------------------+-------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | __t_room_impeach_new | index | NULL          | uid  | 4       | NULL | 10527757 | Using index |
+----+-------------+----------------------+-------+---------------+------+---------+------+----------+-------------


當時有個擔心

主庫臨時表 __t_room_impeach_new 數據 = 觸發器產生數據 + 舊表產生數據

從庫臨時表 __t_room_impeach_new數據 = 觸發器產生的數據


如果所有點執行最後一步操作 rename 臨時表__t_room_impeach_new to t_room_impeach 正式表,豈不是主從數據不一致,從庫少了很多數據?


不過按道理這種情況不會發生,因為--set-vars=‘sql_log_bin=0‘會把rename這個DDL語句,像create table一樣給阻隔掉,不會導致從庫改表成功


為了不冒險,打算重新執行一次,這次加入2個參數,

--no-drop-old-table 即使執行完了命令,也不要drop表,讓我確認舊表新表是一致的再手動drop

--no-drop-triggers 觸發器也保留


執行命令之前,先把臨時表,觸發器都手動刪除,正如提示說的

Not dropping triggers because the tool was interrupted.  To drop the triggers, execute:
DROP TRIGGER IF EXISTS `live_oss`.`pt_osc_live_oss_t_room_impeach_del`;
DROP TRIGGER IF EXISTS `live_oss`.`pt_osc_live_oss_t_room_impeach_upd`;
DROP TRIGGER IF EXISTS `live_oss`.`pt_osc_live_oss_t_room_impeach_ins`;
Not dropping the new table `live_oss`.`_t_room_impeach_new` because the tool was interrupted.  To drop the new table, execute:
DROP TABLE IF EXISTS `live_oss`.`_t_room_impeach_new`;
`live_oss`.`t_room_impeach` was not altered.


另外還有在從庫先把臨時表建立起來,這次執行到一半的時候,4個從庫又報錯,同步中斷了

Last_SQL_Errno: 1032

Last_SQL_Error: Could not execute Update_rows event on table live_oss._t_room_impeach_new; Can‘t find record in ‘_t_room_impeach_new‘, Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event‘s master log mysql-bin.056637, end_log_pos 41767716


這次的報錯是update語句失敗了,row模式的update語句是 set 新值 where 舊值 ,如果在從庫的臨時表上,找不到舊值,就會報這樣的錯


同樣因為--set-vars=‘sql_log_bin=0‘,導致從庫臨時表,比主庫臨時表少很多數據,所以很可能一條update語句下來,就會因為找不到數據而中斷


總結-在myqsl主從復制下,主庫不要用這種模式,因為無法阻止觸發器帶來binlog,僅僅在從庫執行時可以的


另外如果使用--no-drop-old-table和--no-drop-triggers參數,最終結果是命令到99%一直卡住


Copying `live_oss`.`t_room_impeach`: 99% 01:01 remain

Copying `live_oss`.`t_room_impeach`: 99% 00:47 remain

Copying `live_oss`.`t_room_impeach`: 99% 00:35 remain

Copying `live_oss`.`t_room_impeach`: 99% 00:21 remain

Copying `live_oss`.`t_room_impeach`: 99% 00:09 remain


還有一個坑就是業務時不時會反饋一吧報錯,一張臨時表不存在,但這張臨時表應該是對業務透明才對的,真心奇怪


XXXX 說: (17:19:49)
Base table or view not found: 1146 Table ‘live_oss.__t_room_impeach_new‘ doesn‘t exist

報了這個錯誤的
xxxx 說: (17:21:00)
怎麽表名變為t_room_impeach_new了
XXXXXX 說: (17:25:27)
原來的表還存在把
XXXXX說: (17:25:47)
現在又恢復了













本文出自 “數據庫運維” 博客,請務必保留此出處http://dadaman.blog.51cto.com/11373912/1953177

案例 - percona-online-schema-change各種坑