读写分离,基本的原理是让主数据库处理事务性增、改、删操作( INSERT、UPDATE、DELETE) ,而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
因为数据库的“写”(写10000条数据可能要3分钟)操作是比较耗时的。
但是数据库的“读”(读10000条数据可能只要5秒钟);
所以读写分离,解决的是,数据库的写入,影响了查询的效率。
数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用。利用数据库主从同步,再通过读写分离可以分担数据库压力,提高性能。
数据分布:通过复制将数据分布到不同地理位置
负载均衡:读写分离以及将读负载到多台从库
备份:可作为实时备份
高可用性:利用主主复制实现高可用
在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。
因此,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。有点类似于rsync(文件同步工具),但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据、语句做备份。
(1) STATEMENT:基于语句的复制。在服务器上执行sql语句,在从服务器上执行同样的语句,mysql默认采用基于语句的复制(5.7版本之前),执行效率高。高并发的情况可能会出现执行顺序的误差,事务的死锁。
(2)ROW:基于行的复制。把改变的内容复制过去,而不是把命令在从服务器上执行一 遍。精确,但效率低,保存的文件会更大。(5.7版本之后默认采用ROW模式)
(3)MIXED:混合类型的复制。默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。更智能,所以大部分情况下使用MIXED。
STATEMENT:
传输效率高,减少延迟。
在从库更新不存在的记录时,语句赋值不会失败。而行复制会导致失败,从而更早发现主从之间的不一致。
设表里有一百万条数据,一条sql更新了所有表,基于语句的复制仅需要发送一条sql,而基于行的复制需要发送一百万条更新记录
row
不需要执行查询计划。
不知道执行的到底是什么语句。
主从复制基于主mysql服务器和从mysql服务器的三个线程和两个日志展开进行的:
两个日志:二进制日志(bin log) 、中继日志(Relay log)
三个线程:I/O线程、dump线程、SQL线程
(1) 用户在主库中更新写入sql语句(查不会写入二进制文件中),主库将其记录在(binlog)二进制日志文件中。
(2)从库会对主库进行检测,当主库的二进制日志发生改变时,从库会开启io线程,向主库请求读取二进制事件。
(3)主库开启dump线程答应从库的请求,向从库发送二进制事件。从库通过io线程将主库发送的二进制事件记录到中继日志中。
(4)从库中的SQL线程之后会对中继日志进行读取,并进行重放的操作,保证数据的一致性。重放结束后,从库的I/O线程和SQL线程将进入睡眠状态,等待下一次被唤醒。
vim /etc/my.cnf
server-id=11
log-bin=mysql-bin #添加,主服务器开启二进制日志
binlog_format=mixed
#选配项
expire_logs_days=7 #设置二进制日志文件过期时间,默认值为0,表示logs不过期
max_binlog_size=500M #设置二进制日志限制大小,如果超出给定值,日志就会发生滚动,默认值是1GB
skip_slave_start=1 #阻止从库崩溃后自动启动复制,崩溃后再自动复制可能会导致数据不一致的
#"双1设置",数据写入最安全
innodb_flush_logs_at_trx_commit=1 #redo log(事务日志)的刷盘策略,每次事务提交时MySQL都会把事务日志缓存区的数据写入日志文件中,并且刷新到磁盘中,该模式为系统默认
sync_binlog=1 #在进行每1次事务提交(写入二进制日志)以后,Mysql将执行一次fsync的磁盘同步指令,将缓冲区数据刷新到磁盘
vim /etc/my.cnf
server-id = 22 #修改,注意id与Master的不同,两个Slave的id也要不同
relay-log=relay-log-bin #开启中继日志,从主服务器上同步日志文件记录到本地
relay-log-index=relay-log-bin.index #定义中继日志文件的位置和名称,一般和relay-log在同一目录
#选配项
innodb_buffer_pool_size=2048M #用于缓存数据和索引的内存大小,让更多数据读写内存中完成,减少磁盘操作,可设置为服务器总可用内存的 70-80%
sync_binlog=0 #MySQL不做任何强制性的磁盘刷新指令,而是依赖操作系统来刷新数据到磁盘
innodb_flush_log_at_trx_commit=2 #每次事务log buffer会写入log file,但一秒一次刷新到磁盘
log-slave-updates=0 #slave 从 master 复制的数据会写入二进制日志文件里,从库做为其他从库的主库时设置为 1
relay_log_recovery=1 #当 slave 从库宕机后,假如 relay-log 损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的 relay-log, 并且重新从 master 上获取日志,这样就保证了 relay-log 的完整性。默认情况下该功能是关闭的,将 relay_log_recovery 的值设置为 1 时, 可在 slave 从库上开启该功能,建议开启。
crontab -e
#配置同步,注意 master_log_file 和 master_log_pos 的值要与Master查询的一致
- 读写分离就是只在主服务器上写,只在从服务器上读。
- 基本的原理是让主数据库处理事务性操作,而从数据库处理select查询。
- 数据库复制被用来把主数据库上事务性操作导致的变更,同步到集群中的从数据库。
1)基于程序代码内部实现
在代码中根据select、insert进行路由分类,这类方法也是目前生产环境应用最广泛的。
优点是性能较好,因为在程序代码中实现,不需要增加额外的设备为硬件开支;缺点是需要开发人员来实现,运维人员无从下手。
但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的Java应用,如果在程序代码中实现读写分离对代码改动就较大。
2)基于中间代理层实现
代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有以下代表性程序。
(1)MySQL-Proxy。MySQL-Proxy为MysQL开源项目,通过其自带的1ua脚本进行sQL判断。
(2)Atlas。是由奇虎360的Web平台部基础架构团队开发维护的一个基于MysQL协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性。360内部使用atlas运行的mysql业务,每天承载的读写请求数达几干保条。支持事物以及存储过程。
(3)Amoeba。由陈思儒开发,作者曾就职于阿里巴巴。该程序由Java语言进行开发,阿里巴巴将其用于生产环境。但是它不支持事务和存储过程。
(4)Mycat。是一款流行的基于Java语言编写的数据库中间件,是一个实现了MySq1协议的服务器,其核心功能是分库分表。配合数据库的主从模式还可以实现读写分离。
由于使用MysQLProxy需要写大量的ua脚本,这些Lua并不是现成的,而是需要自己去写。这对于并不熟悉MysQLProxy 内置变量和MySQL Protocol的人来说是非常困难的。
Amoeba是一个非常容易使用、可移植性非常强的软件。因此它在生产环境中被广泛应用于数据库的代理层。
./jdk-6u14-linux-x64.bin
amoeba.xml设置
dbServers.xml设置
测试:
主从复制延迟原因
- master服务器高并发,形成大量事务。
- 网络延迟。
- 主从硬件设备导致(cpu主频、内存IO、硬盘IO)。
- 是同步复制,而不是异步复制。
解决方案
(1)硬件方面
从库配置更好的硬件,提升随机写的性能。比如原本是机械盘,可以考虑更换为ssd固态。升级核心数更强的cpu、加大内存。避免使用虚拟云主机,使用物理主机
(2)网络方面
将从库分布在相同局域网内或网络延迟较小的环境中。尽量避免跨机房,跨网域进行主从数据库服务器的设置
(3)架构方面
在事务当中尽量对主库读写,其他非事务中的读在从库。消除一部分延迟带来的数据库不一致。增加缓存降低一些从库的负载。
(4)mysqld服务配置方面
该配置设置针对mysql主从复制性能优化最大化,安全性并不高。如果从安全的角度上考虑的话,就要设置双一设置
追求安全性的双一设置:
innodb_flush_log_at_trx_commit=1
sync_binlog=1
主从复制不一致的原因及解决方案
产生主从复制不一致的可能原因
- 人为原因导致从库与主库数据不一致(从库写入)
- 主从复制过程中,主库异常宕机
- 设置了ignore/do/rewrite等replication等规则
- binlog非row格式
- 异步复制本身不保证,半同步存在提交读的问题,增强半同步起来比较完美。 但对于异常重启(Replication Crash Safe),从库写数据(GTID)的防范,还需要策略来保证。
- 从库中断很久,binlog应用不连续,监控并及时修复主从
- 从库启用了诸如存储过程,从库禁用存储过程等
- 数据库大小版本/分支版本导致数据不一致?,主从版本统一
- 备份的时候没有指定参数 例如mysqldump --master-data=2 等
- 主从sql_mode 不一致
- 一主二从环境,二从的server id一致
- MySQL自增列 主从不一致
- 主从信息保存在文件里面,文件本身的刷新是非事务的,导致从库重启后开始执行点大于实际执行点
- 采用5.6的after_commit方式半同步,主库当机可能会引起主从不一致,要看binlog是否传到了从库
- 启用增强半同步了(5.7的after_sync方式),但是从库延迟超时自动切换成异步复制