【20180402】MySQL關於replication filter和trigger的一些應用

需求描述

1.內網服務器有倆套主從複製環境,一套是基於傳統複製的5.6.26版本,另外一套是基於GTID的5.7.19版本的複製。現在開發的需求是需要將基於傳統複製的上面的倆個表同步到基於基於GTID複製上面去,並且要求同步的倆個表中有一個表的一列的值必須是源的10倍。

root@mysqldb 15:51:  [remix_test]> show create table sbtest1 \G
*************************** 1. row ***************************
       Table: sbtest1
Create Table: CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL,
  `k` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

表結構如上所示,在原來的基礎上column k必須乘以10,即new_k = old_k*10

難點

1.如何將倆個表的數據同步過來。在這裏我們採用了使用主從同步的方案,但是主從同步的方案又帶來了以下幾個問題:

  • 基於傳統複製的主從是根據binlog file 和 binlog position進行復制的,基於GTID的主從是根據GTID進行復制,這倆者創建主從是使用基於傳統複製還是GTID複製。
  • 基於GTID複製的主從的binlog的格式是ROW格式,基於傳統複製的主從的binlog格式是STATEMENT格式,在將數據同步到從傳統複製的master上面同步到基於GTID複製的master上,這個時候基於GTID的主從複製的SLAVE是否能夠正常應用binlog日誌,或者說slave能否應用沒有使用GTID的binlog日誌。
  • 僅僅只是需要同步倆張表,其他的庫表的數據不需要同步過來。所以需要進行復制過濾。

2.如何將同步過來的數據做修改。在這裏我們採取的方案是使用觸發器,但是觸發器本身會消耗資源;因爲觸發器是基於row行的,所以假如我們一次性修改500條數據花費1s中,觸發器觸發修改會觸發500次,加入每行數據修改花費1s,那麼觸發器就會花費500s。

  • 在測試的過程中,我們發現在INSERT插入一行數據之後,在針對這行的數據進行update操作,觸發器無法被應用到,並且show slave status\G會報錯:
    Last_SQL_Error: Error 'Can't update table 'sbtest1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.' on query. Default database: 'remix_test'. Query: 'insert into sbtest1(id,k) values(39039,1),(39040,10)'

解決方案

1.數據同步:

  • 爲了使得基於GTID主從複製的master能夠應用來自於傳統複製的主從的master的binlog日誌,在GTID主從複製的master上面必須修改GTID_MODE,將GTID_MODE更改成ON_PERMISSIVE(本地產生GTID事務,並且接受隱性事務(即非GTID事務)):
    mysql> set global gtid_mode='ON_PERMISSIVE';
    • 至於基於GTID複製的主從的SLAVE同步來自於MASTER的數據,這裏暫時沒有什麼好的方案,或者我們還沒有進行實驗。但是之前我們忘記在本地進行修改GTID_MODE導致在SLAVE上面執行SHOW SLAVE STATUS \G可以看出很明顯的錯誤:ERROR:1236 。這個問題也是我們現在遇到無法解決的問題,因爲哪怕最後基於GTID複製的MASTER可以接受來自於基於傳統複製的MASTER的binlog日誌,但是基於GTID複製的SLAVE卻無法從MASTER上面接受和應用這些日誌。
      Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Cannot replicate anonymous transaction when AUTO_POSITION = 1, at file /home/mysql/data_3307/mysql-bin.000005, position 234.; the first event '' at 4, the last event read from '/home/mysql/data_3307/mysql-bin.000005' at 299, the last byte read from '/home/mysql/data_3307/mysql-bin.000005' at 299.'
  • 至於基於GTID主從複製的SLAVE不能應用來自於MASTER的日誌,在這裏解決方案就是將基於GTID複製轉換成傳統複製。就是在show slave status \G獲取Master_Log_File和Read_Master_Log_Pos信息,然後stop slave,chaneg master to master_log_file=Master_Log_File,master_log_pos=Read_Master_Log_Po,master_auto_position=0;然後在start slave。還有就是最好是在低峯期的時候做一下主從數據一致性檢測或者直接查看GTID的集合信息。
  • 複製過濾:需要注意的如果你僅僅只是寫replication_do_table的話,雖然一般的DDL,DML操作不是這倆個表是不會複製過來的,但是當你創建新庫的時候(create database schema_name;)還是會將新庫的創建命令同步過來的,但也是僅僅同步新庫的創建命令,其他的例如在新庫創建表之類的是不會同步過來的。
    mysql> Change replication filter REPLICATE_DO_DB=(remix_test),REPLICATE_DO_TABLE = (remix_test.sbtest1,remix_test.sbtest2);
  1. 觸發器的創建。
    • 創建下面觸發器的時候雖然是創建成功的,但是但你在基於傳統複製的master做insert插入操作的時候是可以觸發觸發器但是卻無法執行,會拋出上面所說的錯誤。
      mysql> delimiter || ;
      mysql> create trigger tr1 after insert on sbtest1
      -> for each row
      -> begin
      ->     update sbtest1 set k=new.k*10 where id=new.id;
      -> end||
      mysql> delimiter ;
    • 最後還是打算創建一個新表,每次舊錶又什麼操作就在新表上面進行修改。

實際操作:

  1. 基於傳統複製數據的slave上面的備份
    shell> mysqldump --set-gtid-purged=OFF --single-transaction --dump-slave=2 -uroot -p -t remix_test sbtest1 > sbtest1_20180402.sql
    shell> mysqldump --set-gtid-purged=OFF --single-transaction --dump-slave=2 -uroot -p -t remix_test sbtest2 > sbtest2_20180402.sql
  2. 基於GTID複製的MASTER上面的恢復
    shell> mysql -S /var/lib/mysql/mysql_3306.sock -uroot -p remix_test< sbtest1_20180402.sql
    shell> mysql -S /var/lib/mysql/mysql_3306.sock -uroot -p remix_test<sbtest2_20180402.sql
  3. 基於傳統複製的MASTER上面創建複製賬號
    mysql> grant replication slave on 'slave'@'ip_address' identified by 'new_password';
    mysql> flush privileges;
  4. 基於GTID複製的MASTER的gtid_node修改
    mysql> set global gtid_mode='ON_PERMISSIVE';
  5. 基於GTID複製的MASTER上面搭建主從(從基於後面備份的sbtest2_20180402.sql上面獲取binlog file和position信息)
    mysql> change master  ......
  6. 基於GTID複製的MASTER上面做複製過濾
    mysql> change replication filter replicate_do_db=(remix_test),replicate_do_table=(remix_test.sbtest1,remix_test.sbtest2);
  7. 基於GTID複製的MASTER開啓主從複製
    【20180402】MySQL關於replication filter和trigger的一些應用
  8. 基於GTID複製的MASTER創建新表
    mysql> create table sbtest1_bak like sbtest1;
    mysql> insert into sbtest1_bak select id,k*10 from sbtest1;

    9.基於GTID複製的MASTER上面創建觸發器

    root@mysqldb 15:39:  [remix_test]> delimiter ||
    root@mysqldb 15:41:  [remix_test]> create trigger tr2 after update on sbtest1
    -> for each row
    -> begin
    ->     update sbtest1_bak set k=new.k*10 where id=new.id;
    -> end||
    root@mysqldb 15:48:  [remix_test]> delimiter ||
    root@mysqldb 15:48:  [remix_test]> create trigger tr3 after delete on sbtest1
    -> for each row
    -> begin
    ->     delete from sbtest1_bak where id=old.id;
    -> end||
    Query OK, 0 rows affected (0.11 sec)
    root@mysqldb 15:49:  [remix_test]> delimiter ;
    root@mysqldb 15:06:  [remix_test]> create trigger tr1 after insert on sbtest1
    -> for each row
    -> begin
    ->     insert into sbtest1_bak(id,k) select id,k*10 from sbtest1 where id=new.id;
    -> end||
    Query OK, 0 rows affected (0.11 sec)
    root@mysqldb 15:08:  [remix_test]> delimiter ;

    【20180402】MySQL關於replication filter和trigger的一些應用

20180615 補充 5.7版本的多源複製和過濾

MySQL5.7已經開始支持多源複製了,主要語法是:

mysql> change master to ........ for channel 'channel_name';

但是我們線上在開啓多源複製的時候還需要針對單個channel做過濾,這時候我們發現MySQL5.7的版本並不支持針對單個channel做過濾,需要升級到MySQL8.0以上,過濾規則是對所有的channel都會起作用。

在偶然的一次有做複製過濾的MySQL實例做了重啓,在啓動的slave的時候突然發現報錯,show slave status的時候看到之前replication filter的規則全部丟失了。所以需要注意一下。

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