为啥
MySQL是现在使用很成熟和很常见的存储解决方案,但是我们如何使用MySQL去设计我们的高性能架构呢?这里设计到的架构指标有系统吞吐量、QPS等,也就是如何提高MySQL的并发能力,常见的办法是通过添加机器来分散系统压力,常见的方案就是读写分离,通过增加机器分散系统的读压力和写压力。
我们今天来折腾一下读写分离,也来研究一下读写分离的原理。
开搞
-
由于自己搭建环境实在太麻烦,小弟我决定使用docker镜像来搭建多个mysql机器
- 拉取docker镜像,运行,拉取的镜像是这个
- 可以看到我们这里跑起来两个docker容器
暂定mysql1为master,mysql2为salve-
docker inspect可以看到两个机器的IP分别为:
master:172.17.0.2; slave:172.17.0.3
-
配置各个mysql机器(需要指定root用户进容器)
-
配置主库,
- 创建用于复制的用户slave,赋予从站从主机读取二进制日志权限
# 创建slave用户,该用户在任何主机上都可以登录 CREATE USER slave@% IDENTIFIED BY 'pass'; # 赋予slave用户的权限 GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%';
生产环境由于安全性考虑可能还是需要指定用户的IP,如果我们有多个Slave,可以执行上面的SQL多次,只需将 'slave'@'%' 中的 IP(%) 改成其他Slave的IP。下图可以看到用户的权限:
-
打开binlog,设置server_id
log-bin=/var/lib/mysql/mysql-bin server-id=1 # 选择需要备份的数据库,如果需要备份全部,可以不增加这行; binlog-do-db = test_db # 指定只保存7天的二进制日志,防止占用磁盘空间; expire-logs-days= 7
-
重启主库 , 查看 Master Status
# 重启MySQL服务: docker restart mysql1 # 查看 Master Status mysql> SHOW MASTER STATUS;
可以看到master关于复制的配置binlog文件 -
配置从库
#打开binlog,设置server_id log-bin=/var/lib/mysql/mysql-bin server_id=2 # 配置中继日志(主要是在MySQL服务器的主从架构中的Slave上用到的, # 当Slave想要和Master进行数据的同步时, # 从服务器将Master的二进制日志文件拷贝到自己的主机上放在中继日志中, # 然后调用SQL线程按照拷中继日志文件中 # 的二进制日志文件执行以便就可达到数据的同步。) relay_log=mysql-relay-bin # 表示slave将复制事件写进自己的二进制日志; log_slave_updates=1 read_only=1
-
重启从库,从库连接主库,启动slave
mysql> CHANGE MASTER TO MASTER_HOST='172.17.0.2', MASTER_USER='slave', MASTER_PASSWORD='pass', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=0; //启动 Slave mysql> START SLAVE;
如图- 查看复制是否成功
#查看主从复制是否配置成功 mysql>SHOW SLAVE STATUS\G #Slave_IO_Running:Yes #Slave_SQL_Running:Yes
如图:
-
-
填充数据
好的!接下来我们尝试填充数据,可以看到在mysql1主库创建的数据库、表以及新增的记录都同步到了mysql2。
masterslave成功生效。
主从复制原理
操作配置完后,我们可以知道主从复制的原理就是不同机器之间的binlog同步,从库拉取到主库的binlog后,在本地执行同样的动作,以此同步主库的改动,这里包括使用relay log去实现异步的复制。
Mysql的复制(replication)是一个异步的复制,从一个Mysql instace(称之为Master)复制到另一个Mysql instance(称之Slave)。实现整个复制操作主要由三个进程完成的,其中两个进程在Slave(Sql进程和IO进程),另外一个进程在 Master(IO进程)上。
要实施复制,首先必须打开Master端的binary log(binlog)功能,否则就无法实现。因为整个复制过程实际上就是Slave从Master端拉取二进制日志然后再在本地完全顺序地执行日志中所记录的各种操作。
查了一下相关的资料,复制的基本过程如下:
- Master将用户对数据库更新的操作以二进制格式保存到 Binary Log日志文件中;
- Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
- Master接收到来自Slave的IO进程的请求后,通过负责复制的IO进程根据请求信息读取制定日志指定位置之后的日志信息,返回给Slave 的IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置;
- Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的 bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master“我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我”;
- Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行。
在了解了复制的基本过程后,我们能理解一般常用的架构是一主多从和双主多从,双主的考虑我理解是基于主库1的备份和容灾处理。
但是通过学习我们能意识到一个问题,一个slave无法设置两个master,所以在分散系统写压力方面其实最多只能部署2台机器。这一点上可能还需要继续探索。
主从复制相关参数简述
这里主要是要学习一下几个MySQL关于复制的参数以及对这几个参数使用场景的思考
主从复制的过滤
-
在主服务器在把事件从进二制日志中过滤掉:
binlog_do_db:设定哪些数据库需要记录binlog
binlog_ignore_db:设定哪里数据库不需要记录binlog
优点:Master端的Binlog记录减少所带来的IO量减少,网络IO减少,slave端的IO线程减少,能从源头提高复制性能;
缺点(记录):是mysql判断是否需要复制某个事件不是根据产生该事件的查询所在的DB,而是根据执行查询时刻所在的默认数据库(也就是登录时指定的库名或运行”use database”中指定的DB),只有当前默认DB和配置中所设定的DB完全吻合时IO线程才会将该事件读取给slave的IO线程.所以,如果在默认DB和设定须要复制的DB不一样的情况下改变了须要复制的DB中某个Table中的数据,该事件是不会被复制到Slave中去的,这样就会造成Slave端的数据和Master的数据不一致.同样,在默认的数据库下更改了不须要复制的数据库中的数据,则会被复制到slave端,当slave端并没有该数据库时,则会造成复制出错而停止。
-
在从服务器上把事件从中继日志中过滤掉,相关的参数是replicate_*:
replicate_do_db:设定需要复制的数据库,多个DB用逗号分隔
replicate_ignore_db:设定可以忽略的数据库.
replicate_do_table:设定需要复制的table
replicate_ignore_table:设定可以忽略的table
replicate_wild_do_table:功能同replicate_do_table,但可以带通配符来进行设置。
replicate_wild_ignore_table:功能同replicate_do_table,功能同replicate_ignore_table,可以带通配符。
缺点:性能方面比在Master端作限制要差一些,原因是复制的binlog数据量大,所有事件都会被IO线程读取到Slave端,增加了网络IO量,Slave端的IO线程也增加了Relay Log写入量。
应用场景思考
通过这几个参数的配置我们可以实现多种架构,可能会因不同的业务、不同公司实际数据储存情况,或开发人员的喜好而异,以下是可能存在的数据库部署结构:
常用读写方案
在工作中,比较常见的读写分离方案一般有两种:在业务代码里配置主库和从库;只用数据库中间件;
代码里配置参数
- laravel ORM实现读写分离
有时间我们可以阅读一下laravel的ORM源码,了解业界如何随机选取配置好的主库和从库;
中间件
mysql-proxy、Atlas、Mysql router、Mycat、Amoeba等中间件一般会用在体量相对大一些的公司;
配置备忘和记录
-
--logs-slave-updates 参数的作用
通常情况,从服务器从主服务器接收到的更新不记入它的二进制日志。该选项告诉从服务器将其SQL线程执行的更新记入到从服务器自己的二进制日志。为了使该选项生效,还必须用--logs-bin选项启动从服务器以启用二进制日志。如果想要应用链式复制服务器,应使用--logs-slave-updates。例如,可能你想要这样设置:
A -> B -> C
也就是说,A为从服务器B的主服务器,B为从服务器C的主服务器。为了能工作,B必须既为主服务器又为从服务器。你必须用--logs-bin启动A和B以启用二进制日志,并且用--logs-slave-updates选项启动B。
MySQL配置官方文档
MySQL5.7复制