主从跳过SQL错误的脚本

GTID跳过SQL错误的脚本

    #!/bin/bash
    #Author:wwa
    USER=
    PWD=
    HOST=
    PORT=
    REP=
    REPPWD=
    REPH=
    REPP=
    GTID=$2
    GTID_START=$3
    GTID_END=$4
    GTID_PURGE(){
    echo "GTID_UUID:$GTID, GTID_START:$GTID_START, GTID_END=%GTID_END"
    mysql -u$USER -p$PWD -h$HOST -P$PORT -e "stop slave;reset slave;reset master;set global gtid_purged='$GTID:$GTID_START-$GTID_END';CHANGE MASTER TO MASTER_HOST='$REPH', MASTER_PORT=$REPP, MASTER_USER='$REP',MASTER_PASSWORD='$REPPWD', master_auto_position=1;start slave;"
    sleep 1
    mysql -u$USER -p$PWD -h$HOST -P$PORT -e "show slave status\G;"
    }
    GTID_SKIP(){
    mysql -u$USER -p$PWD -h$HOST -P$PORT -e "stop slave;set session gtid_next='$GTID:$GTID_START';begin;commit;set session gtid_next="AUTOMATIC";start slave;"
    }

    case "$1" in
    GTID_PURGE)
    echo "Start GTID_PURGE, transaction in $GTID between $GTID_START-$GTID_END will be skipped......"
    GTID_PURGE
    echo "GTID_PURGE success......"
    ;;
    GTID_SKIP)
    echo "Start GTID_SKIP, transaction $GTID:$GTID_START will be skipped......"
    GTID_SKIP
    echo "GTID_SKIP success......"
    ;;
    *)
    echo $"Usage: $0 {GTID_PURGE args1 args2 args3|GTID_SKIP args1 args2}"
    exit 1
    ;;
    esac


GTID_PURGE() 当同步发生大量的错误时,使用flush table with read lock锁住主库,记录GTID的事务编号(最后那个,例如后面示例里面的142787),然后数据同步到从库,在参数中加上UUID(空格)起始事务编号(空格)中止事务编号
原理:purge掉master log中,同步数据的SCN之前的事务,从同步时间点以后开始读取binlog; 这样做的好处是不用去master操作,清理binlog(手抖清理了其他东西就不好了~)

GTID_SKIP() 当发生少量的错误时,使用show slave status\G;找到UUID和出错的事务编号,参数中加上 UUID(空格)事务编号
原理:生成一个空事务来跳过原本出错的事务,然后继续往下同步

关于如何判断GTID_SKIP()需要跳过的事务编号:
假设出错时,slave status是这个样子,

箭头所指的代表从主库拉去的日志中,包含哪些事务(以编号的形式);
方框所指的代表从库现在执行了哪些事务;这里的意思就是从库已经执行了编号1到编号139595的事务
如果出错了,说明139596事务出错了,这时候执行脚本里的事务编号写上139596就行

实际使用效果如图(截图时间截晚了,事务ID对不上,不过意思表达清楚了就好~)


结语:任何发生于数据库上的操作一定要三思而后回车,血的教训数不胜数,所以验证无误的固定操作,用脚本来实现是个不错的选择。

 

check mysql slave状态并跳过相应错误(守护模式)

一、具体代码

#!/bin/bash

#--------------------------------------------------

#Author:jimmygong

#Email:[email protected]

#FileName:checkslave.sh

#Function: 

#Version:1.0 

#Created:2016-01-01

#--------------------------------------------------

if [[ `id -u` -ne 0 ]]

then

    echo "script need root"

    exit 1

fi

function usage ()

{

    echo "Usage:bash $0 start"

    echo "Usage:bash $0 stop"

}

if [[ $# -ne 1 ]]

then

    usage

    exit 1

fi

#根据情况修改变量信息

mysqluser="root"

mysqlpass="123456"

mysqlport=3398

mysqlsocket="/opt/mysql/$mysqlport/$mysqlport.sock"

currdate=$(date +%Y%m%d)

logpath="/root/checkslave"

logfile=$logpath/${currdate}

#各个错误代码解释

1158网络错误,出现读错误,请检查网络连接状况

1159网络错误,读超时,请检查网络连接状况

1008数据库不存在,删除数据库失败

1007数据库已存在,创建数据库失败

1062字段值重复,入库失败

1452不能删除或更新父行,外键约束失败

PS:1452这个根据情况跳过错误

allerror=(1158 1159 1008 1007 1062 1452)

sleeptime=60

[[ -d $logpath ]] || mkdir $logpath

function echosucc ()

{

    succstatus="[ Ok ]"

    printf "\033[32m $succstatus $* \033[0m\n"

}

function mysqlconn ()

{

    comm=$1

    mysql -u${mysqluser} -p${mysqlpass} -S ${mysqlsocket} -e "$comm"

}

function checkrun ()

{

        esport=$(lsof -i:$mysqlport|wc -l)

    if [[ $esport -lt 2 ]]

    then

    echo "Mysql Server Failed"

    exit 1

        fi

}

function statuserror ()

{

    for((i=0;i<${#allerror[*]};i++))

    do

        if [[ "$1" == "${allerror[$i]}" ]]

        then

            mysqlconn "stop slave;set global sql_slave_skip_counter=1;start slave;"

        else 

            echo "$(date +"%m-%d %H:%M:%S") Mysql Slave Failed $1" >> $logfile

        fi

    done

}

function checkslave ()

{

    status=($(mysqlconn "show slave status\G"|egrep -i "_running|last_errno"|awk '{print $NF}'))

    if [[ "${status[0]}" == "Yes" ]] && [[ "${status[1]}" == "Yes" ]]

    then

        echo "$(date +"%m-%d %H:%M:%S") Mysql Slave Ok" >> $logfile

    else

        statuserror ${status[2]}

    fi

}

function start () 

{

    echosucc "Starting Monitor"

    while true

    do

        checkrun

        checkslave

        sleep $sleeptime

    done

}

function stop () 

{

    echosucc "Stop Monitor"

    echo $$ > "$logpath/pidfile"

    for PID in $(ps -eo pid,command|grep "$0"|grep -v grep|awk '{print $1}');

    do

        [[ $PID != $(cat $logpath/pidfile) ]] && kill -s TERM $PID >& /dev/null

    done

}

case $1 in

    start)

    start &

    ;;

    stop)

    stop

    ;;

    *)

    usage

    ;;

esac

 

 

 

mysql开启GTID跳过错误的方法

1、数据库版本

mysql> select version()
    -> ;
+-------------------------------------------+
| version()                                 |
+-------------------------------------------+
| 5.7.17                                  |
+-------------------------------------------+
1 row in set (0.00 sec)

 

--主从同步

10.10.6.87  主

10.10.90 从

 

2、产生问题过程

(1)主从库开启了GTID模式

(2)在从库某表添加了唯一索引,然后去主库给某表添加索引,导致问题 (此处为了测试,故意为之,制造问题)

 'Duplicate key name 'i_index'' on query. Default database: 'test'. Query: 'create unique index i_index on t(id)'
 (3) 查看从库状态,发现Slave_SQL_Running: No
mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.10.6.87
                  Master_User: rep
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000028
          Read_Master_Log_Pos: 1113
               Relay_Log_File: mysql-bin.000007
                Relay_Log_Pos: 1151
        Relay_Master_Log_File: mysql-bin.000028
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
              Replicate_Do_DB: 
          Replicate_Ignore_DB: mysql,information_schema
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 1061
                   Last_Error: Error 'Duplicate key name 'i_index'' on query. Default database: 'test'. Query: 'create unique index i_index on t(id)'
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 938
              Relay_Log_Space: 2301
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 1061
               Last_SQL_Error: Error 'Duplicate key name 'i_index'' on query. Default database: 'test'. Query: 'create unique index i_index on t(id)'
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2
                  Master_UUID: 8f9e146f-0a18-11e7-810a-0050568833c8
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0                                            #SQL延迟同步
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 170421 15:44:05
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 8f9e146f-0a18-11e7-810a-0050568833c8:1-4
            Executed_Gtid_Set: 8f9e146f-0a18-11e7-810a-0050568833c8:1-3,     #多出了一个GTID(本身实例执行的事务)
f7c86e19-24fe-11e7-a66c-005056884f03:1-9
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)


ERROR: 
No query specified


3、解决问题过程:

mysql> stop slave sql_thread;
Query OK, 0 rows affected, 1 warning (0.00 sec)


mysql> set global sql_slave_skip_counter=1;
ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)


mysql> set global sql_slave_skip_counter=1;
ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction


从上面可以发现按照往常解决办法,是行不通的,因为开启了GTID原因




3.1 分析出现问题时候GTID值

通过分析法获取gtid值 

通过查看mysql> show slave status \G;

查看一下信息并记录下来:
 Retrieved_Gtid_Set: 8f9e146f-0a18-11e7-810a-0050568833c8:1-4  --跳过此事务  
Executed_Gtid_Set: 8f9e146f-0a18-11e7-810a-0050568833c8:1-3,f7c86e19-24fe-11e7-a66c-005056884f03:1-9

 

通过上面的信息可以知道已经执行的gtid是8f9e146f-0a18-11e7-810a-0050568833c8:1-3,准备要执行8f9e146f-0a18-11e7-810a-0050568833c8:4的时候出问题了,所以条跳过此步骤




或者 通过日志查看  (推荐)


#170421 15:36:28 server id 2  end_log_pos 938 CRC32 0x9f9f38d8 Xid = 140
COMMIT/*!*/;
# at 938
#170421 15:39:10 server id 2  end_log_pos 1003 CRC32 0x20f00692 GTID last_committed=3sequence_number=4
SET @@SESSION.GTID_NEXT= '8f9e146f-0a18-11e7-810a-0050568833c8:4'/*!*/;
# at 1003
#170421 15:39:10 server id 2  end_log_pos 1113 CRC32 0x4b10f015 Query thread_id=25420exec_time=0error_code=0
use `test`/*!*/;
SET TIMESTAMP=1492760350/*!*/;
create unique index i_index on t(id)
/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

 

解决办法一:跳过错误

(1)停止slave进程

mysql> STOP SLAVE;

(2)设置事务号,事务号从Retrieved_Gtid_Set获取

在session里设置gtid_next,即跳过这个GTID

mysql> SET @@SESSION.GTID_NEXT= '8f9e146f-0a18-11e7-810a-0050568833c8:4'

(3)设置空事物

mysql> BEGIN; COMMIT;

(4)恢复事物号

mysql> SET SESSION GTID_NEXT = AUTOMATIC;

(5)启动slave进程

mysql> START SLAVE;

解决办法二:重置master方法跳过错误


mysql>  STOP SLAVE;
mysql> RESET MASTER;
mysql>   SET @@GLOBAL.GTID_PURGED ='8f9e146f-0a18-11e7-810a-0050568833c8:1-4'
mysql>  START SLAVE;


上面这些命令的用意是,忽略8f9e146f-0a18-11e7-810a-0050568833c8:1-4 这个GTID事务,下一次事务接着从 5 这个GTID开始,即可跳过上述错误。
 

解决办法三:使用pt-slave-restart工具


pt-slave-restart工具的作用是监视某些特定的复制错误,然后忽略,并且再次启动SLAVE进程(Watch and restart MySQL replication after errors)。
忽略所有1062错误,并再次启动SLAVE进程
[root@dgt mysql]# pt-slave-resetart -S /var/lib/mysql/mysql.sock —error-numbers=1062


检查到错误信息只要包含 test.t1,就一概忽略,并再次启动 SLAVE 进程
[root@dgt mysql]# pt-slave-resetart -S /var/lib/mysql/mysql.sock  —error-text=”test.t1”


下面举例解决错误问题号
            Last_SQL_Error: Could not execute Delete_rows event on table test.t; Can't find record in 't', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000028, end_log_pos 1862
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2
                  Master_UUID: 8f9e146f-0a18-11e7-810a-0050568833c8
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 170421 17:45:11
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 8f9e146f-0a18-11e7-810a-0050568833c8:1-7
            Executed_Gtid_Set: 8f9e146f-0a18-11e7-810a-0050568833c8:1-6,
f7c86e19-24fe-11e7-a66c-005056884f03:1
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)


[root@dgt mysql]# pt-slave-restart  -S  /var/lib/mysql/mysql.sock   --error-numbers=1032   --user=root --password='bc.123456'
2017-04-21T17:53:27 S=/var/lib/mysql/mysql.sock,p=...,u=root mysql-bin.000015         620 1032 
2017-04-21T17:54:31 S=/var/lib/mysql/mysql.sock,p=...,u=root mysql-bin.000015        1140 1032 


参数解释:
 --slave-password=s     Sets the password to be used to connect to the slaves
  --slave-user=s         Sets the user to be used to connect to the slaves
  --sleep=i              Initial sleep seconds between checking the slave ( default 1)
  --socket=s         -S  Socket file to use for connection=
--password=s       -p  Password to use when connecting
pt-slave-resetart  -S./mysql.sock —error-numbers=1032
 --error-numbers=h      Only restart this comma-separated list of errors
 --host=s           -h  Connect to host
 --user=s           -u  User for login if not current user

你可能感兴趣的:(MYSQL主从)