1. 程式人生 > >【20180402】MySQL關於replication filter和trigger的一些應用

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

trigger replication filter binlog format sql_mode

需求描述

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上面必須修改SQL_MODE,將SQL_MODE更改成ON_PERMISSIVE(本地產生GTID事務,並且接受隱性事務(即非GTID事務)):
    mysql> set global sql_mode=‘ON_PERMISSIVE‘;
  • 至於基於GTID復制的主從的SLAVE同步來自於MASTER的數據,這裏暫時沒有什麽好的方案,或者我們還沒有進行實驗。但是之前我們忘記在本地進行修改SQL_MODE導致在SLAVE上面執行SHOW SLAVE STATUS \G可以看出很明顯的錯誤:ERROR:1236
  • 復制過濾:需要註意的如果你僅僅只是寫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的sql_node修改
    mysql> set global sql_mode=‘ON_PERMISSIVE‘;
  5. 基於GTID復制的MASTER上面搭建主從(從基於後面備份的sbtest2_20180402.sql上面獲取binlog file和position信息)
    mysql> change master  ......
  6. 基於GTID復制的MASTER上面做復制過濾
    mysql> change replication replication_do_db=(remix_test),replication_do_table=(remix_test.sbtest1,remix_test.sbtest2);
  7. 基於GTID復制的MASTER開啟主從復制
    技術分享圖片
  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的一些應用