1. MySQL 主从复制概述
1.1 MySQL 主从复制简介
MySQL 数据库的主从复制方案,与使用 scp/rsync 等命令进行的文件级别复制类似,都是数据的远程传输,只不过 MySQL 的主从复制是其自带的功能,无需借助第三方工具,而且,MySQL 的主从复制并不是数据库磁盘上的文件直接拷贝,而是通过逻辑的 binlog 日志复制到要同步的服务器本地,然后由本地的线程读取日志里面的 SQL 语句,重新应用到 MySQL 数据库中。
1.2 MySQL 主从复制架构逻辑图概述
MySQL 数据库支持单向、双向、链式级联、环状等不同业务场景的复制。在复制过程中,一台服务器充当主服务器(Master),接收来自用户的内容更新,而一个或多个其他的服务器充当从服务器(Slave),接收来自主服务器 binlog 文件的日志内容,解析出 SQL,重新更新到从服务器,使得主从服务器数据达到一致。
如果设置了链式级联复制,那么从服务器本身除了充当从服务器外,也会同时充当其下面从服务器的主服务器,链式级联复制类似 A→B→C 的复制形式。
下图为单向主从复制架构逻辑图,此架构只能在 Master 端进行数据写入:
下图为双向主主复制逻辑架构图,此架构可以在 Master1 端或 Master2 端进行数据写入,或者两端同时写入数据(需要特殊设置)。
下图为线性级联单向双主复制逻辑架构图,此架构只能在 Master1 端进行数据写入,工作场景中,Master1 和 Master2 作为主主互备,Slave1 作为从库,中间的 Master2 需要做特殊的设置。
下图为环状级联单向多主同步逻辑架构图,任意一个点都可以写入数据,此架构比较复杂,属于极端环境下的“作品”,一般场景应慎用。
提示:在当前的生产环境中,MySQL 主从复制都是异步的复制方式,即不是严格实时的数据同步,但是正常情况下给用户的体验是实时的。
1.3 MySQL 主从复制的企业应用场景
MySQL 主从复制集群功能使得 MySQL 数据库支持大规模高并发读写成为可能,同时有效地保护了物理服务器宕机场景的数据备份。
应用场景1:从服务器作为主服务器的实时数据备份。
应用场景2:主从服务器实现读写分离,从服务器实现负载均衡。
应用场景3:把多个从服务器根据业务重要性进行拆分访问。
1.4 实现 MySQL 主从读写分离的方案
① 通过程序实现读写分离(性能和效率最佳,推荐):
PHP 和 JAVA 程序都可以通过设置多个连接文件轻松地实现对数据库的读写分离,即当语句关键字为 select 时,就去找读库的连接文件,若为 update、insert、delete 时,则连接写库的连接文件。
通过程序实现读写分离的缺点就是需要开发人员对程序进行改造,使其对下层不透明,但这种方式更容易开发和实现,适合互联网业务场景。
② 通过开源的软件实现读写分离:
MYSQL-proxy、Amoeba 等代理软件也可以实现读写分离,但是这些软件的稳定性和功能一般,不建议生产场景使用。绝大多数公司常用的还是在应用端开发程序实现读写分离。
③ 大型门户独立开发 DAL 层综合软件;
2. MySQL 主从复制原理
2.1 MySQL 主从复制原理介绍
MySQL 的主从复制是一个异步的复制过程(虽然一般情况下感觉是实时的),数据将从一个 MySQL 数据库(Master)复制到另一个 MySQL 数据库(Slave),在 Master 和 Slave 之间实现整个主从复制的过程是由三个线程参与完成的,其中有两个线程(SQL 和 I/O )在 Slave 端,另一个线程(I/O)在 Master 端。
要实现 MySQL 的主从复制,首先必须打开 Master 端的 binlog 记录功能,否则就无法实现,因为整个复制过程实际上就是 Slave 从 Master 获取 binlog 日志,然后再在 Slave 上以相同顺序执行获取的 binlog 日志中所记录的各种 SQL 操作。MySQL 的 binlog 功能在 /etc/my.cnf 中的 [mysqld] 模块下增加 log-bin 参数来实现。
2.2 MySQL 主从复制过程原理详细描述
① 在 Slave 服务器上执行 start slave 命令开启主从复制开关,开始进行主从复制。
② 此时 Slave 服务器的 I/O 线程会通过在 Master 上已经授权的复制用户权限请求连接 Master 服务器,并请求 Master 从指定 binlog 日志文件的指定位置(日志文件名和位置就是在配置主从服务时执行 change master 命令指定的)发送 binlog 日志内容。
③ Master 服务器接收到来自 Slave 服务器的 I/O 线程的请求后,其上负责复制的 I/O 线程会根据 Slave 服务器的 I/O 线程请求的信息分批读取指定 binlog 日志文件指定位置之后的 binlog 日志信息,然后返回给 Slave 端的 I/O 线程,返回的信息中除了 binlog 日志内容外,还有在 Master 服务器端记录的新的 binlog 文件名称,以及在新的 binlog 中的下一个指定更新位置。
④ 当 Slave 服务器的 I/O 线程获取到 Master 服务器上 I/O 线程发送的日志内容、日志文件及位置点后,会将 binlog 日志内容依次写到 Slave 端自身的 Relay Log(中继日志)文件的最末端,并将新的 binlog 文件名和位置记录到 master-info文件中,以便下一次读取 Master 端新 binlog 日志时能够告诉 Master 服务器从新 binlog 日志的指定文件及位置开始请求新的 binlog 日志内容。
⑤ Slave 服务器端的 SQL 线程会实时检测本地 Relay Log 中 I/O 线程新增加的日志内容,然后及时地把 Relay Log 文件中的内容解析成 SQL 语句,并在自身 Slave 服务器上按解析 SQL 语句的位置顺序执行应用这些 SQL 语句,并在 relay-log.info 中记录当前应用中继日志的文件名及位置点。
⑥ 经过了上面的过程,就可以确保在 Master 端和 Slave 端执行了同样的 SQL 语句。当复制状态正常时,Master 端和 Slave 端的数据是完全一样的。当然,MySQL 的复制机制也有一些特殊情况,详情查看官方手册。
2.3 MySQL 主从复制重点小结
① 主从复制是异步的逻辑的 SQL 语句级的复制;
② 复制时,主库有一个 I/O 线程,从库有两个线程,及 I/O 和 SQL 线程;
③ 实现主从复制的必要条件是主库要开启记录 binlog 的功能;
④ 作为复制的所有 MySQL 节点的 server-id 都不能相同;
⑤ binlog 文件只记录对数据内容有更改的 SQL 语句,不记录任何查询语句。
2.4 MySQL 主从复制过程原理逻辑图
3. MySQL 主从复制实战
3.1 数据库环境准备
主从复制的搭建可以用两台 MySQL 服务器或者一台 MySQL 多实例服务器完成,这里以一台有两个实例的 MySQL 数据库服务器来完成。
一般做主从复制,主从服务器在不同的机器上,并且监听的端口均为默认的 3306。
3.2 检查及准备环境
[root@centos ~]# ll /data
drwxr-xr-x 3 mysql mysql 4096 Apr 7 10:12 3306
drwxr-xr-x 3 mysql mysql 4096 Apr 7 17:38 3307
[root@centos ~]# /data/3306/mysql start
[root@centos ~]# /data/3307/mysql start
[root@centos ~]# ss -lnt|grep 330
LISTEN 0 128 *:3306 *:*
LISTEN 0 128 *:3307 *:*
定义监听 3306 端口的实例为 Master 主库。
定义监听 3307 端口的实例为 Slave 从库。
3.3 主库上的操作
[root@centos ~]# vi /data/3306/my.cnf # 编辑 3306 实例的配置文件。
[root@centos ~]# egrep "log-bin|server-id" /data/3306/my.cnf
log-bin = /data/3306/mysql-bin # 打开 binlog 日志功能。
server-id = 1
[root@centos ~]# /data/3306/mysql restart
注意上面两个参数的位置:
[mysqld]
server-id = 1
log-bin = /data/3306/mysql-bin
查看上面配置是否生效:
[root@centos ~]# ll /data/3306/ # 有如下内容表示生效。
-rw-rw---- 1 mysql mysql 126 Apr 7 02:19 mysql-bin.000001
-rw-rw---- 1 mysql mysql 264 Apr 7 03:17 mysql-bin.000002
-rw-rw---- 1 mysql mysql 126 Apr 7 03:52 mysql-bin.000003
-rw-rw---- 1 mysql mysql 252 Apr 7 10:12 mysql-bin.index
[root@centos ~]# mysql -uroot -p'123456' -S /data/3306/mysql.sock -e "show variables like 'log_bin';" # 可以看到 binlog 功能生效。
| Variable_name | Value |
| log_bin | ON |
查看从库配置:
[root@centos ~]# egrep "log-bin|server-id" /data/3307/my.cnf
#log-bin = /data/3307/mysql-bin # 不做级联的话从库 binlog 功能是否开启都不影响主库。
server-id = 3 # 两个 id 要不同。
建立用于同步的账号 rep :
[root@centos ~]# mysql -uroot -p'123456' -S /data/3306/mysql.sock # 登录 3306。
mysql> grant replication slave on *.* to 'rep'@'192.168.136.%' identified by '123456';
mysql> flush privileges ;
提示:replication slave 为 mysql 同步的必须权限,此处不要授权 all。
在主库上做备份:
mysql> flush table with read lock; # 在主库上给所有表加只读锁。
mysql> show master status;
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
| mysql-bin.000009 |335 | | |
mysql> show master logs; # 输出略略略。
[root@centos ~]# mysqldump -uroot -p123456 -S /data/3306/mysql.sock -A -B --events >/opt/rep.sql
[root@centos ~]# mysqldump -uroot -p123456 -S /data/3306/mysql.sock -A -B --events --master-data=2 >/opt/rep.sql
[root@centos ~]# vim /opt/rep.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000009', MASTER_LOG
_POS=335;
mysql> unlock tables; # 打开只读锁。
mysql> show master status; # 备份完毕查看是否有变化,确保锁表成功。
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
| mysql-bin.000009 |335 | | |
提示:上面是官方文档给的备份方式,生产场景中的备份常用 -x 参数锁表,用 --master-data 参数记录 binlog 的文件及位置。
3.4 从库上的操作
把上面的备份数据放入从库
[root@centos ~]# mysql -uroot -p'oldboy123' -S /data/3307/mysql.sock </opt/rep.sql
[root@centos ~]# mysql -uroot -p'oldboy123' -S /data/3307/mysql.sock # 登录到 3307。
mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.136.141',
-> MASTER_PORT=3306,
-> MASTER_USER='rep',
-> MASTER_PASSWORD='123456',
-> MASTER_LOG_FILE='mysql-bin.000009',
-> MASTER_LOG_POS=335;
[root@centos data]# cd /data/3307/data
[root@centos data]# ll
-rw-rw---- 1 mysql mysql 80 Apr 7 18:49 master.info
[root@centos data]# cat master.info # 上面的 change 语句结果会放到 master.info 里面。
18
mysql-bin.000009
335
192.168.136.141
rep
123456
3306
60
mysql> start slave; # 打开从库两个线程的开关。
Query OK, 0 rows affected (0.10 sec)
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
3.5 主从复制配置完成后检查(主库写入数据看从库)
[root@centos ~]# mysql -uroot -p'123456' -S /data/3306/mysql.sock # 登录主库。
mysql> create database oldboy; # 创建 oldboy 库。
[root@centos ~]# mysql -uroot -p'oldboy123' -S /data/3307/mysql.sock # 登录从库。
mysql> show databases; #查看 Master 中的 oldboy 库是否同步到 Slave。(成功)。
| Database |
| information_schema |
| mysql |
| oldboy |
| performance_schema |
| test |
[root@centos 3307]# ll
total 44
drwxr-xr-x 6 mysql mysql 4096 Apr 7 19:06 data
-rw-r--r-- 1 mysql mysql 1901 Apr 6 20:05 my.cnf
-rwx------ 1 root root 1310 Apr 7 03:06 mysql
-rw-rw---- 1 mysql mysql 6 Apr 7 17:38 mysqld.pid
-rw-r----- 1 mysql root 11758 Apr 7 18:57 mysql_oldboy3307.err
srwxrwxrwx 1 mysql mysql 0 Apr 7 17:38 mysql.sock
-rw-rw---- 1 mysql mysql 150 Apr 7 18:57 relay-bin.000001
-rw-rw---- 1 mysql mysql 340 Apr 7 19:06 relay-bin.000002
-rw-rw---- 1 mysql mysql 56 Apr 7 18:57 relay-bin.index
-rw-rw---- 1 mysql mysql 53 Apr 7 19:06 relay-log.info
[root@centos 3307]# mysqlbinlog relay-bin.000002
create database oldboy # 可看到从库里的建库语句。
[root@centos 3307]# cat relay-bin.index
/data/3307/relay-bin.000001
/data/3307/relay-bin.000002
[root@centos 3307]# cat relay-log.info
/data/3307/relay-bin.000002
340 # relay log 的位置。
mysql-bin.000009
422 # 从库 I/O 线程取 binlog 的位置。
[root@centos 3307]# cat data/master.info # 从库 I/O 线程取 binlog 的位置。
mysql-bin.000009
422
4. MySQL 主从复制配置步骤小结
4.1 MySQL 主从复制配置完整步骤如下
① 准备两台数据库环境或单台多实例环境,确定能正常启动和登录。
② 配置 my.cnf 文件:主库配置 log-bin 和 server-id 参数;从库配置 server-id,该值不能和主库及其他从库一样,一般不开启从库 log-bin 功能。注意配置后需要重启服务使之生效。
③ 登录主库,增加从库连接主库同步的账户,例如:rep,并授权 replication slave 同步的权限。
④ 登录主库,整库锁表 flush table with read lock(窗口关闭后即失效,超时参数设置的时间到了也失效);然后 show master status 查看 binlog 的位置状态。
⑤ 新开窗口,Linux 命令行备份或导出原有的数据库数据,并拷贝到从库所在的服务器目录。如果数据量很大,并且允许停机,可停机打包,而不用 mysqldump 。
⑥ 解锁主库,unlock tables;
⑦ 把主库导出的原有数据恢复到从库。
⑧ 根据主库的 show master status 查看 binlog 的位置状态,在从库执行 change master to ... 语句验证 rep 用户。
⑨ 从库开启同步开关,start slave。
⑩ 从库 show slave status\G,检查同步状态,并在主库进行更新测试。
锁表语句知识补充:
5.1 flush tables with read lock
5.5 flush table with read lock
4.2 MySQL 主从复制从库 IO 线程不运行解决方案
https://blog.csdn.net/mqsyoung/article/details/78361469
原因: 首先确认 server-id 是否唯一, mysql 有可能并没有加载 my.cnf 文件中的 server-id。
解决办法:① 修改server-id (在my.cnf 文件中) 。
② mysql> set global server_id=119; #此处的server_id的值和my.cnf里设置的一样。
③ mysql> start slave;
4.3 MySQL 主从复制生产实践及排障
4.3.1 故障一:主库 show master status 没结果
原因:主库 binlog 功能开关没开或没生效。
[root@centos ~]# egrep "server-id|log-bin" /data/3306/my.cnf
log-bin = /data/3306/mysql-bin
server-id = 1
mysql> show variables like 'server_id';
| Variable_name | Value |
| server_id | 1 |
mysql> show variables like 'log_bin';
| Variable_name | Value |
| log_bin | ON |
提示:配置文件里的参数和 show variables 里的参数不一样。
4.3.2 故障二:提示找不到第一个 binlog 文件
mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.136.141',
-> MASTER_PORT=3306,
-> MASTER_USER='rep',
-> MASTER_PASSWORD='123456',
-> MASTER_LOG_FILE='mysql-bin.000009',
-> MASTER_LOG_POS=335;
原因:上面一行,binlog 文件名两端不能有空格。
4.3.3 对主数据库锁表只读(当前窗口不要关掉)
5.1 flush tables with read lock
5.5 flush table with read lock
提示:在 MySQL 5.1 中,锁表的语句是: flush tables with read lock;这个锁表语句的时间,在不同引擎的情况,会受下面参数的控制。锁表时如果超过设置时间不操作,会自动解锁。
interactive_timeout = 60
wait_timeout = 60
mysql> show variables like '%timeout%'; # 查看默认的锁表超时时间。
interactive_timeout | 28800
wait_timeout | 28800
把上面两个参数改为 60 秒:
mysql> set global wait_timeout = 60;
mysql> set global interactive_timeout = 60;
mysql> show variables like '%timeout%'; # 退出重新进入生效。
interactive_timeout | 60
wait_timeout | 60
重启 3306 实例后失效:(因为没有修改配置文件)
mysql> show variables like '%timeout%';
interactive_timeout | 28800
wait_timeout | 28800
由于切换 binlog 导致 show master 位置变化无影响。(数据没有更新)
4.4 回顾 MySQL 主从复制原理要点
① 异步方式同步;
② 逻辑同步模式,多种模式,默认是通过 SQL 语句执行;
③ 主库通过记录 binlog 实现对从库的同步。binlog 记录数据库的更新语句。
④ 主库一个 I/O 线程,从库由一个 I/O 线程和一个 SQL 线程来完成;
⑤ 从库关键文件 master.info 、relay-log 、relay-info 功能。
⑥ 如果从库还想级联从库,需要打开 log-bin 和 log-slave-updates 参数。
4.5 企业场景快速配置 MySQL 主从复制方案
① 安装好要配置从库的数据库,配置好 log-bin 和 server-id 参数;
② 无配置主库 my.cnf 文件,主库的 log-bin 和 server-id 参数默认就是配置好的;
③ 登陆主库增加由于从库连接主库同步的账户如 rep 并授权 replication slave 的权限;
④ 半夜使用 mysqldump 带 --master-data=1 备份的全备数据恢复到从库;
⑤ 在从库执行 change master to ... 语句,无需 binlog 文件及对应位置点。
⑥ 从库开启同步开关,start slave;
⑦ 从库 show slave status\G 检查同步状态,并在主库进行更新测试。
4.6 主从复制过程中的同步状态
4.6.1 查看主库从库同步状态
查看 3306 实例 Master 线程的同步状态:
[root@centos ~]# mysql -uroot -p123456 -S /data/3306/mysql.sock
mysql> show processlist;
Master has sent all binlog to slave; waiting for binlog to be updated
查看 3307 实例 Slave 线程的同步状态:
[root@centos ~]# mysql -uroot -p123456 -S /data/3307/mysql.sock
mysql> show processlist;
Slave has read all relay log; waiting for the slave I/O thread to update it
Waiting for master to send event
4.6.2 复制主线程状态
以下是主服务器的线程最常见状态。如果在主副武器上没有看到任何 binlog dump 线程说明复制没有运行,即没有连接任何从服务器。
Sending binlog event to slave
表示线程已经从二进制日志读取了一个事件并且正在将它发送到从服务器。
Finished reading one binlog;swiching to next binlog
表示线程已经读完二进制日志文件并且正打开下一个要发送到从服务器的日志文件。
Has sent all binlog to slave;waiting for binlog to be updated
表示线程已经把所有更新的 binlog 内容发给从库,目前正在等待 binlog 的更新。
Waiting to finalize termination
表示线程停止时发生的一个很简单的状态。
4.6.3 复制从 I/O 线程状态
Connection to master
线程正试图连接主服务器。
Checking master version
建立主从服务器之间的连接后立即临时出现的状态。
Registering slave on master
建立主从服务器之间的连接后立即临时出现的状态。
Registering binlog dump
建立主从服务器之间的连接后立即临时出现的状态。线程向主服务器发送一条请求,索取从请求的二进制日志文件名和位置开始的二进制日志的内容。
Waiting to reconnect after a failed binlog dump request
如果二进制日志转储请求失败(由于没有连接),线程进入睡眠状态,然后定期尝试重新连接,可以使用 --master-connect-retry 选项指定重试之间的间隔。
Reconnecting after a failed binlog dump request
线程正尝试重新连接服务器。
Waiting for master to send event
线程已经连接上主服务器,正等待二进制日志事件到达。如果主服务器正空闲,会持续较长的时间,如果等待持续 slave_read_timeout 秒,则发生超时。此时,线程认为连接被中断并企图重新连接。
Queueing master event to the relay log
线程已经读取一个事件,正将它复制到中继日志供 SQL 线程来处理。
4.6.4 复制从 SQL 线程状态
Reading event from the relay log
线程已经从中继日志读取一个事件,可以对事件进行处理了。
Slave has read all relay log; waiting for the slave I/O thread to update it
线程已经处理了中继日志文件中的所有事件,现在正等待 I/O 线程将新事件写入中继日志。
Waiting for slave mutex on exit
线程停止时发生的一个很简单的状态。
4.7 查看 MySQL 线程同步状态的用途
通过 MySQL 线程同步状态查看数据库同步是否完成,用于主库宕机或者人工数据库主从切换迁移等。主库宕机选择最快的从库提升为主,就需要查看线程状态,当然也可以利用 MySQL 的半同步功能,选择固定的库提升为主。
[root@centos ~]# mysql -uroot -p123456 -S /data/3307/mysql.sock
mysql> show slave status\G
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.136.141
Master_User: rep
Master_Port: 3306
Connect_Retry: 60 # 连接重试。
Master_Log_File: mysql-bin.000011
Read_Master_Log_Pos: 107
Relay_Log_File: relay-bin.000008
Relay_Log_Pos: 253
Relay_Master_Log_File: mysql-bin.000011
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
# 上面两个参数的 yes 表示同步的状态是 OK 的,但是是否有延迟等无法体现出来。
Last_Errno: 0 # 如果报错,该参数会有数字提示码。
Last_Error:
Seconds_Behind_Master: 0 # 从库落后于主库的秒数。参考,不绝对准确。
Master_SSL_Verify_Server_Cert: No
Master_Server_Id: 1
5. MySQL 常见错误码及解释
1005:MYSQL创建表失败
1006:MYSQL创建数据库失败
1007:MYSQL数据库已存在,创建数据库失败
1008:MYSQL数据库不存在,删除数据库失败
1009:MYSQL不能删除数据库文件导致删除数据库失败
1010:MYSQL不能删除数据目录导致删除数据库失败
1011:MYSQL删除数据库文件失败
1012:MYSQL不能读取系统表中的记录
1016:文件无法打开,使用后台修复或者使用 phpmyadmin 进行修复。
1017:服务器非法关机,导致该文件损坏。
1020:MYSQL记录已被其他用户修改
1021:硬盘剩余空间不足,请加大硬盘可用空间
1022:MYSQL关键字重复,更改记录失败
1023:MYSQL关闭时发生错误
1024:MYSQL读文件错误
1025:MYSQL更改名字时发生错误
1026:MYSQL写文件错误
1030:可能是服务器不稳定。(具体原因不是很清楚)
1032:MYSQL记录不存在
1036:MYSQL数据表是只读的,不能对它进行修改
1037:系统内存不足,请重启数据库或重启服务器
1038:MYSQL用于排序的内存不足,请增大排序缓冲区
在my.ini 修改为max_connections=1000或更大,重启mysql
1041:系统内存不足
1042:无效的主机名
1043:无效连接
1044:MYSQL当前用户没有访问数据库的权限
1045:MYSQL不能连接数据库,服务器、数据库名、用户名或密码错误
6. MySQL 主从复制读写分离授权多种方案
6.1 生产授权方案一
主库:web oldboy123 10.0.0.1 3306 (select、insert、delete、update)
从库:将主库的 web 用户同步到从库,然后回收 insert、delete、update 权限。
不收回权限的解决办法:
不收回从库权限,在 my.cnf 里面设置 read-only 参数或指定 read-only 只读启动。
开发登陆数据库(主从的用户密码一致):
web oldboy123 10.0.0.1 3306 (select、insert、delete、update)
web oldboy123 10.0.0.2 3306 (select)
6.2 生产授权方案二
主库:web_w oldboy123 10.0.0.1 3306 (select、insert、delete、update)
从库:web_r oldboy123 10.0.0.2 3306 (select)
风险:web_w 用户连接从库写数据!
解决上述风险的方法:
设置 read-only 参数或指定 read-only 只读。
开发登陆数据库:多套用户密码,不专业。
生产授权方案三:【推荐】
不同步 mysql 库:主从库分别进行如下授权:
主库:web oldboy123 10.0.0.1 3306 (select、insert、delete、update)
从库:web oldboy123 10.0.0.2 3306 (select)
从库设置 read-only 参数读,增加双保险。
缺陷:
当从库切换主库时,连接用户会有权限问题。
解决方法:(百度架构方案)
保留一个从库专门准备接替主库。
6.3 忽略 MySQL 主从复制主库授权表同步实战
[root@centos ~]# vi /data/3306/my.cnf # 添加如下四行。
#replicate-ignore-db = mysql
#binlog-ignore-db = mysql
#binlog-ignore-db = performance_schema
#binlog-ignore-db = information_schema
[root@centos ~]# mysql -uroot -p123456 -S /data/3306/mysql.sock
mysql> create user oldgirl@localhost identified by 'old123';
# 登录 3306 创建用户 oldgirl@localhost 。
[root@centos ~]# mysql -uroot -p123456 -S /data/3307/mysql.sock
mysql> select user,host from mysql.user;
oldgirl | localhost
# 登录 3307 查看用户: oldgirl@localhost 存在【说明主从同步了】。
去掉上面的注释,并重启 3306 数据库:
[root@centos ~]# grep ignore-db /data/3306/my.cnf
replicate-ignore-db = mysql
binlog-ignore-db = mysql
binlog-ignore-db = performance_schema
binlog-ignore-db = information_schema
[root@centos ~]# /data/3306/mysql restart
Stopping MySQL...
Starting MySQL...
此时,在主库上创建新的用户后,再到从库查看,新建的用户不会同步。
mysql> show slave status\G # 确定是在主从同步正常的前提下不同步用户的。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
6.4 Replication 中可通过以下选项减少 binlog 数据量
Master 端:
--binlog-do-db 二进制日志记录的数据库(多个库用逗号分隔)。
--binlog-ignore-db 二进制日志忽略的数据库(多个库用逗号分隔)。
Slave 端:
--replication-do-db 设定需要复制的数据库。
--replication-ignore-db 设定忽略复制的数据库。
--replication-do-table 设定需要复制的数据表。
--replication-ignore-table 设定忽略复制的数据表。
--replication-wild-do-table 同 --replication-do-table ,但是可加通配符。
--replication-wild-ignore-table 同 --replication-ignore-table ,但是可加通配符。
通过 read-only 参数防止数据写从库的方法:
在从库上设置 read-only 参数或者直接带 --read-only 参数启动或重启数据库。
① 在 my.cnf 里 [mysqld] 模块下加 read-only 参数并重启数据库:
[mysqld]
read-only
② 直接带 --read-only 参数启动或重启数据库:
mysqld_safe --default-file=/data/3307/my.cnf --read-only &
测试从库是否只读:
[root@centos ~]# mysql -uroot -p123456 -S /data/3306/mysql.sock # 登录 3306.
mysql> show databases;
| Database |
| information_schema |
| mysql |
| oldboy |
| performance_schema |
| test |
mysql> drop database test; # 删除 test 库。
mysql> Bye
[root@centos ~]# mysql -uroot -p123456 -S /data/3307/mysql.sock # 登录 3307 。
mysql> show databases; # 可看到 test 库已删除(表示同步正常)。
| Database |
| information_schema |
| mysql |
| oldboy |
| performance_schema |
mysql> create database test; # 在从库可建库,这是为什么?!
Query OK, 1 row affected (0.00 sec)
提示:由于从库设置了 read-only,非 SUPER 权限是无法写入的,root 用户有 SUPER 权限,所以依旧可以在配置 read-only 的从库写入数据。创建非 SUPER 权限的用户测试:
mysql> grant select,insert,update,delete on *.* to leilei@localhost identified by '123456';
# 创建用户授权增删改查。
mysql> flush privileges;
[root@centos ~]# mysql -uleilei -p123456 -S /data/3307/mysql.sock
mysql> select user();
| user() |
| leilei@localhost |
mysql> use test;
mysql> create table t(id int); # 无法建表(提示 read-only)
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
测试结论:
① 若想要从库只读,前提是主从同步正常。
② 在 my.cnf 里的 [mysqld] 模块下加入 read-only 参数后重启服务才能生效。
③ root 用户及 ALL 权限的用户可以插入及删除数据。
④ 具备 insert、update、delete 权限的用户无法通过 read-only 的限制。
7. 生产案例:MySQL 从库数据冲突导致同步停止
7.1 模拟错误,重现问题
[root@centos ~]# mysql -uroot -p123456 -S /data/3307/mysql.sock # 登录从库。
mysql> create database butongbu; # 创建 butongbu 库。
mysql> show databases;
| butongbu |
| mysql |
| oldboy |
mysql> show slave status\G # 检查同步状态(正常)。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
[root@centos ~]# mysql -uroot -p123456 -S /data/3306/mysql.sock
mysql> create database butongbu; # 在主库创建同名的库。
Query OK, 1 row affected (0.00 sec)
登录从库即可看到出现的错误:
[root@centos ~]# mysql -uroot -p123456 -S /data/3307/mysql.sock
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: No
Last_Errno: 1007
Last_Error: Error 'Can't create database 'butongbu'; database exists' on query. Default database: 'butongbu'. Query: 'create database butongbu'
MySQL 错误代号1007: MYSQL数据库已存在,创建数据库失败。
7.2 解决上述问题
7.2.1 方法一:添加忽略冲突次数为 1 忽略
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> set global sql_slave_skip_counter = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G # 可以看到主从同步恢复正常了。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Last_Errno: 0
① 对于普通的互联网业务,忽略问题不是很大,当然要确认不影响公司业务的前提下。
② 企业场景解决主从同步,比主从不一致更重要。如果主从数据一致也很重要,再找时间恢复从库。
③ 主从数据不一致和保持主从同步持续状态哪个更总要需要根据不同业务选择。
7.2.2 方法二:通过忽略错误号跳过指定的错误
[root@centos ~]# vi /data/3307/my.cnf
[root@centos ~]# grep slave-skip /data/3307/my.cnf
slave-skip-errors = 1032,1062,1007
[root@centos ~]# /data/3307/mysql stop
[root@centos ~]# /data/3307/mysql start
此时主库创建从库已经存在的数据库,就不会产生不同步的问题(忽略 1007 错误)。
8. 从库开启 binlog 原因及开启实战讲解
8.1 需要记录 binlog 的情况
① 当前主库还要作为其他从库的主库,也就是级联同步,需要开启 binlog;
② 把从库作为备份服务器时需要开启 binlog。
8.2 实践操作:在从库添加如下参数
[root@centos ~]# vi /data/3307/my.cnf
#log-bin = /data/3307/mysql-bin
#log-slave-updates
#expire_logs_days = 7
8.3 重启生效:
[root@centos ~]# /data/3307/mysql stop
[root@centos ~]# /data/3307/mysql start
9. 企业场景一主多从主库宕机,切换从库为主库继续主从同步过程详解
9.1 主库 master 宕机,登陆从库查看两个线程的更新状态
mysql> show processlist;
State:Waiting for master to send event # IO 线程。
State:Has read all relay log;waiting for the slave I/O thread to update it # SQL 线程。
提示:上面的状态说明该从库是和主库同步的,或者说是最新的。
9.2 工作中确定将哪个从库提升为主库的方法
cat /data/3307/data/master.info
cat /data/3308/data/master.info
① 确保更新完毕,看看从库哪个最快,经过测试没有延迟的情况 POS 差距很小,甚至是一致的。通过查看 master.info 的内容来确定将哪个(数据最新,跟主库一致性更高的)从库提升为主库。
② 其他方法:利用半同步的功能,直接选择做了实时同步的从库(太子)。
③ 提示:如果主库只是服务宕机,服务器正常,可以将主库服务器的 binlog 拉倒要提升为主库的从库,用来补全数据。
9.3 开始切换选择的从库为主库
① 确保所有 relay log 全部更新完毕:
在每个从库上执行 stop slave io_thread;show processlist;直到看到 Slave has read all relay log 表示从库更新都执行完毕。
② 登录选定的从库执行如下操作:
stop slave;
retset master;
quit;
还要授权同步用户 rep 和原本来的主库一样。
提示:如果提升为主库的从库存在忽略授权表,read-only 等设置,需要清理这些设置。
③ 进入到数据库目录,删除 master.info、relay-log.info
提示:检查授权表,read-only 等参数。
④ 编辑该从库的配置文件:
开启 log-bin 参数,如果存在 log-slave-updates、read-only 等一定要注释掉,重启后提升主库完毕。
⑤ 如果主库服务器没有宕机,需要去主库拉取 binlog 补全提升主库的从库数据。
⑥ 其他从库操作:
前提检查提升为主库的从库中同步用户 rep 已经存在。
登录从库:
stop slave;
change master to master_host = '192.168.136.152'; # 如果数据不同步就指定位置点。
start slave;
show slave status\G
⑦ 修改程序配置文件从主库指向从库、平时访问数据库用域名则可直接改 hosts 解析。
⑧ 修改损坏的主库,作为从库或重新切换为主库。
10. MySQL 5.5 以上支持半同步
半同步下的一主多从恢复,直接对设置半同步的从库确定为主库。让某一个稳定的从库和主库完全一致,即主库和这个从库都更新数据完毕后再返回给用户更新数据成功的消息。
半同步的优点:
确保至少一个从库和主数据库一致。
半同步的缺点:
主从之间网络延迟,或者从库有问题的时候会降低用户体验。(可设置超时时间为10秒)。