MySQL主从复制概述
在实际生产中,数据的重要性不言而喻
如果我们的数据库只有一台服务器,那么很容易产生单点故障的问题,比如这台服务器访问压力过大而没有响应或者奔溃,那么服务就不可用了,再比如这台服务器的硬盘坏了,那么整个数据库的数据就全部丢失了,这是重大的安全事故.
为了避免服务的不可用以及保障数据的安全可靠性,我们至少需要部署两台或两台以上服务器来存储数据库数据,也就是我们需要将数据复制多份部署在多台不同的服务器上,即使有一台服务器出现故障了,其他服务器依然可以继续提供服务.
MySQL提供了主从复制功能以提高服务的可用性与数据的安全可靠性.
主从复制是指服务器分为主服务器和从服务器,主服务器负责读和写,从服务器只负责读,主从复制也叫 master/slave,master是主,slave是从,但是并没有强制,也就是说从也可以写,主也可以读,只不过一般我们不这么做。
主从复制可以实现对数据库备份和读写分离
MySQL的主从复制架构
一主多从架构
双主双从结构
MySQL主从复制原理
-
当主库服务器上的数据发生改变时,则将其改变写入二进制事件日志文件中
-
从库服务器会在一定时间间隔内对主库服务器上的二进制日志进行探测,探测其是否发生过改变,如果探测到主库服务器的二进制事件日志发生了改变,则开始一个 I/O Thread 请求 master 二进制事件日志
-
同时主库服务器为每个 I/O Thread 启动一个dump Thread,用于向其发送二进制事件日志
-
从库服务器将接收到的二进制事件日志保存至自己本地的中继日志文件(relaylog)中
-
从库服务器将启动 SQL Thread 从中继日志中读取二进制日志,在本地重放,使得其数据和主服务器保持一致;
-
最后 I/O Thread 和 SQL Thread 将进入睡眠状态,等待下一次被唤醒
主从复制的过程会有很小的延迟,基本没有影响
MySQL多实例搭建
MySQL的多实例就是指安装了MySQL之后,在一台Linux机器上启动多个MySQL实例(主要用于学习)。
多实例配置
-
安装mysql,下载mysql的安装包之后,解压。
-
创建mysql用户和用户组
1 2 3
groupadd mysql useradd -r -g mysql mysql chown -R mysql.mysql /usr/local/mysql5.7
-
创建数据目录用于存放mysql多实例数据。
1 2 3 4
mkdir -p /usr/local/mysql5.7/data/3307 mkdir -p /usr/local/mysql5.7/data/3308 mkdir -p /usr/local/mysql5.7/data/3309 mkdir -p /usr/local/mysql5.7/data/3310
-
在mysql的解压目录下的bin目录下执行命令:
1 2 3 4 5 6 7
./mysqld --initialize --console --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data/3307 --user=mysql ./mysqld --initialize --console --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data/3308 --user=mysql ./mysqld --initialize --console --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data/3309 --user=mysql ./mysqld --initialize --console --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data/33010 --user=mysql
注意:–initialize 会随机生成root用户的密码。–console控制台输出初始化日志。这就可以看到生成的root密码。–user指定运行MySQL实例的用户
-
在各自的数据目录下创建配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#================3307端口================ [client] port = 3307 socket = /usr/local/mysql5.7/data/3307/mysql.sock default-character-set=utf8 [mysqld] port = 3307 socket = /usr/local/mysql5.7/data/3307/mysql.sock datadir = /usr/local/mysql5.7/data/3307 log-error = /usr/local/mysql5.7/data/3307/error.log pid-file = /usr/local/mysql5.7/data/3307/mysql.pid character-set-server=utf8 lower_case_table_names=1 autocommit = 1 #================3308端口================ [client] port = 3308 socket = /usr/local/mysql5.7/data/3308/mysql.sock default-character-set=utf8 [mysqld] port = 3308 socket = /usr/local/mysql5.7/data/3308/mysql.sock datadir = /usr/local/mysql5.7/data/3308 log-error = /usr/local/mysql5.7/data/3308/error.log pid-file = /usr/local/mysql5.7/data/3308/mysql.pid character-set-server=utf8 lower_case_table_names=1 autocommit = 1 #================3309端口================ [client] port = 3309 socket = /usr/local/mysql5.7/data/3309/mysql.sock default-character-set=utf8 [mysqld] port = 3309 socket = /usr/local/mysql5.7/data/3309/mysql.sock datadir = /usr/local/mysql5.7/data/3309 log-error = /usr/local/mysql5.7/data/3309/error.log pid-file = /usr/local/mysql5.7/data/3309/mysql.pid character-set-server=utf8 lower_case_table_names=1 autocommit = 1 #================3310端口================ [client] port = 3310 socket = /usr/local/mysql5.7/data/3310/mysql.sock default-character-set=utf8 [mysqld] port = 3310 socket = /usr/local/mysql5.7/data/3310/mysql.sock datadir = /usr/local/mysql5.7/data/3310 log-error = /usr/local/mysql5.7/data/3310/error.log pid-file = /usr/local/mysql5.7/data/3310/mysql.pid character-set-server=utf8 lower_case_table_names=1 autocommit = 1
启动多实例
在MySQL的安装目录下bin目录执行
|
|
登录实例
|
|
修改密码
alter user 'root'@'localhost' identified by '123456;'
开启远程访问许可
grant all privileges on *.* to root@'%' identified by '123456';
其中*.* 的第一个表示所有数据库名,第二个表示所有的数据库表
root@’%’ 中的root表示用户名
%表示所有ip地址,%也可以指定具体的ip地址,比如root@localhost,root@192.168.10.129。
刷新权限
flush privileges;
多实例关闭
|
|
MySQL一主多从架构搭建
配置文件
在MySQL主服务器(3307)配置文件my.cnf里面加入
log-bin=mysql-bin #表示启用二进制日志
server-id=3307 #表示server编号,编号要唯一
在MySQL从服务器(3308)配置文件my.cnf里面加入
server-id=3308
在MySQL从服务器(3309)配置文件my.cnf里面加入
server-id=3309
在MySQL从服务器(3310)配置文件my.cnf里面加入
server-id=3310
主库配置
-
登录,
./mysql -uroot -p -P3307 -h127.0.0.1
-
创建复制数据的账户并且授权:
grant replication slave on *.* to 'copy'@'%' identified by '123456';
-
查看主库状态
mysql主库状态默认值:
File:mysql-bin.000001
。Position:154
。因为执行过授权语句,所以偏移量会变化。所以需要充值一下master的偏移量。
从库配置
-
查看从库状态:
show slave status \G;
结果会是:Empty set (0.00 sec)。从库的初始状态。如果不是需要重置:reset slave;
-
设置监听的主库并开始执行复制
1 2 3
change master to master_host='192.168.79.150',master_user='copy',master_port=3307,master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=154; start slave;
master_host :Master的IP地址
master_port:Master的端口号,指的是容器的端口号
master_user:用于数据同步的用户
master_password:用于同步的用户的密码
master_log_file:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值
master_log_pos:从哪个 Position 开始读,即上文中提到的 Position 字段的值
-
查看从库状态:
show slave status;
Slave_IO_Running
和Slave_SQL_Running
的值为yes说明slave开启成功1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
mysql> show slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.79.150 Master_User: copy Master_Port: 3307 Connect_Retry: 60 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 154 Relay_Log_File: dev1-relay-bin.000002 Relay_Log_Pos: 320 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 154 Relay_Log_Space: 526 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: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 3307 Master_UUID: be3a5680-8fdd-11eb-897a-000c297b3e36 Master_Info_File: /usr/local/mysql5.7/data/3309/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec)
测试
-
连接上主库,在主库创建一个数据库,再在此库里面创建一个表并写入一下数据。
-
为了方便可以使用Navicat之类的软件进行操作
-
查看在主库创建的表数据库和表及其数据是否存在。数据存在主从模式搭建成功
-
查看一下主库的binlong:
show binlog events in 'mysql-bin.000001'\G;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
mysql> show binlog events in 'mysql-bin.000001'\G; *************************** 1. row *************************** Log_name: mysql-bin.000001 Pos: 4 Event_type: Format_desc Server_id: 3307 End_log_pos: 123 Info: Server ver: 5.7.24-log, Binlog ver: 4 *************************** 2. row *************************** Log_name: mysql-bin.000001 Pos: 123 Event_type: Previous_gtids Server_id: 3307 End_log_pos: 154 Info: *************************** 3. row *************************** Log_name: mysql-bin.000001 Pos: 154 Event_type: Anonymous_Gtid Server_id: 3307 End_log_pos: 219 Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS' *************************** 4. row *************************** Log_name: mysql-bin.000001 Pos: 219 Event_type: Query Server_id: 3307 End_log_pos: 368 Info: CREATE DATABASE `shop` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci' *************************** 5. row *************************** Log_name: mysql-bin.000001 Pos: 368 Event_type: Anonymous_Gtid Server_id: 3307 End_log_pos: 433 Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS' *************************** 6. row *************************** Log_name: mysql-bin.000001 Pos: 433 Event_type: Query Server_id: 3307 End_log_pos: 628 Info: use `shop`; CREATE TABLE `shop`.`order` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NULL, PRIMARY KEY (`id`) ) *************************** 7. row *************************** Log_name: mysql-bin.000001 Pos: 628 Event_type: Anonymous_Gtid Server_id: 3307 End_log_pos: 693 Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS' *************************** 8. row *************************** Log_name: mysql-bin.000001 Pos: 693 Event_type: Query Server_id: 3307 End_log_pos: 765 Info: BEGIN *************************** 9. row *************************** Log_name: mysql-bin.000001 Pos: 765 Event_type: Table_map Server_id: 3307 End_log_pos: 816 Info: table_id: 109 (shop.order) *************************** 10. row *************************** Log_name: mysql-bin.000001 Pos: 816 Event_type: Write_rows Server_id: 3307 End_log_pos: 862 Info: table_id: 109 flags: STMT_END_F *************************** 11. row *************************** Log_name: mysql-bin.000001 Pos: 862 Event_type: Xid Server_id: 3307 End_log_pos: 893 Info: COMMIT /* xid=94 */ *************************** 12. row *************************** Log_name: mysql-bin.000001 Pos: 893 Event_type: Anonymous_Gtid Server_id: 3307 End_log_pos: 958 Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS' *************************** 13. row *************************** Log_name: mysql-bin.000001 Pos: 958 Event_type: Query Server_id: 3307 End_log_pos: 1030 Info: BEGIN *************************** 14. row *************************** Log_name: mysql-bin.000001 Pos: 1030 Event_type: Table_map Server_id: 3307 End_log_pos: 1081 Info: table_id: 109 (shop.order) *************************** 15. row *************************** Log_name: mysql-bin.000001 Pos: 1081 Event_type: Write_rows Server_id: 3307 End_log_pos: 1126 Info: table_id: 109 flags: STMT_END_F *************************** 16. row *************************** Log_name: mysql-bin.000001 Pos: 1126 Event_type: Xid Server_id: 3307 End_log_pos: 1157 Info: COMMIT /* xid=97 */ 16 rows in set (0.00 sec)
-
查看slave的状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
mysql> mysql> show slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.79.150 Master_User: copy Master_Port: 3307 Connect_Retry: 60 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 1157 Relay_Log_File: dev1-relay-bin.000002 Relay_Log_Pos: 1323 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 1157 Relay_Log_Space: 1529 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: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 3307 Master_UUID: be3a5680-8fdd-11eb-897a-000c297b3e36 Master_Info_File: /usr/local/mysql5.7/data/3308/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec)
注意事项
在主库写入数据,数据会同步到从库中,但是当在从库写入一条数据时并不会同步到其他从库和主库中。当一个表的数据的主键是自增的。当主库插入数据时,单独在从库写入的数据和主库同步过来的数据的主键发生冲突时,该从库的数据不会显示主库同步过来的数据。此时从库的状态中:
|
|
查看从库的状态。Last_Error字段就会报错。
解决方法:
- 删掉从库上与主库冲突的数据。然后停止
stop slave;
,再重启start slave;
。这时候就会重新生成中继文件,重新同步数据。 - 删除掉从库上的整个表或数据库,重新添加主库。不过这样的操作会复制很多的数据,毕竟整个表和数据库都被删除了。
MySQL多主多从架构搭建
上面一主多从的架构,如果主库的服务器宕机了,写操作就会完成不了,读操作时可以的(因为一般MySQL的主从架构就是用于读写分离的)。这样一个主库的架构就会出现单点故障了。
3307端口的MySQL为主库,3309端口的MySQL为其从库
3308端口的MySQL为主库,3310端口的MySQL为其从库
3307端口的MySQL和3309端口的MySQL护卫主从
配置文件的配置
在第一台主服务器3307端口的MySQL的my.cnf文件增加如下配置
|
|
在第二台主服务器3308端口的MySQL的my.cnf文件增加如下配置
|
|
配置项说明
-
auto_increment_increment
控制主键自增的自增步长,用于防止Master与Master之间复制出现重复自增字段值,通常auto_increment_increment=n,有多少台主服务器,n 就设置为多少
-
auto_increment_offset=1
设置自增起始值,这里设置为1,这样Master的auto_increment字段产生的数值是:1, 3, 5, 7, …等奇数ID
auto_increment_offset的设置,不同的master设置不应该一样,否则就容易引起主键冲突,比如master1的offset=1,则master2的offset=2,master3的offset=3
-
log-slave-updates
在双主模式中,log-slave-updates 配置项一定要配置,否则在master1(3307端口)上进行了更新数据,在master2(3308端口)和slave1(3309端口)上会更新,但是在slave2(3310端口)上不会更新
-
sync_binlog
表示每几次事务提交,MySQL把binlog缓存刷进日志文件中,默认是0,最安全的是设置为1。
sync_binlog=0,当事务提交之后,MySQL不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让Filesystem自行决定什么时候来做同步,或者cache满了之后才同步到磁盘
sync_binlog=n,当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘
注意事项
从库只开启log-bin功能,不添加log-slave-updates参数,从库从主库复制的数据不会写入log-bin日志文件里。开启log-slave-updates参数后,从库从主库复制的数据会写入log-bin日志文件里。这也是该参数的功能,直接向从库写入数据时,是会写入log-bin日志的。
在自动生成主键的时候,会在已生成主键的基础上按照规则生成,即比存在的值大。
主从库的配置
-
启动分别启动MySQL
1 2 3 4 5 6 7
./mysqld_safe --defaults-file=/usr/local/mysql5.7/data/3307/my.cnf & ./mysqld_safe --defaults-file=/usr/local/mysql5.7/data/3308/my.cnf & ./mysqld_safe --defaults-file=/usr/local/mysql5.7/data/3309/my.cnf & ./mysqld_safe --defaults-file=/usr/local/mysql5.7/data/3310/my.cnf &
-
在3308端口的MySQL里执行:
grant replication slave on *.* to 'copy'@'%' identified by '123456';
由于3307端口的MySQL已经执行过。 -
重置Master:
reset master;
不重置也可以,再从库监听主库的时候,制定好biglog的名字就好。重置后都是:mysql-bin.000001
。 -
在3308端口的MySQL执行,因为3308端口、3309端口和3310端口在上面设置过主从配置。
1 2
stop slave; reset salve;
-
从库监听主库
1 2 3 4 5 6 7 8 9
#==============在3307端口和3310端口的MySQL里执行=============== change master to master_host='192.168.79.150',master_user='copy',master_port=3308,master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=154; start slave; #==============在3308端口和3309端口的MySQL里执行=============== change master to master_host='192.168.79.150',master_user='copy',master_port=3307,master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=154; start slave;
测试
跟上一节的一主多从的测试一样。
代码实战:多数据源配置
当我们的MySQL集群搭建好了之后,那就要在代码里面使用了。一般我们在写代码时,数据源都是只设置了一个。现在这么多个数据源,又要怎么使用呢!
有两种情况:1、做读写分离。2、只操作主库,从库只是当作备份。
方案一:MyBatis的Mapper包隔离
核心原理
基于Mapper包的隔离,每个Mapper包操作不同的数据库,每个Mapper包对应一个数据库。
代码实现
pom.xml:
|
|