Mysql主从复制实现读写分离

在网站访问量越来越大时,我们都数据库压力也会越来越大,此时,单机mysql渐渐承受不住越来越大的访问压力,满足不了我们的业务需求。此时我们便需要搭建mysql集群,以应对我们的访问压力。 在普遍的网站业务中,更多的数据库访问是读数据,而写数据是相对(读数据)较少的。例如一个电商网站,我们逛的淘宝,一般我们会看好多个商品的详情,评论,参数(读数据),货比三家,最终选自己心仪的商品下单(写数据)。可以看到,大部分时间,我们用户都是在读数据。所以主从复制,读写分离的数据库集群方案应运而生。 我们搭建多个数据库实例,只有一个主节点实例负责写入数据,而多个从节点实例负责提供读取数据,从而达到负载均衡,减轻单个节点的读数据压力。而数据的写入往往相对比较少,且可以通过消息队列来为数据写入削峰,所有的读数据都访问从节点实例,同样也减轻了主节点的压力(主节点只负责写数据)。 ## 一 、 主从复制的原理 #### 1、复制原理 mysql的二进制日志(binlog)会记录下所有mysql的数据修改语句,包括所有的 `DDL` (Data Definition Language)数据定义语句 和 `DML` (Data Manipulation Language)数据操纵语句(注意查询语句:select,show 语句不会记录),如:insert,update,delete,create/alter/drop table, grant等等语句,都会记录在binlog中。 主从复制其实就是把主节点的binlog复制到各个从节点中,各个从节点依照binlog都执行一遍,这样从节点的数据就和主节点的数据就相同了。 #### 2、复制流程 [![](http://static.tscgo.cn/FpQA32Y3RaaYXlpTk-xOZLlz3jVU)](http://static.tscgo.cn/FpQA32Y3RaaYXlpTk-xOZLlz3jVU) 1. 主节点开启binlog,所有的数据修改sql句都记录到binlog中。 2. 从节点开启一个线程(I/O Thread)把自己扮演成mysql客户端,通过mysql协议,连接主节点,发送自己最后一次执行的binlog文件和位置。 3. 主节点启动一个线程(dump Therad)检查自己二进制日志中的事件,跟对方请求的位置对比,如果不带请求位置参数,则主节点就会从第一个日志文件中的第一个事件一个一个发送给从节点。 4. 从节点接收到主节点发送过来的数据把它放置到中继日志(Relay log)文件中。并记录该次请求到主节点的具体哪一个二进制日志文件内部的哪一个位置(主节点中的二进制文件会有多个,在后面详细讲解)。 5. 从节点启动另外一个线程(sql Thread ),把 Relay log 中的事件读取出来,并在本地再执行一次。 #### 3,复制中各个线程的作用 从节点: 1. I/O Thread: 从 Master 节点请求二进制日志事件,并保存于中继日志中。 2. Sql Thread: 从Relay log 中读取日志事件并在本地完成重放。 主节点: 1. Dump Thread:为每个 Slave 的 I/O Thread 启动一个 dump 线程,用于向从节点发送二进制事件。 **思考:**从节点需要建立二进制日志文件吗? 看情况,如果从节点需要作为其他节点的主节点时,是需要开启二进制日志文件的。这种情况叫做级联复制。如果只是作为从节点,则不需要创建二进制文件。 ## 二、主从复制的配置 主节点(192.168.249.128): 1. 启用二进制日志。 2. 为当前节点设置一个全局唯一的server_id。 3. 创建有复制权限的用户账号 REPLIACTION SLAVE ,REPLIATION CLIENT。 从节点(192.168.249.130): 1. 启动中继日志。 2. 为当前节点设置一个全局唯一的server_id。 3. 使用有复制权限的用户账号连接至主节点,并启动复制线程。 #### 1、主节点配置 1,编辑主节点配置文件`/etc/my.cnf`,添加如下配置,用于开启binlog。注意:下面👇配置必须写在`[mysqld]`模块下 ```` log-bin = mysql-bin server-id =1 innodb-file-per-table =ON skip_name_resolve=ON ```` 2,重启mysql,登录mysql客户端,查看binlog是否开启成功: ```shell [root@node-07 /]# service mysql restart Shutting down MySQL... SUCCESS! Starting MySQL.. SUCCESS! [root@node-07 /]# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.34-log Source distribution mysql> show variables like 'log_bin'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | log_bin | ON | +---------------+-------+ 1 row in set (0.02 sec) ``` 3,查看主节点二进制日志列表,查看主节点server id ``` mysql> show master logs; +------------------+-----------+ | Log_name | File_size | +------------------+-----------+ | mysql-bin.000011 | 1283 | | mysql-bin.000012 | 177 | | mysql-bin.000013 | 2643 | | mysql-bin.000014 | 177 | | mysql-bin.000015 | 177 | | mysql-bin.000016 | 777 | | mysql-bin.000017 | 364 | | mysql-bin.000018 | 619 | +------------------+-----------+ 8 rows in set (0.00 sec) mysql> show global variables like '%server_id%'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | server_id | 1 | | server_id_bits | 32 | +----------------+-------+ 2 rows in set (0.00 sec) ``` 4,在主节点上创建有复制权限的用户。REPLIACTION SLAVE ,REPLIATION CLIENT,然后`flush privileges`刷新 ```shell mysql> grant replication slave,replication client on *.* to 'testuser'@'192.168.249.130' identifiedby 'test123456'; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) mysql> use mysql Database changed mysql> select Host,User from user where User = 'testuser'; +-----------------+----------+ | Host | User | +-----------------+----------+ | 192.168.249.130 | testuser | +-----------------+----------+ 1 row in set (0.01 sec) ``` 注意:上面👆设置的 testuser 是你创建的新用户名,test123456是该新用户的密码,如果不懂这部分内容,可以看我的上一篇博文:[mysql允许远程连接](http://www.tscgo.cn/blog/detail?id=61c13fae78256#),同时,需要确保你的从节点能够连接上主节点的mysql,这部分的内容同样也可以看我上一篇博客。 #### 2、从节点配置 1,编辑配置文件`/etc/my.cnf`,添加一个server id ,开启中继日志,添加如下配置项: ```shell relay-log=relay-log relay-log-index=relay-log.index server-id=2 innodb_file_per_table=ON skip_name_resolve=ON ``` 2,重启mysql,登录mysql客户端,查看中继日志和server id 是否配置成功: ```shell [root@node-08 ~]# service mysql restart Shutting down MySQL.. SUCCESS! Starting MySQL. SUCCESS! [root@node-08 ~]# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 mysql> show global variables like '%relay_log%'; +---------------------------+--------------------------------------+ | Variable_name | Value | +---------------------------+--------------------------------------+ | max_relay_log_size | 0 | | relay_log | relay-log | | relay_log_basename | /usr/local/mysql/var/relay-log | | relay_log_index | /usr/local/mysql/var/relay-log.index | | relay_log_info_file | relay-log.info | | relay_log_info_repository | FILE | | relay_log_purge | ON | | relay_log_recovery | OFF | | relay_log_space_limit | 0 | | sync_relay_log | 10000 | | sync_relay_log_info | 10000 | +---------------------------+--------------------------------------+ 11 rows in set (0.01 sec) mysql> show global variables like '%server_id%'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | server_id | 2 | | server_id_bits | 32 | +----------------+-------+ 2 rows in set (0.01 sec) ``` 3,在从节点配置访问主节点的参数信息,此时,我们要先去主节点查看主节点的当前binlog文件是哪一个,当前的binlog位置是哪一行;然后再去从节点配置。 在主节点查看binlog文件及位置: ``` mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000018 | 619 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) ``` 得到文件名和位置之后,然后在从节点配置主节点信息:(注意下面的testuser是我们上文配置到那个新用户,根据自己的需求配置新用户账号密码) ``` mysql> CHANGE MASTER TO MASTER_HOST='192.168.249.128', -> MASTER_USER='testuser',MASTER_PASSWORD='test123456', -> MASTER_LOG_FILE='mysql-bin.000018',MASTER_LOG_POS=619; Query OK, 0 rows affected, 2 warnings (0.01 sec) ``` 4,查看从节点的状态信息 ```` mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Master_Host: 192.168.249.128 Master_User: test user Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000018 Read_Master_Log_Pos: 619 Relay_Log_File: relay-log.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: mysql-bin.000018 Slave_IO_Running: No <============ 因为还没有启动 Slave_SQL_Running: No <============ 因为还没有启动 ```` 5,启动复制线程,并再次查看salve信息状态,发现没有开启,并且报错了。 ```shell mysql> start slave; Query OK, 0 rows affected (0.01 sec) mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Master_Host: 192.168.249.128 Master_User: testuser Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000018 Read_Master_Log_Pos: 619 Relay_Log_File: relay-log.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: mysql-bin.000018 Slave_IO_Running: No <===========没有开启 Slave_SQL_Running: Yes Replicate_Do_DB: ························· Last_IO_Errno: 1593 Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work. Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 1 ·························· 1 row in set (0.00 sec) ``` 在网上查了一下,原因是因为我主从两个节点的centos系统都是来自同一份备份aof安装的,所以两个节点的配置都是一模一样的,所以会有uuid一样的情况,解决方法: 找到auto.cnf,移除,然后重启mysql: ```shell [root@node-08 ~]# find / -name auto.cnf /usr/local/mysql/var/auto.cnf [root@node-08 ~]# cd /usr/local/mysql/var/ [root@node-08 var]# mv auto.cnf ./auto.cnf.bak [root@node-08 var]# service mysql restart Shutting down MySQL.. SUCCESS! Starting MySQL. SUCCESS! ``` 再次进入从节点mysql客户端,查看状态,发现状态正常了: ````shell [root@node-08 var]# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.7.34 Source distribution mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.249.128 Master_User: testuser Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000018 Read_Master_Log_Pos: 619 Relay_Log_File: relay-log.000003 Relay_Log_Pos: 320 Relay_Master_Log_File: mysql-bin.000018 Slave_IO_Running: Yes <=============开启 Slave_SQL_Running: Yes <=============开启 ```` 至此,主从复制配置完成,我们在主节点增加一个test数据库,然后往里面新增一个user表,然后往里面增加数据,可以发现,从库也有相同的数据库,表和数据增加。 主节点(192.168.249.128): ``` mysql> use test Database changed mysql> select * from user; +----+-----------+-----+-------------+ | id | name | age | phone | +----+-----------+-----+-------------+ | 1 | 李小龙 | 19 | 13124928322 | | 2 | 张小龙 | 18 | 13124928323 | | 3 | 陈添记 | 23 | 13124928333 | | 4 | 王白胡 | 33 | 13124928355 | | 5 | 李富贵 | 1 | 13124928388 | +----+-----------+-----+-------------+ 5 rows in set (0.00 sec) ``` 从节点(192.168.249.130): ``` mysql> use test Database changed mysql> select * from user; +----+-----------+-----+-------------+ | id | name | age | phone | +----+-----------+-----+-------------+ | 1 | 李小龙 | 19 | 13124928322 | | 2 | 张小龙 | 18 | 13124928323 | | 3 | 陈添记 | 23 | 13124928333 | | 4 | 王白胡 | 33 | 13124928355 | | 5 | 李富贵 | 1 | 13124928388 | +----+-----------+-----+-------------+ 5 rows in set (0.00 sec) ``` #### 3、注意问题 参考文章: 1,[mysql复制--主从复制配置](https://blog.csdn.net/daicooper/article/details/79905660) 2,[mysql binlog介绍](https://blog.csdn.net/wwwdc1012/article/details/88373440) 3,["Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs](https://www.cnblogs.com/datalife/p/8026928.html)

评论

  1. #1

    小朱 2022-01-01 18:02:16
    有几点看不懂

  2. #2

    馍馍 2022-01-11 22:15:59
    还没更新啊?!

  3. #3

    一个帅哥 2022-02-11 11:42:38
    这篇写的最好!!

  4. #4

    女神 2022-04-12 22:57:35
    讨厌你!!!!!

  5. #5

    妹妹 2022-04-20 08:08:39
    早上好