【MySql系列11】:mysql的主从复制和读写分离

VMware Workstation创建虚拟机及安装MySQL数据库

1、虚拟机启动mysql

[root@node1 ~]# service mysqld start  #启动mysql服务
Starting MySQL SUCCESS! 
[root@node1 ~]# 2021-02-21T18:16:57.561631Z mysqld_safe A mysqld process already exists
[root@node1 ~]# chkconfig mysqld on  #mysql开机自启动
[root@node1 ~]# mysql -uroot -p123456  #登录mysql
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.19-log Source distribution

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

2、 mysql主从复制原理

1、为什么需要主从复制?

  1. 在业务复杂的系统中,有这么一个情景,有一句sql语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。
  2. 做数据的热备
  3. 架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。

2、什么是mysql的主从复制?

MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。

在这里插入图片描述

  1. 主库db的更新事件(update、insert、delete)被写到binlog
  2. 主库创建一个binlog dump thread,把binlog的内容发送到从库
  3. 从库启动并发起连接,连接到主库
  4. 从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
  5. 从库启动之后,创建一个SQL线程,从relay
    log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db

3、mysql复制原理

  • (1)master服务器将数据的改变记录二进制binlog日志,当master上的数据发生改变时,则将其改变写入二进制日志中;

  • (2)slave服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/OThread请求master二进制事件

  • (3)同时主节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,从节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒。

也就是说:

  • 从库会生成两个线程,一个I/O线程,一个SQL线程;
  • I/O线程会去请求主库的binlog,并将得到的binlog写到本地的relay-log(中继日志)文件中;
  • 主库会生成一个log dump线程,用来给从库I/O线程传binlog;
  • SQL线程,会读取relay log文件中的日志,并解析成sql语句逐一执行;

注意:

1–master将操作语句记录到binlog日志中,然后授予slave远程连接的权限(master一定要开启binlog二进制日志功能;通常为了数据安全考虑,slave也开启binlog功能)。

2–slave开启两个线程:IO线程和SQL线程。其中:IO线程负责读取master的binlog内容到中继日志relay log里;SQL线程负责从relay log日志里读出binlog内容,并更新到slave的数据库里,这样就能保证slave数据和master数据保持一致了。

3–Mysql复制至少需要两个Mysql的服务,当然Mysql服务可以分布在不同的服务器上,也可以在一台服务器上启动多个服务。

4–Mysql复制最好确保master和slave服务器上的Mysql版本相同(如果不能满足版本一致,那么要保证master主节点的版本低于slave从节点的版本)

5–master和slave两节点间时间需同步

具体步骤:

1、从库通过手工执行change master to 语句连接主库,提供了连接的用户一切条件(user 、password、port、ip),并且让从库知道,二进制日志的起点位置(file名 position 号); start slave

2、从库的IO线程和主库的dump线程建立连接。

3、从库根据change master to 语句提供的file名和position号,IO线程向主库发起binlog的请求。

4、主库dump线程根据从库的请求,将本地binlog以events的方式发给从库IO线程。

5、从库IO线程接收binlog events,并存放到本地relay-log中,传送过来的信息,会记录到master.info中

6、从库SQL线程应用relay-log,并且把应用过的记录到relay-log.info中,默认情况下,已经应用过的relay 会自动被清理purge

4、mysql主从形式

(一)一主一从
(二)主主复制
(三)一主多从
(四)多主一从
(五)联级复制

4、mysql主从同步延时分析

​ mysql的主从复制都是单线程的操作,主库对所有DDL和DML产生的日志写进binlog,由于binlog是顺序写,所以效率很高,slave的sql thread线程将主库的DDL和DML操作事件在slave中重放。DML和DDL的IO操作是随机的,不是顺序,所以成本要高很多,另一方面,由于sql thread也是单线程的,当主库的并发较高时,产生的DML数量超过slave的SQL thread所能处理的速度,或者当slave中有大型query语句产生了锁等待,那么延时就产生了。

解决方案:

​ 1.业务的持久化层的实现采用分库架构,mysql服务可平行扩展,分散压力。

​ 2.单个库读写分离,一主多从,主写从读,分散压力。这样从库压力比主库高,保护主库。

​ 3.服务的基础架构在业务和mysql之间加入memcache或者redis的cache层。降低mysql的读压力。

​ 4.不同业务的mysql物理上放在不同机器,分散压力。

​ 5.使用比主库更好的硬件设备作为slave,mysql压力小,延迟自然会变小。

​ 6.使用更加强劲的硬件设备

注:mysql5.7以后版本使用了MTS并行复制技术,永久解决了复制延时问题

3、mysql主从复制安装配置

1、基础设置准备

#操作系统:
centos6.5
#mysql版本:
5.7
#两台虚拟机:
node1:192.168.85.111(主)
node2:192.168.85.112(从)

2、安装mysql数据库

#创建虚拟机及安装mysql
https://blog.csdn.net/weixin_40964170/article/details/113813840

3、在两台数据库中分别创建数据库

--注意两台必须全部执行
create database msb;

Linux系统中查看mysql数据库表文件所在目录

mysql> show global variables like '%datadir%';
+---------------+-------------------+
| Variable_name | Value             |
+---------------+-------------------+
| datadir       | /www/server/data/ |
+---------------+-------------------+
1 row in set (0.00 sec)

mysql>

4、在主(node1)开启binlog日志:

binlog日志一般情况默认是不开启的

mysql> show variables like '%log_bin%'; #查看binlog日志
+---------------------------------+----------------------------------+
| Variable_name                   | Value                            |
+---------------------------------+----------------------------------+
| log_bin                         | ON   #开启中                            |
| log_bin_basename                | /www/server/data/mysql-bin       |
| log_bin_index                   | /www/server/data/mysql-bin.index |
| log_bin_trust_function_creators | OFF                              |
| log_bin_use_v1_row_events       | OFF                              |
| sql_log_bin                     | ON                               |
+---------------------------------+----------------------------------+
6 rows in set (0.00 sec)

mysql> 

开启binlog操作

[root@node1 etc]# cd /etc/
[root@node1 etc]# vi my.cnf 

#修改配置文件,执行以下命令打开mysql配置文件
vi /etc/my.cnf
#在mysqld模块中添加如下配置信息
log-bin=master-bin #二进制文件名称
binlog-format=ROW  #二进制日志格式,有row、statement、mixed三种格式,row指的是把改变的内容复制过去,而不是把命令在从服务器上执行一遍,statement指的是在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。mixed指的是默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。
server-id=1		   #要求各个服务器的id必须不一样
binlog-do-db=msb   #同步的数据库名称

5、配置从服务器登录主服务器的账号授权

--授权操作
--调整MySQL密码验证规则,修改 policy 和 length 的值
-- 宝塔安装的mysql不需要修改密码规则
set global validate_password_policy=0;
set global validate_password_length=1;
grant replication slave on *.* to 'root'@'%' identified by '123456';
--刷新权限
flush privileges;

演示

mysql> grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

mysql> quit
Bye
[root@node4 ~]# 

6、从服务器的配置

[root@node2 ~]# cd /etc/
[root@node2 etc]# vi my.cnf 
[root@node2 etc]# 

#修改配置文件,执行以下命令打开mysql配置文件
vi /etc/my.cnf
#在mysqld模块中添加如下配置信息
log-bin=master-bin	#二进制文件的名称
binlog-format=ROW	#二进制文件的格式
server-id=2			#服务器的id

7、重启主服务器的mysqld服务

#重启mysql服务
service mysqld restart
#登录mysql数据库
mysql -uroot -p
#查看master的状态
show master status;

8、重启从服务器并进行相关配置

#重启mysql服务
service mysqld restart
#登录mysql
mysql -uroot -p
#连接主服务器
change master to master_host='192.168.91.11',master_user='root',master_password='123456',master_port=3306,master_log_file='master-bin.000001',master_log_pos=154;
#启动slave
start slave
#查看slave的状态
show slave status\G(注意没有分号)

9、此时可以在主服务器进行相关的数据添加删除工作,在从服务器看相关的状态

4、读写分离

1、什么是读写分离

读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
在这里插入图片描述

2、读写分离介绍

MySQL读写分离基本原理是让master数据库处理写操作,slave数据库处理读操作。master将写操作的变更同步到各个slave节点。
在这里插入图片描述

MySQL读写分离提高系统性能的原因:

  1. 物理服务器增加,机器处理能力提升。拿硬件换性能。
  2. ​ 主从只负责各自的读和写,极大程度缓解X锁和S锁争用。
  3. ​ slave可以配置myiasm引擎,提升查询性能以及节约系统开销。
  4. ​ master直接写是并发的,slave通过主库发送来的binlog恢复数据是异步。
  5. ​ slave可以单独设置一些参数来提升其读的性能。
  6. ​ 增加冗余,提高可用性。

3、案例演示:mysql-proxy配置读写分离

1、硬件配置

3台mysql5.7版本+1台mysql-proxy

主数据库:master 192.168.85.11           #node1
从数据库:slave  192.168.85.12           #node2
中间件服务器:proxy  192,168.85.14       #node3

在这里插入图片描述

2、首先在master和slave上配置主从复制
3、进行proxy的相关配置
#1、下载mysql-proxy
https://downloads.mysql.com/archives/proxy/#downloads
#2、上传软件proxy机器到node3
直接通过xftp进行上传
#3、解压安装包
tar -zxvf mysql-proxy-0.8.5-linux-glibc2.3-x86-64bit.tar.gz
#4、修改解压后的目录
mv mysql-proxy-0.8.5-linux-glibc2.3-x86-64bit mysql-proxy
#5、进入mysql-proxy的目录
cd mysql-proxy
#6、创建目录
mkdir conf   #配置文件
mkdir logs   #日志
#7、添加环境变量
#打开/etc/profile文件
vi /etc/profile
#在文件的最后面添加一下命令
export PATH=$PATH:/root/mysql-proxy/bin
#8、执行命令让环境变量生效
source /etc/profile
#9、进入conf目录,创建文件并添加一下内容
vi mysql-proxy.conf
添加内容
[mysql-proxy]
user=root
proxy-address=192.168.85.14:4040
proxy-backend-addresses=192.168.85.11:3306
proxy-read-only-backend-addresses=192.168.85.12:3306
proxy-lua-script=/root/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua
log-file=/root/mysql-proxy/logs/mysql-proxy.log
log-level=debug
daemon=true
#10、开启mysql-proxy
mysql-proxy --defaults-file=/root/mysql-proxy/conf/mysql-proxy.conf
#11、查看是否安装成功,打开日志文件
cd /root/mysql-proxy/logs
tail -100 mysql-proxy.log
#内容如下:表示安装成功
2019-10-11 21:49:41: (debug) max open file-descriptors = 1024
2019-10-11 21:49:41: (message) proxy listening on port 192.168.85.14:4040
2019-10-11 21:49:41: (message) added read/write backend: 192.168.85.11:3306
2019-10-11 21:49:41: (message) added read-only backend: 192.168.85.12:3306
2019-10-11 21:49:41: (debug) now running as user: root (0/0)

4、进行连接
#mysql的命令行会出现无法连接的情况,所以建议使用客户端
mysql -uroot -p123 -h192.168.85.14 -P 4040

4、amoeba实现mysql读写分离

1、什么是amoeba?

​ Amoeba(变形虫)项目,专注 分布式数据库 proxy 开发。座落与Client、DB Server(s)之间。对客户端透明。具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库、可并发请求多台数据库合并结果。

主要解决:

  • 降低 数据切分带来的复杂多数据库结构
  • 提供切分规则并降低 数据切分规则 给应用带来的影响
  • 降低db 与客户端的连接数
  • 读写分离
2、为什么要用Amoeba

目前要实现mysql的主从读写分离,主要有以下几种方案:

  1. 通过程序实现,网上很多现成的代码,比较复杂,如果添加从服务器要更改多台服务器的代码。
  2. 通过mysql-proxy来实现,由于mysql-proxy的主从读写分离是通过lua脚本来实现,目前lua的脚本的开发跟不上节奏,而写没有完美的现成的脚本,因此导致用于生产环境的话风险比较大,据网上很多人说mysql-proxy的性能不高。
  3. 自己开发接口实现,这种方案门槛高,开发成本高,不是一般的小公司能承担得起。
  4. 利用阿里巴巴的开源项目Amoeba来实现,具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库,并且安装配置非常简单。
3、amoeba安装
1、首先安装jdk,直接使用rpm包安装即可
2、下载amoeba对应的版本https://sourceforge.net/projects/amoeba/,直接解压即可
3、配置amoeba的配置文件

dbServers.xml

<?xml version="1.0" encoding="gbk"?>

<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">

		<!-- 
			Each dbServer needs to be configured into a Pool,
			If you need to configure multiple dbServer with load balancing that can be simplified by the following configu
ration:			 add attribute with name virtual = "true" in dbServer, but the configuration does not allow the element with n
ame factoryConfig			 such as 'multiPool' dbServer   
		-->
		
	<dbServer name="abstractServer" abstractive="true">
		<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
			<property name="connectionManager">${defaultManager}</property>
			<property name="sendBufferSize">64</property>
			<property name="receiveBufferSize">128</property>
				
			<!-- mysql port -->
			<property name="port">3306</property>
			
			<!-- mysql schema -->
			<property name="schema">msb</property>
			
			<!-- mysql user -->
			<property name="user">root</property>
			
			<property name="password">123456</property>
		</factoryConfig>

		<poolConfig class="com.meidusa.toolkit.common.poolable.PoolableObjectPool">
			<property name="maxActive">500</property>
			<property name="maxIdle">500</property>
			<property name="minIdle">1</property>
			<property name="minEvictableIdleTimeMillis">600000</property>
			<property name="timeBetweenEvictionRunsMillis">600000</property>
			<property name="testOnBorrow">true</property>
			<property name="testOnReturn">true</property>
			<property name="testWhileIdle">true</property>
		</poolConfig>
	</dbServer>

	<dbServer name="writedb"  parent="abstractServer">
		<factoryConfig>
			<!-- mysql ip -->
			<property name="ipAddress">192.168.85.11</property>
		</factoryConfig>
	</dbServer>
	
	<dbServer name="slave"  parent="abstractServer">
		<factoryConfig>
			<!-- mysql ip -->
			<property name="ipAddress">192.168.85.12</property>
		</factoryConfig>
	</dbServer>
	<dbServer name="myslave" virtual="true">
		<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
			<!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
			<property name="loadbalance">1</property>
			
			<!-- Separated by commas,such as: server1,server2,server1 -->
			<property name="poolNames">slave</property>
		</poolConfig>
	</dbServer>
</amoeba:dbServers>

amoeba.xml

<?xml version="1.0" encoding="gbk"?>

<!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">
<amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/">

	<proxy>
	
		<!-- service class must implements com.meidusa.amoeba.service.Service -->
		<service name="Amoeba for Mysql" class="com.meidusa.amoeba.mysql.server.MySQLService">
			<!-- port -->
			<property name="port">8066</property>
			
			<!-- bind ipAddress -->
			<!-- 
			<property name="ipAddress">127.0.0.1</property>
			 -->
			
			<property name="connectionFactory">
				<bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory">
					<property name="sendBufferSize">128</property>
					<property name="receiveBufferSize">64</property>
				</bean>
			</property>
			
			<property name="authenticateProvider">
				<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">
					
					<property name="user">root</property>
					
					<property name="password">123456</property>
					
					<property name="filter">
						<bean class="com.meidusa.toolkit.net.authenticate.server.IPAccessController">
							<property name="ipFile">${amoeba.home}/conf/access_list.conf</property>
						</bean>
					</property>
				</bean>
			</property>
			
		</service>
		
		<runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext">
			
			<!-- proxy server client process thread size -->
			<property name="executeThreadSize">128</property>
			
			<!-- per connection cache prepared statement size  -->
			<property name="statementCacheSize">500</property>
			
			<!-- default charset -->
			<property name="serverCharset">utf8</property>
			
			<!-- query timeout( default: 60 second , TimeUnit:second) -->
			<property name="queryTimeout">60</property>
		</runtime>
		
	</proxy>
	
	<!-- 
		Each ConnectionManager will start as thread
		manager responsible for the Connection IO read , Death Detection
	-->
	<connectionManagerList>
		<connectionManager name="defaultManager" class="com.meidusa.toolkit.net.MultiConnectionManagerWrapper">
			<property name="subManagerClassName">com.meidusa.toolkit.net.AuthingableConnectionManager</property>
		</connectionManager>
	</connectionManagerList>
	
		<!-- default using file loader -->
	<dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader">
		<property name="configFile">${amoeba.home}/conf/dbServers.xml</property>
	</dbServerLoader>
	
	<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">
		<property name="ruleLoader">
			<bean class="com.meidusa.amoeba.route.TableRuleFileLoader">
				<property name="ruleFile">${amoeba.home}/conf/rule.xml</property>
				<property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property>
			</bean>
		</property>
		<property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property>
		<property name="LRUMapSize">1500</property>
		<property name="defaultPool">writedb</property>
		
		<property name="writePool">writedb</property>
		<property name="readPool">myslave</property>
		<property name="needParse">true</property>
	</queryRouter>
</amoeba:configuration>
4、启动amoeba
/root/amoeba-mysql-3.0.5-RC/bin/launcher
4、测试amoeba
--测试的sql
--在安装amoeba的服务器上登录mysql
mysql -h192.168.85.13 -uroot -p123 -P8066
--分别在master、slave、amoeba上登录mysql
use msb
select * from user;
--在amoeba上插入数据
insert into user values(2,2);
--在master和slave上分别查看表中的数据
select * from user;
--将master上的mysql服务停止,继续插入数据会发现插入不成功,但是能够查询
--将master上的msyql服务开启,停止slave上的mysql,发现插入成功,但是不能够查询
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值