主从复制解决的问题
数据分布:通过复制将数据分布到不同地理位置
负载均衡:读写分离以及将读负载到多台从库
备份:可作为实时备份
高可用性:利用主主复制实现高可用
复制原理
复制的原理其实很简单,仅分为以下三步:
在主库上把数据更改记录到二进制日志binary log中,具体是在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志中去,MySQL会按照事务提交的顺序来记录二进制日志的。日志记录好之后,主库通知存储引擎提交事务。
从库会启动一个IO线程,该线程会连接到主库。而主库上的binlog dump线程会去读取主库本地的binlog日志文件中的更新事件。发往从库,从库接收到日志之后会将其记录到本地的中继日志relay-log当中。
从库中的SQL线程读取中继日志relay-log中的事件,将其重放到从库中。(在5.6版本之前SQL线程是单线程的,使得主从之间延迟更大)
两种复制方式
日志文件中记录的到底是什么呢?mysql支持了两种日志格式,这两种日志格式也体现了各自的复制方式
基于语句复制
基于语句的复制相当于逻辑复制,即二进制日志记录了操作的语句,通过这些语句在从库进行重放来实现复制。
这种方式简单,二进制日志占用空间少,使得带宽小传输效率较高。但是基于语句的更新依赖于其他因素,比如插入数据时利用时间戳函数调用当前时间作为时间值也会出现问题,因为由于主从之间的延迟导致时间值不一致。存储过程和触发器也可能出现问题。
所以在开发当中我们应该将逻辑尽量放在代码层,而不应放到mysql中,不易扩展。
基于行复制
基于行的复制相当于物理复制,即二进制日志记录了实际更新数据的每一行。这样导致行复制的压力比较大,因为日志占用空间较大,传输占用带宽也较高。但是比基于语句复制更加精确,可以屏蔽一些由于主库从库之间的差异导致的不一致。如刚才提到的时间戳函数。
二者对比:
语句复制
传输效率高,减少延迟。
在从库更新不存在的记录时,语句赋值不会失败。而行复制会导致失败,从而更早发现主从之间的不一致。
设表里有一百万条数据,一条sql更新了所有表,基于语句的复制仅需要发送一条sql,而基于行的复制需要发送一百万条更新记录
行复制
不需要执行查询计划。
不知道执行的到底是什么语句。
例如一条更新用户总积分的语句,需要统计用户的所有积分再写入用户表。如果是基于语句复制的话,从库需要再一次统计用户的积分,而基于行复制就直接更新记录,无需再统计用户积分。
因为两种方式各有优缺点,所以mysql在这两种复制模式进行动态的切换。默认是语句。
配置要点
innodb_flush_logs_at_trx_commit的三个参数很容易弄混。以下是详细地解析:
推荐使用:
性能会较快
较为安全
延迟问题
延迟的产生
当主库的TPS并发较高时,由于主库上面是多线程写入的,而从库的SQL线程是单线程的,导致从库SQL可能会跟不上主库的处理速度(生产者比消费者快,导致商品堆积)。
延迟的解决
网络方面:将从库分布在相同局域网内或网络延迟较小的环境中。
硬件方面:从库配置更好的硬件,提升随机写的性能。
配置方面:
从库配置
让更多操作在Mysql内存中完成,减少磁盘操作。或者升级Mysql5.7版本使用并行复制。
架构方面:比如在事务当中尽量对主库读写,其他非事务中的读在从库。消除一部分延迟带来的数据库不一致。增加缓存降低一些从库的负载。
笔者个人心得,如有错误恳请评论指正。