mysql(4)主从复制、读写分离

1. 主从复制

1.1 为啥要主从复制

  1. 解决读写分离。由于在写库时的行锁表锁的行为,可能导致在读取的时候,正好有线程在写库,可能正在进行表锁或者行锁,读线程也需要同步等待。所有进行主从复制,主库负责写数据,从库负责读数据,读写分离。
  2. 可以做数据的热备。当主库挂了,可以使用从库顶上
  3. 架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。便于分库分表

1.2 主从复制的配置操作

1.2.1 机器和服务器的准备
  1. 两台机器,一个作为主服务器,一个作为从服务器。ip: 127.0.0.1 (主)。 127.0.0.2(从)
  2. 下载mysql,并分别安装到两台服务器上
  3. 分别在两她mysql上创建数据库db,create database mytest。
1.2.2 配置主库
#修改配置文件,执行以下命令打开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=mytest   #同步的数据库名称
1.2.3 授权

在主服务器上给从服务器授权,允许从服务器访问主服务器。

--授权操作
set global validate_password_policy=0;
set global validate_password_length=1;
grant replication slave on *.* to 'root'@'%' identified by '123456';
--刷新权限
flush privileges;
1.2.4 配置从服务器
#修改配置文件,执行以下命令打开mysql配置文件
vi /etc/my.cnf
#在mysqld模块中添加如下配置信息
log-bin=master-bin	#二进制文件的名称
binlog-format=ROW	#二进制文件的格式
server-id=2			#服务器的id
1.2.5 重启主服务器
#重启mysql服务
service mysqld restart
#登录mysql数据库
mysql -uroot -p
#查看master的状态
show master status;
1.2.6 重启从服务并做配置
#重启mysql服务
service mysqld restart
#登录mysql
mysql -uroot -p
#连接主服务器
change master to master_host='192.168.85.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(注意没有分号)
1.2.7 操作数据

可以在主库上操作数据,看是否从库也做相同的操作。

1.3 主从复制的原理及过程

1.3.1 主从复制原理
  1. 主从复制就是数据可以从主库复制到一个或者多个从库节点中,默认采用异步复制的方式。

  2. 复制原理
    a. master自己记录日志。 将每次的更新操作都记录到二进制binlog日志中,当发生数据修改就记录binlog日志
    b. slave查看并发送请求给master。 在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个io线程(I/OThread)请求master二进制事件
    c. master 接收到slave的请求,创建一个dump线程,发送二进制日志事件给slave。
    d. slave 拿到日志后,存储到本地的relay-log(中继日志)文件中。然后启动一个sql线程读取relaylog日志,进行数据更新重放。然后sleep,等待下次的操作。

  3. 具体步骤
    a. 准备工作
    需要配置,让从库知道从哪里复制,复制那个文件,知道ip,port,等。从库通过手工执行change master to 语句连接主库,提供了连 接的用户一切条件(user 、password、port、ip),并且让从库知道,二进制日志的起点位置(file名 position 号),启动从库。
    b. 从库的IO线程和主库的Dump线程建立连接
    c. 从库根据change master to 语句提供的file名和position号,通过IO线程发送请求binlog请求到主库
    d. 主库dump线程接收到请求后,将本地的binlog以event事件的方式发送给从库的IO线程
    e. IO线程接收到主库的binlog event事件,存储到从库本地的relay log中。传递过来的信息会记录到master.info文件中,标志还未使用
    f. 从库的sql线程使用redey log日志,将数据同步,并且把使用过的relay log记录到relay-log.info文件中,标志已经使用。默认情况下,使用过的relay会被自动清理。

  4. 注意事项
    a. master一定记得开binlog日志,并授权slave能够进行远程连接。并建议slave也开启binlog日志
    b. master版本尽量和slave一致。或者比slave低
    c. 保持两个服务器时间一致。
    d. 一共三个线程在处理。slave的io线程发请求,master的dump线程发送binlog日志,slave的sql线程执行binlog
    e. 主从复制最少需要两个mysql服务,这两个mysql服务可以在一个物理服务器上也可以在不同的物理服务器上

1.3.2 主从复制形式
  1. 一主一丛复制
  2. 主主复制,互为主从
  3. 一主多从
  4. 多主一从
  5. 联级复制,一主多从,多从还有多从。

1.4 主从复制延时问题

1.4.1 产生延时的场景
  1. 大并发在主库操作后,产生很多binlog,而从库的sql线程是单线程,处理不过来时就会产生延时。
  2. 或者从库有一个大型的查询正在进行,产生了锁等待,导致延时
  3. master写binlog是顺序写,并且推给slave也是顺序的,slave的io线程存储本地也是顺序写,但是当sql线程执行DML 和 DDL语句的时候,这些语句在执行时并不是顺序的,而是随机的(各种语句,create drop alter 或者 insert update 等)时随机的,所有效率不会很快。可能导致延时。
1.4.2 解决办法
  1. 在服务的基础架构在业务和mysql之间加入memcache或者redis的cache层。降低mysql的读压力。
  2. 进行读写分离,将写操作移动到从库中
  3. 使用比主库更好的硬件设备作为slave,mysql压力小,延迟自然会变小。
  4. 业务的持久化层的实现采用分库架构,mysql服务可平行扩展,分散压力。
  5. 半同步复制(5.5开始),半同步复制介于异步复制和同步复制之间。保证数据一致性,防丢。在master处理完之后并不马上返回给客户端,而是等待至少有一个slave将binlog存储到relay-log中在返回。半同步复制提高了数据的安全性,同时它也造成了一个TCP/IP往返耗时的延迟。
  6. mysql5.7之后使用MTS并行复制技术,永久解决复制延时问题

1.5 如何判断主从复制延时

  1. show master status; 先查看主库的当前的Position号和binlog文件名称。
  2. show slave satus;执行在mysql服务器上执行,show slave satus命令,会有很多参数。
Master_Log_File:                      SLAVE中的I/O线程当前正在读取的主服务器二进制日志文件的名称
Read_Master_Log_Pos:          在当前的主服务器二进制日志中,SLAVE中的I/O线程已经读取的位置
Relay_Log_File:                        SQL线程当前正在读取和执行的中继日志文件的名称
Relay_Log_Pos:                        在当前的中继日志中,SQL线程已读取和执行的位置
Relay_Master_Log_File:      由SQL线程执行的包含多数近期事件的主服务器二进制日志文件的名称
Slave_IO_Running:                 I/O线程是否被启动并成功地连接到主服务器上
Slave_SQL_Running:              SQL线程是否被启动
Seconds_Behind_Master:     从属服务器SQL线程和从属服务器I/O线程之间的时间差距,单位以秒计。
  1. 首先看 Relay_Master_Log_File 和 Master_Log_File 是否有差异,如果Relay_Master_Log_File 和 Master_Log_File 有差异的话,那说明延迟很大,说明文件都不是差的一星半点
  2. 如果文件名一样,在看两个pos位置,Read_Master_Log_Pos和Relay_Log_Pos位置,看下执行的位置相差是否很大
  3. 查看一个master.info 和 relay-log.info文件。看是否Position号相同

2. 读写分离

2.1 读写分离

  1. 啥叫读写分离,就是让master负责写,让slave负责读,所以读写分离前提是主从复制。
  2. 读写分离能够提供系统的性能
  • 主库负责写,从库负责读,能够减少锁X锁(排它写锁)和S锁(共享读锁)的争抢。
  • 扩展性好,可以给slave数据库设置mysiam存储引擎,查询效率好,而且可以给从库单独的设置一些有利于读的参数
  • master并发写,slave通过异步的方式保证数据一致
  • 读写分离最好不同的服务器,服务器增多,减少压力

2.2 读写分离的实操配置

2.2.1 准备机器和服务

三台机器,三个服务,一个mysql服务(master),一个mysql服务(slave),一个mysql代理服务(proxy)

master 127.0.0.1 
slave 127.0.0.2
proxy 127.0.0.3
2.2.2 配置主从复制

将master和slave进行主从复制配置。具体见1.2 章节

2.2.3 配置proxy
#1、下载mysql-proxy
https://downloads.mysql.com/archives/proxy/#downloads
#2、上传软件到proxy的机器
直接通过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)
2.2.4 进行连接测试

利用命令窗口可能连接不上,可以使用jdbc连接或者使用客户端navicat等客户端工具。

3. Amoeba实现读写分离

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

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

3.1 为啥要用amoeba

之前不是用mysql-proxy也能实现么,那么为啥要用amoeba呢
目前实现读写分离的方式:

  1. 通过代码实现读写分离(网上有很多),但是实现比较复杂。
  2. 通过mysql-proxy来实现,由于mysql-proxy的主从读写分离是通过lua脚本来实现,目前lua的脚本的开发跟不上节奏,而写没有完美的现成的脚本,因此导致用于生产环境的话风险比较大,据网上很多人说mysql-proxy的性能不高。
  3. 使用阿里巴巴的amoeba,Amoeba来实现,具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库,并且安装配置非常简单。国产的开源软件,应该支持

3.2 amoeba的安装使用

  1. 下载amoeba,免安装的,解压就是使用
  2. 配置dbServers.xml
<?xml version="1.0" encoding="gbk"?>

<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">
	<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">mytest</property>
			<!-- mysql user 用户 -->
			<property name="user">root</property>
			<!-- mysql user 密码 -->
			<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>
  1. 配置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>
  1. 启动amoeba
    /root/amoeba-mysql-3.0.5-RC/bin/launcher
  2. 进行测试
    进行读写分离测试。
    登陆amoeba,master ,slave

分别停止master和slave,测试查询和插入,看是否会成功的读写分离。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值