前言订单缓存方案上线后,我们以为太平盛世的日子又开始了.然而一周后的一天,DBA直接来了,DBA直接说:“领导让我直接找你,是这样的,上次加了缓存优化后,效果确实不错,但是我发现今天12:00到12:05订单查询sql不可用,中间有很多慢sql,查询时间超过2.5s。”这时,我们立即启动故障排除模式。首先我们查看了上次添加的缓存,发现缓存是正常的,然后根据DBA提供的信息进行查找。在日志中,此时发现订单请求量在这段时间突然增加,大约是正常订单请求量的2到3倍。经过了解,发现在这段时间里,营销系统做了一些活动,导致订单请求量突然增加。说白了就是大量下单的用户在做了促销活动之后,会不断的刷新订单,查看订单的信息,比如查看订单是否已经开始发货。这时候就会有大量的请求发往MySQL。这时候单体数据库无法抵抗这样的读请求,导致数据库负载过高,严重降低了MySQL的查询效率。现在我们添加了缓存,但是数据库负载还是很高,这时候怎么办呢?其实也很简单。既然单个库搞不定,那就用两个库来拼一下吧,因为对于外卖订单来说是典型的读多写少的场景。所以,在这种场景下,我们可以实现一个主双从的架构来进行优化,像这样:即写数据走主库,读数据走从库,可以看出,此时由于我们有2个从库,所以这2个从库可以共同抵抗大量的读请求。关键是从库会通过主从复制不断的从主库同步数据,从而保证从库中的数据和主库是完全一样的,所以如果要实现读-写分离,那么,首先要了解主从复制是如何工作的。主从复制的原理是什么?我们以mysql一主二从架构为例,即一个主节点下有两个从节点。在这种架构下,写请求统一交给主节点处理,而读请求则交给从节点处理。为了保证从节点和主节点之间的数据一致性,主节点在写入数据后,也会将数据复制到自己的各个从节点中。复制过程中一共使用了三个线程,一个是binlogdump线程,位于master节点上,另外两个线程是I/O线程和SQL线程,分别位于slave上节点,如下图:组合图,我们看一下主从复制的核心流程:(1)当主节点收到写请求时,写请求可能是增删改查操作,所有的写请求操作都会记录在binlog日志中。(2)主节点将数据复制到从节点,如图中的slave01节点和slave02节点。在这个过程中,每个从节点都必须先连接到主节点。当slave节点连接到master节点时,master节点会为每个slave节点创建一个binlogdump线程,将binlog日志发送给每个slave节点。(3)binlogdump线程会读取master节点上的binlog日志,然后将binlog日志发送给slave节点上的I/O线程。(4)slave节点上的I/O线程收到binlog日志后,会先将binlog日志写入本地relaylog,binlog日志保存在relaylog中。(5)从节点上的SQL线程会读取relaylog中的binlog日志,解析成具体的增删改查操作,在从节点上重做主节点上的操作,达到效果数据恢复,保证主从节点之间的数据一致性。主从复制有几种模式?Mysql主从复制分为四种:全同步复制、异步复制、半同步复制和增强型半同步复制。全同步复制首先,全同步复制是指主库执行完一个事务后,所有从库也必须执行完该事务,然后才将处理结果返回给客户端;因此,虽然全同步复制的数据一致性得到了保证,但是主库需要等待所有从库完成一个事务,性能比较低。异步复制和异步复制。当主库提交事务时,会通知binlogdump线程将binlog日志发送给从库。一旦binlogdump线程将binlog日志发送到从库,就不需要等到从库也同步完成事务。处理结果将返回给客户端。因为主库只需要自己执行事务,就可以将处理结果返回给客户端,而无需关心从库是否已经完成事务,这可能会造成短期的主从数据不一致,比如数据刚刚插入主库。如果立即从数据库中查询新数据,则可能查询不到。而且主库提交东西的时候,如果机器挂了,binlog可能来不及同步到从库。这时候如果切换主从节点恢复failover,就会出现数据丢失的问题,所以异步复制虽然性能高,但是数据一致性弱。MySQL主从复制默认采用异步复制策略。半同步复制半同步复制,顾名思义,是同步和异步的折衷。我们可以结合MySQL官网看一下半同步主从复制的过程。看下图:当主库提交事务时,至少还需要从库接收一条消息才能接收到binlog日志,并成功写入relaylog。此时主库会将处理结果返回给客户端。与前两种复制方式相比,半同步复制更好地平衡了数据一致性和性能损失的问题。同时,半同步复制也存在以下问题:半同步复制的性能相比异步复制有所下降。与异步复制相比,不需要等待库的任何响应来接收数据,而半同步复制需要等待至少一个库的响应来确认收到binlog日志,成本更高性能方面。可以配置主库等待从库响应的最长时间。如果超过配置的时间,半同步复制就会变成异步复制,那么异步复制的问题也会出现。MySQL5.7.2之前的版本存在半同步复制幻读的问题。当主库成功提交交易,正在等待从库确认的过程中,此时从库还没有来得及将处理结果返回给客户端,但是由于主库存储引擎已经已经提交了交易,其他客户端可以从主库读取数据。但是,如果下一秒主库突然挂了,如下图:这时候,下一个请求来了,因为主库挂了,请求只能切换到从库,因为从库图书馆尚未从主图书馆发送。数据库已经同步完数据了,当然不能从数据库中读取这条数据了。与上一秒读取数据的结果相比,出现幻读。增强型半同步复制最后,增强型半同步复制是mysql5.7.2以后版本对半同步复制的改进。原理差不多,主要是解决幻读问题。主库配置参数rpl_semi_sync_master_wait_point=AFTER_SYNC后,在存储引擎提交事务前,主库必须收到从库提交事务前数据同步完成的确认,解决幻象问题阅读。可以参考MySQL官网对增强型半同步主从复制过程的描述:主从延迟问题及常规解决方案主库写入速度非常快,因为主库是多线程并发写入.库是单线程从主库拉取数据,所以从库从主库复制数据的速度比较慢,造成主从延迟的问题。MySQL从5.6版本开始支持多线程复制,但是5.6版本是基于库级别运行的,也就是说会为每个数据库开一个线程,不同的库在处理的时候不会同时相互影响;但是,当业务压力集中在一个库上时,又会回到单线程复制的情况。直到mysql5.7版本,引入了基于组提交(group_commit)的概念。这时候才真正支持多路复制功能。正式名称为增强型多线程从机(简称MTS)。所以建议大家尽量选择MySQL5.7。的版本。但是主库挂载过多的从库也会造成主从复制的延迟。一般我们建议一个主库挂载从库的个数,3到5个之间比较合适。另外,如果我们执行的SQL语句中有太多慢SQL语句,也会造成主从复制延误。比如我们在工作中会遇到批量插入的场景。如果批量插入的数据量过大,很容易导致执行时间过长。例如,如果执行一条批量插入数据的SQL语句,到从库上找到数据的过程需要10秒,就会造成主从库之间有10秒的延迟;所以,SQL优化将是一项常态化的工作。慢SQL可以通过慢SQL日志或者监控平台进行监控。如果单个数据写入时间过长,可以分批写入一批数据。最后,如果有网络延迟或者机器性能比较差,也会造成主从复制延迟的问题。遇到这种情况也没啥好说的,及时优化网络,提高机器的性能就好了。读写分离实战读写分离配置核心组件流程图:|读写分离配置步骤(1)在配置文件中配置主从库连接信息(2)注入数据源(3)数据源切换上下文,使用ThreadLocal保存当前线程的数据源(4)继承AbstractRoutingDataSource类并重写determineCurrentLookupKey方法,实现数据源的动态切换(5)为阅读库创建自定义注解(6)section类(7)在walk的业务方法中添加@ReadOnly注解-读库,那么在执行这些业务方法的时候,会被切面拦截修改数据源,这样就可以查询到走读库了。(八)写主库和读从库的作用1)生成订单2)查询订单
