主从读写分离

大部分系统的访问模型是读多写少,读写请求量的差距可能达到几个数量级。

因此,我们优先考虑数据库如何抵抗更高的查询请求,那么首先你需要把读写流量区分开,因为这样才方便针对读流量做单独的扩展(但是不易太对IO线程消耗太大,一般一个主库最多挂 3~5 个从库),这就是我们所说的主从读写分离。从库也可以当成一个备库来使用,以避免主库故障导致数据丢失。

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

  1. 一个是数据的拷贝,我们称为主从复制;

  2. 在主从分离的情况下,我们如何屏蔽主从分离带来的访问数据库方式的变化,让开发像是在使用单一数据库一样。

主从复制

以MySQL的主从复制为例,MySQL 的主从复制是依赖于 binlog 的,主从复制就是将 binlog 中的数据从主库传输到从库上,一般这个过程是异步的,即主库上的操作不会等待 binlog 同步的完成。

同步完整流程:

image-20230320152932581

一个事务日志同步的完整过程是这样的:

  1. 在备库B上通过change master命令,设置主库A的IP、端口、用户名、密码,以及要从哪个位置开始请求binlog,这个位置包含文件名和日志偏移量。
  2. 在备库B上执行start slave命令,这时候备库会启动两个线程,就是图中的io_thread和sql_thread。其中io_thread负责与主库建立连接。
  3. 主库A校验完用户名、密码后,创建一个 log dump 线程来开始按照备库B传过来的位置,从本地读取binlog,发给B。
  4. 备库B拿到binlog后,写到本地文件,称为中转日志(relay log),避免写入从库实际存储会比较耗时,最终造成从库和主库延迟变长。
  5. sql_thread读取中转日志,解析出日志里的命令,并执行。

缺点:

  • 带来了部署上的复杂度。
  • 主从同步可能会产生延迟,正常的时间是在毫秒级别,一旦落后的时间达到了秒级别就需要告警了。
  • 主从同步问题会在MySQL中讲解,如果遇到刚插入就需要查询的业务可以强制查询主库避免主从延迟,或者保证MySQL是MGR那种全同步高可用方案。
  • 还可以利用KeepAlived 做了一个简单的自动主从切换机制,或利用云服务本身提供的这种自动切换功能。

如何访问数据库

当使用了多节点实现主从读写分离之后,访问方式就跟以前的一个地址有差别了;现在需要使用一个主库地址和多个从库地址,并且需要区分写入操作和查询操作。

为了降低实现的复杂度,业界涌现了很多数据库中间件来解决数据库的访问问题,这些中间件可以分为两类。

  • 淘宝的 TDDL( Taobao Distributed Data Layer)

    这种以代码形式内嵌运行在应用程序内部。你可以把它看成是一种数据源的代理,它的配置管理着多个数据源,每个数据源对应一个数据库,可能是主库,可能是从库。当有一个数据库请求时,中间件将 SQL 语句发给某一个指定的数据源来处理,然后将处理结果返回。

    特点:

    • 简单易用,没有多余的部署成本
    • 只支持Java
  • 另一类是单独部署的代理层方案,如早期阿里巴巴开源的 Cobar,基于 Cobar 开发出来的 Mycat,360 开源的 Atlas,美团开源的基于 Atlas 开发的 DBProxy 等等。

    这一类中间件部署在独立的服务器上,业务代码如同在使用单一数据库一样使用它,实际上它内部管理着很多的数据源,当有数据库请求时,它会对 SQL 语句做必要的改写,然后发往指定的数据源。

    特点:

    • 独立部署,方便维护升级
    • 支持多种语言
    • 所有的 SQL 语句都需要跨两次网络:从应用到代理层和从代理层到数据源