zoukankan      html  css  js  c++  java
  • MYSQL主从复制搭建及切换操作(GTID与传统)

    结构如下:

    MYSQL主从复制方式有默认的复制方式异步复制,5.5版本之后半同步复制,5.6版本之后新增GTID复制,包括5.7版本的多源复制。

    MYSQL版本:5.7.20

    操作系统版本:linux 6.7 64bit

    1、异步复制

    MYSQL 默认的复制方式,就是主库写入binlog日志后即可成功返回客户端,无须等待binlog日志传递给从库的过程。但这样一旦主库发生宕机,就有可能出现数据丢失的情况。

    1.1搭建异步主从

    1、 server-id 不一样

    2、 开启binlog,建议开启log_slave_updates,让从库也写binlog,方便后期扩展架构

    3、 binlog格式为row。

    主库操作:

    创建主从复制账号

    create user 'rep'@'192.16.20.%' identified by 'mysql';

    grant replication slave on *.* to 'rep'@'192.16.20.%';

     

    初始化:

    mysqldump -S /tmp/mysql3307.sock --single-transaction -uroot -pmysql --master-data=2 -A > salve.sql (我全库导入的有问题,只用的test库)

    注意:必须加参数 –master-data=2,让备份出来的文件中记录备份这一刻binlog文件与position号,为搭建主从环境做准备。查看备份文件中记录的当前binlog文件和position号。

     

    scp salve.sql 172.16.20.21:/binlogbak

     

    mysql -S /tmp/mysql3307.sock -uroot -pmysql < slave_test.sql

     

    在数据库命令行执行配置主从命令。

    change master to

        master_host='172.16.20.32',

        master_user='rep',

        master_password='mysql',

        master_port=3307,

        master_log_file='mysql-binlog.000010',

        MASTER_LOG_POS=797;

    start slave;

    stop slave;

    reset slave all; 清空从库的所有配置信息。

    start slave;

     

    当前从库I/O和SQL thread都是呈现Yes状态,代表从库上面操作已经完成了。

     

    Master_Log_File= Relay_Master_Log_File;

    Read_Master_Log_Pos= Exec_Master_Log_Pos

    证明目前没有主从延迟状态。

    Slave_IO_Running:从库上I/O thread 负责请求和接收主库传递来的binlog信息。

    Slave_SQL_Running:从库上SQL thread负责应用relay中的binlog的信息。

    1.2主从复制故障处理

    1、主从故障之主键冲突,错误代码为1062

    原因:由于误操作,从从库上执行写操作,导致再在主库上执行相同的操作,由于主键冲突,主从复制状态会报错。所以生产环境建议在从库上开启read only,避免在从库执行写操作。

    在主库上建表t1;

    现在从库插入数据,后面再在主库上插入相同的语句。

    主库:

     

    从库:

     

    主库执行相同语句:

     

    从库报错如下:

     

    报错代码:

    Last_Errno: 1062

    处理办法:

    利用percona-toolkit 工具:

    mount /dev/sr0 /mnt

    yum -y install perl-DBD-MySQL

    ./pt-slave-restart -S /tmp/mysql3307.sock -uroot -pmysql

     

    在查看主从状态,已经恢复正常:

     

    2、主从故障之主库更新数据,从库找不到而报错,错误代码为10032

    上一个错误是主从都有相同的数据,我们可以直接通过percona-toolkit工具跳过错误。但如果从库上少数据,就不能跳过错误了,需要找到缺少的数据,在从库上从新执行一遍。

    故障原因:由于误操作,在从库上执行delete 删除操作,导致主从数据不一致。这时再在主库执行同条数据的更新操作,由于从库没有该数据,SQL无法再从库上实现。

     

    模拟故障:

    先在从库服务器的test库下的t表中,执行delete删除语句操作。

     

    再在主库上执行:相同数据的update更新操作。

     

    从库报错如下:

     

    报错代码1032。

    解决办法:

    根据报错信息所知道Binlog文件和position(7153)号,在主库上,通过mysqlbinlog 命令,找到在主库上执行的那条SQL语句导致的主从报错。

    /usr/local/mysql5.7/bin/mysqlbinlog --no-defaults -v -v --base64-output=decode-rows /mydata/mysql/mysql3307/logs/mysql-binlog.000010 |grep -A 10 7153

     

    insert into t2 values(1,'bbb');

    生产上如果丢失数以万计条的数据,建议重新搭建主从确保数据一致性。

     

    从库跳过错误:

    ./pt-slave-restart -S /tmp/mysql3307.sock -uroot -pmysql

     

    重新同步成功:

    show slave statusG;

     

    1.3主从故障之主从server-id一致

    由于粗心,安装的时候用模板并没有修改servier-id。(修改从库server-id成不同的值)

    1.4主从故障之跨库操作,丢失数据

    原因:在主库中设置binlog-do-db参数,使用的binlog记录格式为statement模式,导致在主库上执行跨库操作时,从库没有复制成功,丢失数据。

    故障操作描述:

    在主库的参数文件中添加binlog-do-db=test,代表只复制zs这个库,并且主库binlog_format 设置为statement。

    2、半同步复制

       异步复制方式不足之处在于,当主库把event写入二进制日志后,并不知道从库是否已经接受并应用日志了;如果主库发生意外宕机或者是奔溃,很有可能主库提交的事物没有传到任何一台从库机器上。在高可用集群架构下做主备切换,就会造成新的主库丢失数据。

       mysql5.5版本之后引入了半同步复制功能,主从服务器必须安装半同步复制插件,才能开启该复制功能。该功能确保从库接收完主库传递过来的binlog内容已经写入到自己的relay log里面了,才会通知主库上面的等待线程,该操作完毕。

        如果等待超时,超过rpl_semi_sync_master_timeout 参数设置时间,则关闭半同步复制,并自动转换为异步复制模式,直到至少有一台从库通知主库已经接收到binlog信息位置。

      半同步复制提升了主从之间数据的一致性,让复制更加安全可靠,在mysql5.7版本中又增加了rpl_semi_sync_master_wait_point 参数,用来控制半同步模式下主库在返回给session事物成功之前的事务提交方式。

    该参数有两个值:

    AFTER_SYNC(缺省值):主服务器将每个事务写入其二进制日志和从服务器,并将二进制日志同步到磁盘。同步后,主设备等待从设备确认事务接收。在收到确认后,主服务器将事务提交给存储引擎,并将结果返回给客户端,然后客户端可以继续。

    AFTER_COMMIT:主服务器将每个事务写入其二进制日志和从服务器,同步二进制日志,并将事务提交给存储引擎。主提交后等待从服务器确认事务接收。在收到确认后,主人将结果返回给客户端,然后客户端可以继续。

    2.1半同步方式安装

    主库上先安装复制插件和开启半同步复制功能:

    INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

    set global rpl_semi_sync_master_enabled=on;

     

    show variables like '%rpl_semi_sync_master%';

     

    主库参数文件添加:

    rpl_semi_sync_master_enabled=1

    rpl_semi_sync_master_timeout=1000

    show plugins;

     

    SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS  WHERE PLUGIN_NAME LIKE '%semi%';

     

    同理从库也安装插件和开启半同步复制功能:

    从库安装复制插件和开启半同步复制功能:

    INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

    set global rpl_semi_sync_slave_enabled=on;

     

    备库参数文件添加:

    rpl_semi_sync_slave_enabled=1

    由于之前是异步复制,需要重启从库IO线程,激活半同步复制。

    STOP SLAVE IO_THREAD;

    START SLAVE IO_THREAD;

     

    此时查看主库:

    已经有一个从库连接到主库了。而且是半同步方式。

    show variables like '%rpl_semi_sync_master%';

     

    2.2半同步方式切换成异步方式

    1、主库超时超过指定参数时间

    rpl_semi_sync_master_timeout 时间为10s。生产上建议设置的尽可能大。

    set global rpl_semi_sync_master_timeout=xxx;

     

    2、手动切换

    主库

    set rpl_semi_sync_master_enabled=off;

    从库:

    set rpl_semi_sync_slave_enabled=off

    关闭从库I/O thread

    stop salve io_thread;

    start salve io_thread;

    3、GTID复制

    GTID复制又叫全局事物ID(global transaction ID),是一个已提交事物的编号,并且是一个全局唯一的编号,MYSQL5.6版本之后在主从复制类型上新增了GTID复制。

    GTID是由server_uuid和事物id组成的,即GTID=servier_uuid:transacton_id。Server_uuid是在数据库启动过程中自动生成的,每台机器的server-uuid不一样。UUID存放在数据目录的auto.cnf文件下。而trasaciton_id就是事物提交时由系统顺序分配的一个不会重复的序列号。

    3.1GTID存在的价值

    1、GTID使用master_auto_position=1 代替了基于binlog和position号的主从复制搭建的方式,更便于主从复制的搭建。

    2、GTID可以知道事务在最开始是在哪个实例上提交的。

    3、GTID方便实现主从之间的failover,再也不用不断的去找position和binlog。

    3.2GTID搭建模式

    GTID不需要传统的binlog和position号了,而是在从库”change master to”时使用”master_auto_position=1”的方式搭建,这就让操作变得更加方便和可靠了。

    3.3配置前期准备

    在安装好主从数据库之后:

    主库需要以下配置:

    gtid_mode=on

    enforce_gtid_consistency=on

    log_bin=on

    从库需要以下配置:

    servier-id 主从库不能一样。

    gtid_mode=on

    enforce_gtid_consistency=on

    log_slave_updates=1

    主库操作:

    创建主从复制账号

    create user 'rep'@'192.16.20.%' identified by 'mysql';

    grant replication slave on *.* to 'rep'@'172.16.20.%';

    show grants for 'rep'@'172.16.20.%';

     

    如果不同网段建议主从各建各的。

    初始化:

    /usr/local/mysql5.7/bin/mysqldump -S /tmp/mysql3307.sock --single-transaction -uroot -pmysql --master-data=2 -A > slave.sql(我全库导入的有问题,换成绝对路径就行,只用的test库)

    注意:必须加参数 –master-data=2,让备份出来的文件中记录备份这一刻binlog文件与position号,为搭建主从环境做准备。查看备份文件中记录的当前binlog文件和position号。

     

    scp salve.sql 172.16.20.21:/binlogbak

    test库操作:

    mysqldump -S /tmp/mysql3307.sock --single-transaction -uroot -pmysql --master-data=2 --database test > slave_test.sql

    注意,如果主从GTI不一样,数据一致可以:

    set global gtid_purged='*******';

     

    如果数据不一样,GTID也不一样,建议按照下面先reset从库:reset master;

    mysql -S /tmp/mysql3307.sock -uroot -pmysql < slave_test.sql

     

    注意:多次恢复会报错,因为里面已经有gtid的信息了。

    如果你还想继续强制恢复,可以在从库上 reset master ;reset后,gtid_executed,gtid_purged为空。

    show global variables like '%gtid%';

    3.4主从配置

    1、如果是在已经跑的服务器,你需要重启一下mysql server。

    2、启动之前,一定要先关闭master的写入,保证所有slave端都已经和master端数据保持同步。

    3、所有slave需要加上skip_slave_start=1的配置参数,避免启动后还是使用老的复制协议。

    在数据库命令行执行配置主从命令。

    change master to master_host='172.16.20.32',master_port=3307,master_user='rep',master_password='mysql',master_auto_position=1;

    提示:master_log_file='mysql-binlog.000010',MASTER_LOG_POS=797 这两个参数替换成了 master_auto_position=1

     

    show slave statusG;

     

    start slave;

    stop slave;

    reset slave all; 清空从库的所有配置信息。

    start slave;

    一般复制建议半同步+GTID复制:

    附带主从参数:

    主:

    [root@mysql5 ~]# cat /etc/my3307.cnf

    [client]

    port = 3307

    socket = /tmp/mysql5.7.sock

    [mysql]

    prompt="u@db R:m:s [d]> "

    no-auto-rehash

    [mysqld]

    user = mysql

    port = 3307

    basedir = /usr/local/mysql5.7

    datadir = /mydata/mysql/mysql3307/data

    socket = /tmp/mysql3307.sock

    rpl_semi_sync_master_enabled=1

    rpl_semi_sync_master_timeout=1000

    event_scheduler=1

    sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

    lower_case_table_names=1

    secure_file_priv=/tmp

    character-set-server = utf8mb4

    skip_name_resolve = 1

    open_files_limit = 65535

    back_log = 1024

    max_connections = 500

    max_connect_errors = 1000000

    table_open_cache = 1024

    table_definition_cache = 1024

    table_open_cache_instances = 64

    thread_stack = 512K

    external-locking = FALSE

    max_allowed_packet = 32M

    sort_buffer_size = 4M

    join_buffer_size = 4M

    thread_cache_size = 768

    query_cache_size = 0

    query_cache_type = 0

    interactive_timeout = 600

    wait_timeout = 600

    tmp_table_size = 32M

    max_heap_table_size = 32M

    slow_query_log = 1

    slow_query_log_file = /mydata/mysql/mysql3307/logs/slow.log

    log-error = /mydata/mysql/mysql3307/logs/error.log

    long_query_time = 0.5

    server-id = 3307101

    log-bin = /mydata/mysql/mysql3307/logs/mysql-binlog

    sync_binlog = 1

    binlog_cache_size = 4M

    max_binlog_cache_size = 1G

    max_binlog_size = 500M

    expire_logs_days = 7

    master_info_repository = TABLE

    relay_log_info_repository = TABLE

    gtid_mode = on

    enforce_gtid_consistency = 1

    log_slave_updates

    binlog_format = row

    relay_log_recovery = 1

    relay-log-purge = 1

    key_buffer_size = 32M

    read_buffer_size = 8M

    read_rnd_buffer_size = 4M

    bulk_insert_buffer_size = 64M

    lock_wait_timeout = 3600

    explicit_defaults_for_timestamp = 1

    innodb_thread_concurrency = 0

    innodb_sync_spin_loops = 100

    innodb_spin_wait_delay = 30

    transaction_isolation = REPEATABLE-READ

    innodb_buffer_pool_size = 1024M

    innodb_buffer_pool_instances = 8

    innodb_buffer_pool_load_at_startup = 1

    innodb_buffer_pool_dump_at_shutdown = 1

    innodb_data_file_path = ibdata1:1G:autoextend

    innodb_flush_log_at_trx_commit = 1

    innodb_log_buffer_size = 32M

    innodb_log_file_size = 2G

    innodb_log_files_in_group = 2

    innodb_max_undo_log_size = 4G

    innodb_io_capacity = 2000

    innodb_io_capacity_max = 4000

    innodb_flush_neighbors = 0

    innodb_write_io_threads = 8

    innodb_read_io_threads = 8

    innodb_purge_threads = 4

    innodb_page_cleaners = 4

    innodb_open_files = 65535

    innodb_max_dirty_pages_pct = 50

    innodb_flush_method = O_DIRECT

    innodb_lru_scan_depth = 4000

    innodb_checksum_algorithm = crc32

    innodb_lock_wait_timeout = 10

    innodb_rollback_on_timeout = 1

    innodb_print_all_deadlocks = 1

    innodb_file_per_table = 1

    innodb_online_alter_log_max_size = 500M

    internal_tmp_disk_storage_engine = InnoDB

    innodb_stats_on_metadata = 0

    innodb_status_file = 1

    innodb_status_output = 0

    innodb_status_output_locks = 0

    #performance_schema

    performance_schema = 1

    performance_schema_instrument = '%=on'

    #innodb monitor

    innodb_monitor_enable="module_innodb"

    innodb_monitor_enable="module_server"

    innodb_monitor_enable="module_dml"

    innodb_monitor_enable="module_ddl"

    innodb_monitor_enable="module_trx"

    innodb_monitor_enable="module_os"

    innodb_monitor_enable="module_purge"

    innodb_monitor_enable="module_log"

    innodb_monitor_enable="module_lock"

    innodb_monitor_enable="module_buffer"

    innodb_monitor_enable="module_index"

    innodb_monitor_enable="module_ibuf_system"

    innodb_monitor_enable="module_buffer_page"

    innodb_monitor_enable="module_adaptive_hash"

    [mysqldump]

    quick

    max_allowed_packet = 32M

    从:

    [root@rac1 ~]# cat /etc/my3307.cnf

    [client]

    port = 3307

    socket = /tmp/mysql5.7.sock

    [mysql]

    prompt="u@db R:m:s [d]> "

    no-auto-rehash

    [mysqld]

    user = mysql

    port = 3307

    basedir = /usr/local/mysql5.7

    datadir = /mydata/mysql/mysql3307/data

    socket = /tmp/mysql3307.sock

    character-set-server = utf8mb4

    lower_case_table_names=1

    sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

    rpl_semi_sync_slave_enabled=1

    skip_name_resolve = 1

    open_files_limit = 65535

    back_log = 1024

    max_connections = 500

    max_connect_errors = 1000000

    table_open_cache = 1024

    table_definition_cache = 1024

    table_open_cache_instances = 64

    thread_stack = 512K

    external-locking = FALSE

    max_allowed_packet = 32M

    sort_buffer_size = 4M

    join_buffer_size = 4M

    thread_cache_size = 768

    query_cache_size = 0

    query_cache_type = 0

    interactive_timeout = 600

    wait_timeout = 600

    tmp_table_size = 32M

    max_heap_table_size = 32M

    slow_query_log = 1

    slow_query_log_file = /mydata/mysql/mysql3307/logs/slow.log

    log-error = /mydata/mysql/mysql3307/logs/error.log

    long_query_time = 0.1

    server-id = 3307102

    log-bin = /mydata/mysql/mysql3307/logs/mysql-binlog

    sync_binlog = 1

    binlog_cache_size = 4M

    max_binlog_cache_size = 500m

    max_binlog_size = 200m

    expire_logs_days = 7

    master_info_repository = TABLE

    relay_log_info_repository = TABLE

    gtid_mode = on

    enforce_gtid_consistency = 1

    log_slave_updates

    binlog_format = row

    relay_log_recovery = 1

    relay-log-purge = 1

    key_buffer_size = 32M

    read_buffer_size = 8M

    read_rnd_buffer_size = 4M

    bulk_insert_buffer_size = 64M

    lock_wait_timeout = 3600

    explicit_defaults_for_timestamp = 1

    innodb_thread_concurrency = 0

    innodb_sync_spin_loops = 100

    innodb_spin_wait_delay = 30

    transaction_isolation = REPEATABLE-READ

    innodb_buffer_pool_size = 1024M

    innodb_buffer_pool_instances = 8

    innodb_buffer_pool_load_at_startup = 1

    innodb_buffer_pool_dump_at_shutdown = 1

    innodb_data_file_path = ibdata1:1G:autoextend

    innodb_flush_log_at_trx_commit = 1

    innodb_log_buffer_size = 32M

    innodb_log_file_size = 1g

    innodb_log_files_in_group = 2

    innodb_max_undo_log_size = 1G

    innodb_io_capacity = 2000

    innodb_io_capacity_max = 4000

    innodb_flush_neighbors = 0

    innodb_write_io_threads = 8

    innodb_read_io_threads = 8

    innodb_purge_threads = 4

    innodb_page_cleaners = 4

    innodb_open_files = 65535

    innodb_max_dirty_pages_pct = 50

    innodb_flush_method = O_DIRECT

    innodb_lru_scan_depth = 4000

    innodb_checksum_algorithm = crc32

    innodb_lock_wait_timeout = 10

    innodb_rollback_on_timeout = 1

    innodb_print_all_deadlocks = 1

    innodb_file_per_table = 1

    innodb_online_alter_log_max_size = 4G

    internal_tmp_disk_storage_engine = InnoDB

    innodb_stats_on_metadata = 0

    innodb_status_file = 1

    innodb_status_output = 0

    innodb_status_output_locks = 0

    performance_schema = 1

    performance_schema_instrument = '%=on'

    #innodb monitor

    innodb_monitor_enable="module_innodb"

    innodb_monitor_enable="module_server"

    innodb_monitor_enable="module_dml"

    innodb_monitor_enable="module_ddl"

    innodb_monitor_enable="module_trx"

    innodb_monitor_enable="module_os"

    innodb_monitor_enable="module_purge"

    innodb_monitor_enable="module_log"

    innodb_monitor_enable="module_lock"

    innodb_monitor_enable="module_buffer"

    innodb_monitor_enable="module_index"

    innodb_monitor_enable="module_ibuf_system"

    innodb_monitor_enable="module_buffer_page"

    innodb_monitor_enable="module_adaptive_hash"

    [mysqldump]

    quick

    max_allowed_packet = 32M

    4、GTID复制与传统复制的切换

    主:172.16.20.32

    从:172.16.10.21

    a)    GTID切换成传统复制

    从库信息:

    show slave statusG;

     

    主库:

     

    从库切换操作:

    stop slave;

    change master to master_auto_position=0,master_host='172.16.20.32', master_port=3307,master_user='rep',master_password='mysql',Master_Log_File='mysql-binlog.000007',Master_Log_Pos=194;

    start slave;

    主从数据库服务器上同时,依次执行以下操作:

    set global gtid_mode='on_permissive';

    set global gtid_mode='off_permissive';

    主从关闭GTID功能:

    set global enforce_gtid_consistency=off;

    set global gtid_mode=off;

    把gtid_mode=off和enforce_gtid_consistency=off写入配置文件my3307.cnf中。重启后可以继续生效,并进行测试。

    b)    传统复制切换成GTID过程

    主从数据库服务器同时修改以下参数:

    set global enforce_gtid_consistency=warn; error log 不会出现警告信息,如果有,需要先修复,才能继续后面操作。

    set global enforce_gtid_consistency=on;

    set global gtid_mode=off_permissive;

    set global gtid_mode=on_permissive;

     

    确认从库没等待的事务:

    show global status like '%ongoing%';

     

    0代表没有等待的事务。

    主从库上同时设置gtid_mode=on;

    set global gtid_mode=on;

    show variables like '%gtid%';

     

    把传统复制模式改为(GTID)复制。

    stop slave;

    change master to master_auto_position=1;

     

    查看并进行测试:

     

    主:

     

    从:

     

    例如主库:

     

    从库:

    5、GTID使用限制条件

    GTID复制是针对事物,一个gtid对于一个事务。

    1、 不能使用create table table_name select * from table_name。

    2、 在一个事务中即包含事务表的操作,又包含非事物表。

    3、 不支持create temporary table or drop temporary table语句操作。

    4、 使用GTID复制从库调过错误,不支持执行slave_skip_errors。

  • 相关阅读:
    高性能解决线程饥饿的利器 StampedLock
    月初刚拿到美团offer,新鲜的美团现场面试41题(三面技术+HR面)
    这两份Java“并发+异步”编程宝典,堪称编程界的“瑰宝”
    阿里架构师的学习笔记:多线程+JVM+Mysql+Redis+设计模式
    打造多模块+高可用+高扩展Spring Cloud版分布式电商项目源码分享
    想要彻底搞懂微服务架构必先学:SpringBoot+SpringCloud+docker
    厉害了!阿里P8手写《springboot 核心》PDF来了
    阿里P8熬夜总结Spring源码笔记,上线3分钟“全网跪求”
    UFLDL课程学习(二)
    UFLDL课程学习(一)
  • 原文地址:https://www.cnblogs.com/hmwh/p/9198705.html
Copyright © 2011-2022 走看看