MySQL读写分离

1、背景

        数据库还是单机部署,依据一些云厂商的 Benchmark 的结果,在 4 核 8G 的机器上运 MySQL 5.7 时,大概可以支撑 500 的 TPS 和 10000 的 QPS。这时,运营负责人说正在准备双十一活动,并且公司层面会继续投入资金在全渠道进行推广,这无疑会引发查询量骤然增加的问题。当查询请求增加时,我们应该如何做来解决问题?答案是主从读写分离。

2、主从读写分离

        大部分系统的访问模型是读多写少,读写请求量的差距可能达到几个数量级。比如购物网站上一个商品的浏览量也肯定远大于它的下单量。

        数据库如何抗住更高的查询请求,那么首先你需要把读写流量区分开,因为这样才方便针对读流量做单独的扩展。它其实是个流量分离的问题,就好比道路交通管制一样,一个四车道的大马路划出三个车道给领导外宾通过,另外一个车道给我们使用,优先保证领导先行,就是这个道理。这个方法本身是一种常规的做法,即使在一个大的项目中,它也是一个应对数据库突发读流量的有效方法。

        流量突增导致从库负载过高的问题,可以优先做几个从库扩容上去,这样对数据库的读流量就会落入到多个从库上,其他的从库的负载就降了下来。

3、主从读写的关键点

        主从读写分离有两个技术上的关键点:

  • 主从复制:主库数据拷贝到从库;
  • 扩展:在主从分离的情况下,我们如何屏蔽主从分离带来的访问数据库方式的变化,让开发同学像是在使用单一数据库一样。

3.1 主从复制的过程

MySQL读写分离_第1张图片

        首先从库在连接到主节点时会创建一个 IO 线程,把接收到的 binlog 信息写入一个叫做 relay log 的日志文件中,而主库也会创建一个 log dump 线程来发送 binlog 给从库;同时,从库还会创建一个 SQL 线程读取 relay log 中的内容,并且在从库中做回放,最终实现主从的一致性。

这里面涉及到三个线程:

  • IO线程:连接到 master 获取 binlog,并且解析 binlog 写入中继日志,这个线程叫做 I/O 线程。
  • log dump线程:master 节点上有一个 log dump 线程,是用来发送 binlog 给 slave 的。
  • sql线程:从库的 sql 线程,是用来读取 relay log,把数据写入到数据库的。 

3.2 主从复制三种同步方式

  • 全同步复制(Fully synchronous replication)
    • 指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。
    • 问题:因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
  • 异步复制(Asynchronous replication)
    • MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给客户端,并不关心从库是否已经接收并处理。
    • 问题:主如果crash掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
  • 半同步复制(Semisynchronous replication)
    • 介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。
    • 问题:相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。

        基于性能的考虑,可以选择异步复制。数据不一致这种情况出现的概率很低,对于互联网的项目来说是可以容忍的。

3.3 从库数量

        是不是我无限制地增加从库的数量就可以抵抗大量的并发呢?

        实际上并不是的。因为随着从库数量增加,从库连接上来的 IO 线程比较多,主库也需要创建同样多的log dump 线程来处理复制的请求,对于主库资源消耗比较高,同时受限于主库的网络带宽,所以在实际使用中,一般一个主库最多挂 3~5 个从库。

        阿里云的集群从节点最多4个:集群版RDS实例的优势及应用场景_云数据库 RDS-阿里云帮助中心。

4、主从延迟

4.1 问题

        在发微博的过程中会有些同步的操作,像是更新数据库的操作,也有一些异步的操作,比如说将微博的信息同步给审核系统,所以我们在更新完主库之后,会将微博的 ID 写入消息队列,再由队列处理机依据 ID 在从库中获取微博信息再发送给审核系统。此时如果主从数据库存在延迟,会导致在从库中获取不到微博信息,整个流程会出现异常。

MySQL读写分离_第2张图片

4.2 解决方案

        解决核心思想就是尽量不去从库中查询信息。

数据的冗余-推荐

        发送消息队列时不仅仅发送微博 ID,而是发送队列处理机需要的所有微博信息,借此避免从数据库中重新查询数据。

使用缓存

        在同步写数据库的同时,也把微博的数据写入到redis缓存里面,这样队列处理机在获取微博信息的时候会优先查询缓存,这样也可以保证数据的一致性。

查询主库

       在队列处理机中不查询从库而改为查询主库。不过,这种方式使用起来要慎重,要明确查询的量级不会很大,是在主库的可承受范围之内,否则会对主库造成比较大的压力。

总结

        下面都是以问题为例提供解决方案,具体业务需要具体分析。方案推荐使用数据的冗余。

5、读写分离工具

  • 开源工具:ShardingSphere、Mycat等,网上有很多相应工具。
  • 云服务商:代理等相关方案。
    • 阿里云:数据库代理(读写分离)_云数据库 RDS-阿里云帮助中心

参考文章:

MySQL实现主从复制的原理详解_Mysql_脚本之家

你可能感兴趣的:(面试,mysql,mysql,数据库)