MySQL主从

工作原理分析

2140640119-58e3a97a13419_articlex

  • 如图所示,主服务器上面的任何修改都会保存到二进制Binary log日志里面,从服务器启动一个I/O 线程 (实际上就是一个主服务器的客户端进程),连接到主服务器上面请求读取二进制日志文件,然后把读取到的二进制日志文件写入到本地的 Really log 里面,从服务器开启一个SQL thread 定时检查 Realy log 如果发现有更改立即把更改的内容在本机上面执行以下。

  • 如果是一主多从的情况下,这个时候,主库既要负责写操作,又要负责分发二进制文件给多台服务器,对于性能来说还是有不足的地方,这个时候,我们可以将主服务器的二进制分发给其中一台从服务器,这个从服务器再分发给所有其它的从服务器,或者再极端点,主服务器只负责写操作,指定其中一台服务器分发二进制文件,这样的架构可能要好得多,而且数据之间的延时也稍微更好一些。如图所示 1945827240-58e3a97e5f4f2_articlex

  • 实际上老版本中,mysql主从复制中 Slave 端并不是两个进程完成的,而是一个进程。也就是说,从服务器的 I/O线程和SQL Thread线程作为一个线程,从服务器需要读取二进制日志文件,然后把读取到的二进制文件进行解析,将修改内容复制到从服务器上 ,但是后来发现有很多问题,主要如下

    1. 一个进程会使得bin-log 日志和解析日志并在自身执行的过程成为一个串行的过程,性能受到一定的限制,异步复制的延迟也会比较长,通俗点就是,我们需要先读取日志文件再进行解析,相对于两个线程劣势很明显。
    2. Slave端从主服务器上获取 bin-log 过来之后,需要接着解析日志内容,然后在自身执行。这个过程如果遇到大量log文件,并且在这个时间段内 bin-log 又出现了大量新文件,当Master端的存储出现了无法修复的错误,那么这个阶段内所有的变更都无法找回,在高并发网站中,会等待比较长的时间,导致数据延迟太高。
  • 当然,即使是换成了现在这样的两个线程来处理主从,依然有Slave数据延时以及数据丢失的可能性的,毕竟这个复制是异步的。只要数据的更改不是在一个事务内,都是不可避免的有延迟。如果要完全避免这个,就只能用Mysqlcluster来解决了。不过这个是内存数据库的解决方案,需要将所有数据都load到内存中,这样对内存的需求太大了,对于一般应用的实施性不是太大。
  • 还有一点要提的就是Mysql的复制过滤,复制过滤可以让你只复制服务器中的一部分数据。通过设置 my.cnf来配置 binglog_do_db,binlog_ignore_db参数来配置需要的数据库和不需要的数据库。 2610150268-58e3a97a53132_articlex

mysql主从复制基本流程

  1. Slave上面的I/O进程连接上主服务器,并请求指定日志文件的指定位置(或者从最开始的日志)之后的日志内容。
  2. Master 接收来自 slaveI/O进程的请求后,负责复制的IO进程会根据请求信息读取日志指定位置之后的日志信息,返回给 SlaveI/O进程,返回信息除了日志所包含的信息之外,还包括本次返回的信息已经到Master 端的bin-log文件的名称以及bin-log的位置。
  3. SlaveI/O进程接收到信息后,将接收到的日志内容依次添加到Slave端的Relay-log文件的最末端,并将读取到的Master端的bin-log文件名和位置记录到Master-info文件中,以便在下次读取的时候能够清楚地告诉Master端我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我。
  4. SlaveSQL进程检测到Relay-log中新增了内容后,会马上解析Relay-log的内容并且在Master端真实执行的内容(这里是因为读取到的bin-log日志内容包含的不仅仅是执行的操作,还有其他的一些记录),并在自身执行。

mysql主从复制的工作原理

  1. 基于语句的复制,主服务器上执行的语句在服务器上再执行一遍。
    • 存在的问题:时间上可能不完全同步造成偏差,执行语句的用户也可能不是一个用户
  2. 基于行的复制:把主服务器上面改变后的内容直接复制过去,而不关心到底改变该内容是由哪条语句引发的。

    • 存在的问题:比如一个工资表中有一万个用户,我们把每个用户的工资+1000,那么基于行的复制意味着需要复制一万行数据,由此造成比较大的开销,而基于语句的复制仅仅需要一条语句就可以了。
  3. 混合类型的复制(默认):mysql默认使用基于语句的复制,当基于语句的复制引发了问题的时候自动使用基于行的复制,mysql自行选择。
  • 在MySQL主从复制架构中,读操作可以在所有的服务器上面进行,而写操作只能在主服务器上面进行。主从复制架构虽然给读操作提供了扩展,可如果写操作也比较多的话(多台从服务器还要从主服务器上面同步数据),单主模型的复制中主服务器势必会成为性能瓶颈。
  • 对于写操作比较的应用,我们可以使用多主多从的配置,多主多从以后再积累写一篇博客。

开始配置一主一从服务器。

  1. 准备工具:vagrant 如何用vagrant搭建多个服务器这里就不赘述了。
主master : 192.168.8.10
从slave  :   192.168.8.11
  1. 准备工作:在两个服务器上安装mysql,安装过程也不在赘述。

  2. 大体流程

    • 建立一个主节点,开启binlog,设置服务器id,一般主服务器设置为1就可以了,从服务器根据ip最后面几位数字来决定。
    • 建立一个从节点,设置服务器id;
    • 将从节点连接到主节点上。
  3. 开始操作

    • 首先配置主服务器,开启binlog日志,并且设置唯一的服务器id,默认1好了,配置完了记得重启
      
      [mysqld]
      server-id = 1   //服务器id,唯一键,不能重复
      log_bin = /var/log/mysql/mysql-bin.log  //bin-log保存的文件路径
      binlog-do-db = test   //配置需要复制的数据库名

    server-id:master端的ID号; log-bin:同步的日志路径及文件名,一定注意这个目录要是mysql有权限写入的; binlog-do-db:要同步的数据库名 binlog-ignore-db = mysql binlog-ignore-db = test //不同步mysql库和test库

  • 分配权限给从服务器的 Slave

mysql>GRANT REPLICATION SLAVE ON . to 'test'@'192.168.8.11' identified by 'test'; mysql>FLUSH PRIVILEGES;


*查看主服务器当前二进制日志名和偏移量,这个操作的目的是为了在从数据库启动后,从这个点开始进行数据的恢复

mysql> show master status; +---------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +---------------+----------+--------------+------------------+ | binlog.000001 | 1304 | cmdb | | +---------------+----------+--------------+------------------+ row in set (0.00 sec)


到这里,主服务器已经配置好了

* 配置从库

执行sql语句

CHANGE MASTER TO MASTER_HOST='192.168.8.10',   MASTER_PORT=3306,   MASTER_USER='rep1',   MASTER_PASSWORD='test123456',   MASTER_LOG_FILE='binlog.000001',   MASTER_LOG_POS=1304; #后面两个参数的值与主库保持一致


* 启动`Slave`进程

mysql> slave start; Query OK, 0 rows affected (0.04 sec)


* 查看slave状态,如果下面两项值为YES,则表示配置正确,

Slave_IO_Running: Yes Slave_SQL_Running: Yes



这里比较坑的地方是由于我的从服务器是从 vagrant 复制过来的,导致所有配置与主服务器一致,则出了 `/var/lib/mysql/auto.cnf` 文件与主服务器文件一致就报错了,具体原因还没去研究。解决办法就是在从服务器删掉该文件之后重启该服务器,重启后我们对比下这个文件的内容,就会发现不一样了。这时候在 `slave start` 就会成功。

# # 程序如何区分读写,分发至不同服务器?

相对于比较小的应用,程序一般会截取`SQL`语句前面的几个字符,无非 `select`,`delete`,`update`,`insert` ,判断这几个字符,然后再分发到不同的服务器中去,对于比较大的系统,用的就是中间件,这个以后再总结。