# mysql复制的原理与流程 --- 1. **什么是mysql复制?** 简单来说就是保证主服务器(Master)和从服务器(Slave)的数据是一致性的,向Master插入数据后,Slave会自动从Master把修改的数据同步过来(有一定的延迟),通过这种方式来保证数据的一致性,就是Mysql复制。 2. **mysql复制原理** 三个线程:主库线程,从库I/O线程,从库SQL线程 复制流程: (1)I/O 线程向主库发出请求 (2)主库线程响应请求,并推BINLOG 日志到从库 (3)I/O 线程收到线程并记入中继日志 (4)SQL线程从中继日志读取SQL,并记入从库binlog日志,FLUSH进硬盘 3. **mysql一致性,延时性,数据恢复** - 主从复制一致性由BINLOG执行顺序保证(TIMESPAN+POS) 日志越详细,主从一致性越容易保证 - 延时性:延时表现为 延迟时间=从库执行SQL完成的时刻-主库开始执行SQL时间 - 数据恢复 备份时记录的BINLOG位置点 - showslave status\G 4. **Mysql复制能解决什么问题** - 高可用和故障切换 复制能够帮避免MySql单点失败,因为数据都是相同的,所以当Master挂掉后,可以指定一台Slave充当Master继续保证服务运行,因为数据是一致性的(如果当插入Master就挂掉,可能不一致,因为同步也需要时间),当然这种配置不是简单的把一台Slave充当Master,毕竟还要考虑后续的Salve同步Master - 负载均衡 因为读写分离也算是负载均衡的一种,所以就不单独写了,因为一般都是有多台Slave的,所以可以将读操作指定到Slave服务器上(需要代码控制),然后再用负载均衡来选择那台Slave来提供服务,同时也可以吧一些大量计算的查询指定到某台Slave,这样就不会影响Master的写入以及其他查询 - 数据备份 一般我们都会做数据备份,可能是写定时任务,一些特殊行业可能还需要手动备份,有些行业要求备份和原数据不能在同一个地方,所以主从就能很好的解决这个问题,不仅备份及时,而且还可以多地备份,保证数据的安全 - 业务模块化 可以一个业务模块读取一个Slave,再针对不同的业务场景进行数据库的索引创建和根据业务选择MySQL存储引擎, 不同的slave可以根据不同需求设置不同索引和存储引擎 5. **主从配置需要注意的点** (1)主从服务器操作系统版本和位数一致; (2) Master和Slave数据库的版本要一致; (3) Master和Slave数据库中的数据要一致; (4) Master开启二进制日志,Master和Slave的server_id在局域网内必须唯一。 6. **mysql复制的步骤** - Master将数据改变记录到二进制日志(binary log)中,也就是配置文件log-bin指定的文件,这些记录叫做二进制日志事件(binary log events) - Slave通过I/O线程读取Master中的binary log events并写入到它的中继日志(relay log) - Slave重做中继日志中的事件,把中继日志中的事件信息一条一条的在本地执行一次,完成数据在本地的存储,从而实现将改变反映到它自己的数据(数据重放) ![](https://i.loli.net/2019/01/25/5c4aaf506c4f7.jpg) - 第一步Master记录二进制日志, 每次提交事务完成数据更新前,Master将数据更新的时间记录到二进制日志中,MySql会按事务提交的顺序而非每条语句的执行顺序来记录二进制日志。再记录二进制日志后,主库会告诉存储引擎可以提交事务了。 - 第二步,Slave将Master的二进制日志复制到本地的中继日志中,首先,Slave会启动一个工作线程,成为I/O线程, I/O线程跟Master建立一个普通的客户端链接,然后再Master上启动一个特殊的二进制转储(binlog dump)线程(该线程没有对应的SQL命令),这个二进制转储线程会读取主库上的二进制日志中的事件。从库I/O线程将接受到时间记录到中继日志中。 - 第三步从库的SQL线程执行最后异步,该线程的从中继日志中读取事件并在从库执行,从而实现从库数据更新。 - 这种复制架构实现了获取事件和重放事件的解偶,允许这两个过程异步进行。也就是说I/O线程能够独立于SQL线程之外工作。但这种架构页限制了复制的过程,其中最重要的一点是主库上并发运行的查询再从库只能串行化执行,因为只有一个SQL线程重放中继日志中的事件。这是很多工作负载的性能瓶颈所在。因为始终受限于单线程。 7. **mysql复制类型** - 基于语句的复制 在Master上执行的SQL语句,在Slave上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。一旦发现没法精确复制时,会自动选着基于行的复制 - 基于行的复制 把改变的内容复制到Slave,而不是把命令在Slave上执行一遍。从MySQL5.0开始支持 - 混合类型的复制 默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制 相应地,binlog的格式也有三种:STATEMENT,ROW,MIXED。 8. mysql 主从复制实例 我们在本地搭建主从服务器,首先得启动多个Mysql实例 默认安装的Mysql配置再/etc/mysql/my.cnf,端口3306 那下面我们在启动一个3301端口的实例 复制一份配置 cp /etc/mysql/my.cnf my_3301.cnf 创建一个给3301实例使用的文件夹 mkdir /home/mysql/3301 chown mysql:mysql /home/mysql/ -R 修改配置 [client] port = 3301 socket = /home/mysql/3301/mysqld.sock [mysqld_safe] socket = /home/mysql/3301/mysqld.sock [mysqld] user = mysql pid-file = /home/mysql/3301/mysqld.pid socket = /home/mysql/3301/mysqld.sock port = 3301 basedir = /usr datadir = /var/lib/mysql_3301 tmpdir = /tmp 初始化数据库 sudo mysql_install_db --defaults-file=/etc/mysql/my_3301.cnf --basedir=/usr/ --datadir=/var/lib/mysql_3301 --user=mysql 5.启动MySql, 要指定.cnf文件启动 sudo mysqld_safe --defaults-file=/etc/mysql/my_3301.cnf --user=mysql 看看我们的成果: [/home/mysql]$ ps aux|grep 3301 root 14554 0.0 0.0 75556 1232 ? S 11:26 0:00 sudo mysqld_safe --defaults-file=/etc/mysql/my_3301.cnf --user=mysql root 14555 0.0 0.0 4440 628 ? S 11:26 0:00 /bin/sh /usr/bin/mysqld_safe --defaults-file=/etc/mysql/my_3301.cnf --user=mysql mysql 15159 0.0 0.2 698928 42592 ? Sl 11:28 0:14 /usr/sbin/mysqld --defaults-file=/etc/mysql/my_3301.cnf --basedir=/usr --datadir=/var/lib/mysql_3301 --plugin-dir=/usr/lib/mysql/plugin --user=mysql --log-error=/home/mysql/3301/log/error.log --pid-file=/home/mysql/3301/mysqld.pid --socket=/home/mysql/3301/mysqld.sock --port=3301 lidongw+ 15189 0.0 0.0 107352 1900 pts/1 S+ 11:28 0:00 mysql -S /home/mysql/3301/mysqld.sock -uroot -px xxxx lidongw+ 27141 0.0 0.0 18304 1024 pts/13 S+ 18:42 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=.cvs --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn 3301 登录mysql的shell mysql -S /home/mysql/3301/mysqld.sock -uroot -p123456 远程的加上ip 端口 mysql -S /home/mysql/3301/mysqld.sock -uroot -p123456 -h localhost -P 3307 如果上面启动有问题,那就是apparmor的配置问题: vim /etc/apparmor.d/usr.sbin.mysqld 添加新的Mysql实例上面配置的datadir目录进去,这样才能让实例正确找到目录 /var/lib/mysql_3301/ r, /var/lib/mysql_3301/** rwk, 保存重启 service apparmor restart 至此,已经成功运行多一个监听3301的MySql实例,可以为Mysql复制做准备 作者:大富帅 链接:https://www.jianshu.com/p/c2159fa9e948 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。 ```bash ``` 启动多个Mysql实例 要配置主从复制,我们在本机开多个Mysql实例来操作就可以了,让他们监听不同端口 多开实例可以看我另一篇教程:同一台Ubuntu 启动多个mysql 主从复制配置 现在我们两个实例Mysql server1 : 127.0.0.1 3306 master server2 : 127.0.0.1 3301 slave 配置master 在主库创建一个复制帐号,这个帐号是给从库的IO线程建立连接到主库时用的,从库会用这个帐号连接主库并读取主库的二进制日志: grant replication slave, replication client on *.* to 'repl'@'localhost' identified by '123456'; 主库添加配置: # 设置server_id,一般设置为IP, 要独一无二的 server-id = 10 # 开启二进制日志功能,最好是绝对路径 log_bin = /var/log/mysql/mysql-bin.log # 主从复制的格式(mixed,statement,row,默认格式是statement) binlog_format=mixed # 二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。 expire_logs_days=7 # 为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存 binlog_cache_size=1M # 复制过滤:不需要备份的数据库,不输出(mysql库一般不同步) binlog-ignore-db=mysql 启用二进制日志后,重启后, show master status; 可以看到二进制相关信息 mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000008 | 107 | | | +------------------+----------+--------------+------------------+ 添加从库配置 # 设置server_id,一般设置为IP, 要独一无二的 server-id = 10 log_bin = /var/log/mysql/mysql-bin.log # 中继日志路径 relay_log = /home/mysql/3301/mysql-relay-bin # 允许从库将其重放的事件也记录到自身的二进制日志中 log_slave_updates = 1 read_only = 1 从库开启复制 mysql> CHANGE MASTER TO MASTER_HOST='$host', MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_LOG_FILE='msyql-bin.00001', MASTER_LOG_POS=0; MASTER_LOG_POS设为0,是从日志开头开始复制,MASTER_LOG_FILE是master的二进制文件 # 启动复制 mysql> start slave; # 查看复制状态 mysql> show slave status; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000008 Read_Master_Log_Pos: 107 Relay_Log_File: mysql-relay-bin.000020 Relay_Log_Pos: 253 Relay_Master_Log_File: mysql-bin.000008 Slave_IO_Running: Yes Slave_SQL_Running: Yes Seconds_Behind_Master: 0 Master_Server_Id: 10 # Slave_IO_Running: Yes,Slave_SQL_Running: Yes 说明同步正常进行 # Seconds_Behind_Master: 0 就是完全同步了 这时就完成了主从复制的配置,当主服务器有更新,从库也会更新。 我们还可以从线程列表看出复制线程,主库上可以看到由从库I/O线程向主库发起的连接。 mysql> show processlist \G *************************** 1. row *************************** Id: 44 User: repl Host: localhost:32866 db: NULL Command: Binlog Dump Time: 73032 State: Master has sent all binlog to slave; waiting for binlog to be updated Info: NULL 同样,我们看看从库的线程,有两个,一个I/O线程,一个SQL线程: mysql> show processlist \G *************************** 1. row *************************** Id: 4 User: system user Host: db: NULL Command: Connect Time: 73422 State: Waiting for master to send event Info: NULL *************************** 2. row *************************** Id: 5 User: system user Host: db: NULL Command: Connect Time: 72417 State: Slave has read all relay log; waiting for the slave I/O thread to update it Info: NULL 这两个线程都是再system user 帐号下运行,I/O线程是写日志到中继日志的线程, SQL线程是重放SQL的线程。 从已经运行已久的服务器开始复制 那么,至此我们已经完成了Mysql的主从配置。 但是上面是配置两台刚好安装号的服务器,数据相同,并且知道当前主库二进制日志。 更典型的案例是,一个运行已经一段时间的主库,要用一台新安装的从库与之同步,此时这台从库还没有数据。 所以我们得想办法,线初始化从库: 从主库复制数据、使用最近依次备份来启动从库。 这需要三个条件来让主库和从库保持同步: 复制数据到从库 mysqldump --single-transaction --all-databases --master-data=1 -uroot -p1234 ###### tags: `mysql`