二十二、读写分离(主从复制)

从代码实现来说叫读写分离,从架构角度来说叫主从复制

1、什么是主从复制

主从复制,是一种数据备份的方案

是使用两个或两个以上相同的数据库,将一个数据库当做主数据库,而另一个数据库当做从数据库。主数据库中进行相应操作时,从数据库记录下所有主数据库的操作,使其二者一模一样。

为什么要使用:

1、当主数据库出现问题时,可以让从数据库代替主数据库,可以避免数据的丢失。

2、可以进行读写分离

1.1 此时主服务器会将更新信息写入到一个特定的二进制文件中,并会维护文件的一个索引用来跟踪日志循环,这个日志可以记录并发送到从服务器的更新中去。

1.2 一台从服务器连接到主服务器时,从服务器会通知主服务器从服务器的日志文件中读取最后一次成功更新的位置。然后从服务器会接收从哪个时刻起发生的任何更新,然后锁住并等到主服务器通知新的更新。

2、什么是读写分离

就是基于主从复制架构,一个主库,有多个从库,当主数据库进行对数据的增删改也就是写操作时,将查询的任务交给从数据库

为什么要使用:

1、避免从数据库进行写操作而导致的主从数据库数据不一致的情况,因为当主从数据库数据不一致时,那么从数据库最主要的备份任务就没有意义了。

2、减轻主数据库的压力。因为进行写操作更耗时,所以如果不进行读写分离的话,写操作将会影响到读操作的效率。

可以说主从复制是实现读写分离的技术之一

3、主从复制原理

1、Master将数据改变的记录到二进制(binary log)中, 也就是配置文件log-bin指定的文件名

2、slave 通过 i/o 线程读取master中binary log events 内容,并写入到slave的中继日志中

3、slave 重做中继日志事件,把中继日志的事件信息一条一条的在本地执行一次,完成数据库的本地存储,从而实现数据的复制操作。

mysql中有一种日志,叫做bin日志(二进制日志),会记录下所有修改过数据库的sql语句。

主从复制的原理实际是多台服务器都开启bin日志,然后主服务器会把执行过的sql语句记录到bin日志中,之后从服务器读取该日志,在从服务器再把bin日志中记录的sql语句同样的执行一遍。

这样从服务器上的数据就和主服务器相同了。

读写分离 - 图1

注意:

(1)主从都要开启bin日志

(2)主服务器需要授权用户

(3)具体的配置过程;

4、读写分离实现方法

1、 基于程序代码的内部实现

在代码中根据select、insert进行路由分类,这类方法也是目前生产环境中较为常用的,

优点是性能较好,因为在程序代码中实现,不需要增加额外的设备作为硬件开支;

缺点是需要研发人员来实现,运维人员无从下手。

读写分离 - 图2

2、 基于中间代理层实现

代理一般位于客户端和服务器之间,代理服务器接收到客户端请求后通过判断后转发到后端数据库。

mysql_proxy:mysql_proxy是Mysql的一个开源项目,通过其自带的lua脚本进行sql判断。

Atlas:是由 Qihoo 360, Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性。360内部使用Atlas运行的mysql业务,每天承载的读写请求数达几十亿条。支持事物以及存储过程。

Amoeba:由阿里巴巴集团在职员工陈思儒使用序java语言进行开发,阿里巴巴集团将其用户生产环境下,但是他并不支持事物以及存数过程。

5、bin-log开启操作

开启bin-log日志

打开mysql的配置文件,

window下面 my.ini

linux下面 my.cnf

  1. log-bin=mysql-bin

读写分离 - 图3

注意:

修改完成mysql的配置后,要重启mysql服务

mysql里面数据表的存储位置,要看配置文件,

开启配置后,产生的二进制日志文件如下;

读写分离 - 图4

与log-bin日志相关的函数

  1. flush logs; #执行该命令,就会产生一个新的log-bin日志

读写分离 - 图5

读写分离 - 图6

  1. reset master; #清空所有的log-bin日志,并产生一个新的log-bin日志

读写分离 - 图7

通过show master status命令,能查看到二进制文件里面最后一个pos位置。

  1. show master status; #查看最后(新)的一个log-bin日志

读写分离 - 图8

查看log-bin日志里面的内容

新建一张表,测试log-bin日志是否记录增删改的sql语句

读写分离 - 图9

MySQL中二进制文件所在目录: /var/lib/mysql

读写分离 - 图10

读写分离 - 图11

注意:使用mysql安装目录下面的bin目录下面mysqlbinlog命令,来查看日志内容。

语法:

  1. mysqlbinlog --no-defaults 二进制日志的名称(全路径)

读写分离 - 图12

注意:end_log_pos的理解,用于记录上一个 sql语句的结束,下一个sql语句 的开始位置

读写分离 - 图13

读写分离 - 图14

6、主从复制配置步骤

实验规划,需要两台主机

第一台主机:ip地址 192.168.1.69 配置为master服务器

第二台主机:ip地址 192.168.1.70 配置为slave 服务器

1、配置主服务器

(1)开启二进制日志。

(2)要设置一个server-id(作为一个服务器的编号,是唯一) 该值不能和从服务器相同。

读写分离 - 图15

注意:

在my.cnf配置文件里面,配置的区域在[mysqld]与[mysql]之间配置;

配置完成后,要重启mysql服务

(3)授权一个账号,让从服务器通过该账号读取log-bin日志里面的内容

  1. grant replication slave on *.* to 'xiongda'@'%' identified by '123456'

读写分离 - 图16

赋予从库权限账号,允许用户在主库上读取日志,也就是Slave机器读取File权限,

  1. grant FILE on *.* to 'xiongda'@'%' identified by '123456';

(4)记录主服务器里面的最新的二进制的名称和pos位置

读写分离 - 图17

注意:此时,就禁止对主服务器执行增删改的操作,一直到从服务器配置成功。

2、配置从服务器

(1)开启二进制日志。

(2)要设置一个server-id 该值不能和主服务器的相同。

读写分离 - 图18

注意:配置好配置文件后,要重启mysql服务器;

(3)停止从服务器

登录到从服务器后,执行 stop slave 指令即可。

读写分离 - 图19

(4)开始配置

语法:

  1. change master to
  2. master_host="主服务器的ip地址",
  3. master_user="授权用户的名称",
  4. master_password="授权用户的密码",
  5. master_log_file="二进制日志文件的名称",
  6. master_log_pos=记录的pos位置;

例:

  1. change master to
  2. master_host="192.168.1.69",
  3. master_user="xiongda",
  4. master_password="123456",
  5. master_log_file="mysql-bin.000004",
  6. master_log_pos=2603;

读写分离 - 图20

(5)开启从服务器

执行 start slave 指令即可。

读写分离 - 图21

(6)查看是否配置成功

执行show slave status;

读写分离 - 图22

Slave_IO_Running:Yes

此进程负责从服务器从主服务器上读取binlog 日志,并写入从服务器上的中继日志。

Slave_SQL_Running:Yes

此进程负责读取并且执行中继日志中的binlog日志,

注:以上两个都为yes则表明成功,只要其中一个进程的状态是no,则表示复制进程停止,错误原因可以从”last_error”字段的值中看到。

3、测试主从复制

在主服务器创建一个新库,并添加一张新表,并插入新数据,

读写分离 - 图23

在从服务器上面查看是否有该库,该表,该记录。

读写分离 - 图24

4、撤销从服务器

从服务器上执行如下两个指令。

(1)stop slave

(2)reset slave all

读写分离 - 图25

7、实现读写分离

1、通过业务逻辑来实现读写分离

  1. class mysql
  2. {
  3. $dbm=主服务器
  4. $dbs1=从服务器
  5. $dbs2=从服务器
  6. public function query()
  7. {
  8. query里面进行语句判断,分析连接不同的mysql服务器。
  9. }
  10. }

2、TP框架里面实现读写分离

1、TP框架里面配置文件

读写分离 - 图26

读写分离 - 图27

2、在TP里面,测试读写分离的配置

  1. db()->query('select * from user');
  2. db()->execute('insert into user values(3,"xiaolong")')

读写分离 - 图28