主从

读写分离可以看这个:
https://help.aliyun.com/document_detail/85143.html?spm=a2c4g.11186623.2.14.6ab869abTLXJBz

简介

image.png

mysql复制过程分成三步:

  1. master将改变记录到二进制日志(binary log).这些记录过程叫做二进制日志事件,binary log events
  2. slave将master的binary log events拷贝到他的中继日志
  3. slave重做中继日志中的事件,将改变应用到自己的数据库中.mysql复制是异步的而且串行化的

复制的基本原则

  • 每个slave只有一个master
  • 每个slave只能有一个唯一的服务器ID
  • 每个master可以有多个slave

异步半同步区别

  1. 异步复制
    简单的说就是master把binlog发送过去,不管slave是否接收完,也不管是否执行完,这一动作就结束了.
  2. 半同步复制
    简单的说就是master把binlog发送过去,slave确认接收完,但不管它是否执行完,给master一个信号我这边收到了,这一动作就结束了。(谷歌写的代码,5.5上正式应用。)
  3. 异步的劣势
    当master上写操作繁忙时,当前POS点例如是10,而slave上IO_THREAD线程接收过来的是3,此时master宕机,会造成相差7个点未传送到slave上而数据丢失。

binlog作用

记录数据库的增删改查sql语句,二进制文件.
参数说明
-F: 备份后立即刷新binlog日志
--master-date=1或者2.备份语句中添加change master语句,记录binlog位置点
开启binlog,创建rep用户,授权replication slave
binlog转储线程
当从服务器与主服务器连接时,主服务器会创建一个线程将二进制日志内容发送到从服务器。
该线程可以使用 语句SHOW PROCESSLIST(下面有示例介绍) 在服务器 sql 控制台输出中标识为Binlog Dump线程。

二进制日志转储线程获取服务器上二进制日志上的锁,用于读取要发送到从服务器的每个事件。一旦事件被读取,即使在将事件发送到从服务器之前,锁会被释放。

master.info
记录主库的binlog信息

relay log

将slaveIO线程读取到的binlog数据写入relaylog,由relaylog.info管理.
等待sql线程读取内容并转为sql语句并写入到slave

常见配置

注意创建文件夹的时候权限问题

  • 主从都配置在[mysqld]节点下,而且都是小写
  • 主机修改my.ini配置文件(必须主服务器唯一id)
  • 从修改my.ini(必须: 从服务器唯一ID, 可选: 启用二进制日志)
  • 必须启用二进制配置文件
  • 停止从服务复制功能stop slave;

主节点配置

配置文件[mysqld]
配置唯一id

  1. server-id=1

配置mysqlbin和错误日志
没有data文件夹,那就自己创建一个
log-bin=自己本地路径/mysqlbin
log-err=自己本地路径/mysqlerr

  1. log-bin=/usr/share/mysql/data/mysqlbin
  2. log-error=/usr/share/mysql/data/mysqlerr

主机读写都可以

  1. read-only=0

可选

  • 根目录,basedir=”路径”
  • 临时目录,tmpdir=”路径”
  • 数据目录=”自己本地路径/data”
  • 设置不要复制的数据库 binlog-ignore-db=mysql
  • 设置需要复制的数据库 binlog-do-db=需要复制的主数据库名

其余配置

  1. relay_log=relay-bin #开启中继日志 (日志存储位置尽量不要同数据存储同一磁盘同一目录,这里测试方便不重新指向)
  2. binlog-format=row #日志格式三种:STATEMENT,ROW,MIXED
  3. log-slave-updates=true # 配置从服务器的更新写入二进制日志
  1. mysql> show variables like 'log_%'; #查看日志是否开启

log_bin、relay_log,二进制日志和中继日志尽量不要跟数据存储放在同一磁盘同一目录,防止硬盘损坏时日志也丢失

从节点配置

必须:从节点id
可选: 启用二进制日志

log-bin=mysql-bin

其余配置

relay_log=relay-bin
binlog-format=row  
log-slave-updates=true   
sever_id=2              
#relay_log_purge=0 #禁止自动删除中继日志(slave配置文件多了下面这两条),如果是MHA开启此

启动从库

mysql -uroot -p123456 -e "set global read_only=1"  #从库只读,不建议写在配置文件中

主节点备份数据

要把表结构什么的都在从库上回放,不然主从复制会报错

[root@node01 /]# mysqldump -uroot -p123456 -h192.168.2.70 --master-data=2 --single-transaction -R --triggers -A > /home/soft/all.sql;

说明:

—master-data=2代表备份时刻记录master的Binlog位置和Position

—single-transaction意思是获取一致性快照

-R意思是备份存储过程和函数

—triggres的意思是备份触发器

-A代表备份所有的库

查看更多信息mysqldump —help

从库要回放数据

创建用户

flush privileges;
# 创建个从用户replication
Grant replication slave on *.* to 'repl'@'%' identified by '123456';
flush privileges;

查询master的状态,并记录下file和position的值(意思就是这个文件的这个位置开始复制).

mysql> show master status;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| mysqlbin.000002 |      154 |              | mysql            |                   |
+-----------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

建议用这种方式,上一种,如果中间有人修改数据,复制点就变了
查看Master主库备份时的binlog名称和位置,MASTER_LOG_FILE和MASTER_LOG_POS:

[root@node01 soft]# head -n 30 /home/soft/all.sql | grep 'CHANGE MASTER TO'
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154;

从库配置

重启从库
连接从库,准备从当前点开始复制

mysql> stop slave;  #暂停从库

mysql> CHANGE MASTER TO MASTER_HOST='192.168.33.80',
    ->      MASTER_USER='repl',
    ->      MASTER_PASSWORD='123456',
    ->      MASTER_LOG_FILE='mysqlbin.000002',
    ->      MASTER_LOG_POS=1264;
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

查看下状态

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.33.80
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysqlbin.000002
          Read_Master_Log_Pos: 1264
               Relay_Log_File: relay-bin.000002
                Relay_Log_Pos: 319
        Relay_Master_Log_File: mysqlbin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

Slave_IO_State以及Slave_IO_RunningSlave_SQL_Running都为yes

出问题,就看下面的error是什么,或者看日志

故障切换

确保所有主从数据库都开启二进制日志

mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | ON    |
+---------------+-------+
1 row in set (0.01 sec)

确保切换时数据时从库都是最新先把主库设为只读:set global read_only=1;
并且刷新一下主库log-bin日志

mysql>  show variables like 'read_only';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only     | ON    |
+---------------+-------+
1 row in set (0.00 sec)
mysql>  flush logs;
Query OK, 0 rows affected (0.02 sec)

主故障

把主节点关闭

从节点

mysql> show slave status\G;

会发现,io也连接不上

Last_IO_Error: error reconnecting to master
  1. 确保所有的relay log全部读取完毕

在所有从库上

mysql> stop slave io_thread;
Query OK, 0 rows affected (0.00 sec)

mysql> show processlist;
+----+-------------+--------------+--------+---------+------+--------------------------------------------------------+------------------+
| Id | User        | Host         | db     | Command | Time | State                                                  | Info             |
+----+-------------+--------------+--------+---------+------+--------------------------------------------------------+------------------+
|  2 | root        | localhost    | NULL   | Query   |    0 | starting                                               | show processlist |
|  4 | system user |              | NULL   | Connect | 2284 | Slave has read all relay log; waiting for more updates | NULL             |
|  5 | root        | 192.168.33.5 | mytest | Sleep   |  457 |                                                        | NULL             |
|  6 | root        | 192.168.33.5 | mytest | Sleep   |  458 |                                                        | NULL             |
+----+-------------+--------------+--------+---------+------+--------------------------------------------------------+------------------+
4 rows in set (0.00 sec)

直到看到Slave has read all relay log; waitingfor more updates,则表示从库更新都执行完毕了.
或者通过show slave status\G;查看

  1. 选择新的主库
    show slave status\G;
    对比选择Relay_Master_Log_FileExec_Master_Log_Pos最大的作为新的主库,这里我们选择slave1为新的主库(如果两个从IO进程一直都是正常,没有落后于主,且relay log都已经重放完成,两个从是一样的,选择哪个都可以)
  2. 进行相应的配置

在slave1上,执行:

mysql> stop slave;

进入datadir,删除master.inforelay-log.info(删除前,可以先备份)

$ rm -rf master.info 

$ rm -rf relay-log.info

配置my.cnf文件,开启log-bin,如果有log-slaves-updates=1read-only=1则要注释掉,
然后重启slave1或在线修改参数。

master.info:记录了mysql主服务器上的日志文件和记录位置、连接的密码。

  1. 其他slave配置

slave2也要删除这2个文件,或者reset slave;
reset slave将清除slave上的同步位置,删除所有旧的同步日志,使用新的日志重新开始,这正是我们想要的。需要注意的是,必须先停止slave服务(STOP SLAVE),我们已经在第一步停止了它。

  1. reset master

在slave1上reset master,重新生成二进制日志。(RESET MASTER将删除所有的二进制日志,创建一个.000001的空日志。如果盲目的在主库上执行这个命令会导致slave找不到master的binlog,造成同步失败。)

mysql> reset master;
Query OK, 0 rows affected (0.01 sec)

mysql> show binary logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |       154 |
+------------------+-----------+
1 row in set (0.00 sec)
  1. 在slave1上创建用于同步的用户
flush privileges;
# 创建个从用户replication
Grant replication slave on *.* to 'repl'@'%' identified by '123456';
flush privileges;
  1. 重建主从,将slave2指向slave1
    记得是slave1的ip
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_HOST='192.168.33.81',
    ->      MASTER_USER='repl',
    ->      MASTER_PASSWORD='123456',
    ->      MASTER_LOG_FILE='mysql-bin.000001',
    ->      MASTER_LOG_POS=154;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

show slave status\G;查看下

常见问题

常见排查问题

mysql> show processlist; 查看下进程是否Sleep太多。发现很正常。   
mysql> show master status\G;
mysql> show slave status\G;

主从复制,中继日志不断增长,如何设置中继日志自动清除?
配置文件my.cnf,在mysqld下增添

relay_log_purge=1 (自动清除中继日志打开)

主从同步失败,如何快速同步?

跳过出现指定错误的SQL.
如果要断开主从架构,应先stop slave io_thread;
等待执行完relay log里的内容再stop slave;

#表示跳过一步错误,后面的数字可变
stop slave;
set global sql_slave_skip_counter=1;
start slave;
# 之后再用mysql> show slave status\G 查看: