系统架构与设计(4)- 如何设计高并发系统的数据库架构 (三)


在部署 MySQL 数据库集群之前,我们先来搞清楚几个重要的概念和功能。


1. MySQL 主从复制

    MySQL 主从复制是指数据可以从一个 MySQL 数据库服务器(或数据库实例)主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。

    1) 主要用途
    
        (1) 读写分离:在开发工作中,有时候会遇见某个 SQL 语句需要锁表,导致暂时不能使用读的服务,这样就会影响现有业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作;
        (2) 数据实时备份:当系统中某个节点出现故障的时候,方便切换;
        (3) 高可用 (HA, High Availability);
        (4) 架构扩展:随着系统中业务访问量的增大,如果是单机部署数据库,就会导致 I/O 访问频率过高。有了主从复制,增加多个数据存储节点,将负载分布在多个从节点上,降低单机磁盘 I/O 访问的频率,提高单个机器的 I/O 性能;

    2) 主从形式
 
        (1) 一主N从:一主一从和一主多从是最常见的主从架构,实施起来简单并且有效,不仅可以实现 HA,而且还能读写分离,进而提升集群的并发能力;
        (2) 多主一从:从 MySQL 5.7 开始支持,多主一从可以将多个 MySQL 数据库备份到一台存储性能比较好的服务器上;
        (3) 双主复制:也就是互做主从复制,每个 master 既是 master,又是另外一台服务器的 slave。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。
        (4) 级联复制:级联复制模式下,部分 slave 的数据同步不连接主节点,而是连接从节点。因为如果主节点有太多的从节点,就会损耗一部分性能用于 replication,那么我们可以让 3 ~ 5 个从节点连接主节点,其它从节点作为二级或者三级与从节点连接,这样不仅可以缓解主节点的压力,并且对数据一致性没有负面影响。

    3) 主从复制模式

       (1) 异步模式(MySQL Async-mode)

            默认是异步的模式。MySQL 增删改操作会全部记录在 binary log 中,当 slave 节点连接master 时,会主动从 master 处获取最新的 bin log 文件。并把 bin log 中的sql relay。

            这种模式下,主节点不会主动 push bin log 到从节点,这样有可能导致failover的情况下,也许从节点没有即时地将最新的 bin log 同步到本地。
        
        (2) 半同步模式 (MySQL Semi-sync)

            这种模式下主节点只需要接收到其中一台从节点的返回信息,就会 commit;否则需要等待直到超时时间然后切换成异步模式再提交;这样做的目的可以使主从数据库的数据延迟缩小,可以提高数据安全性,确保了事务提交后,binlog 至少传输到了一个从节点上,不能保证从节点将此事务更新到db 中。性能上会有一定的降低,响应时间会变长。

        (3) 全同步模式

            全同步模式是指主节点和从节点全部执行了commit 并确认才会向客户端返回成功。

    4) 基于 binlog 记录复制

         基于 binglog 记录复制有三种复制方式:基于SQL语句的复制(statement-based replication,SBR),基于行的复制(row-based replication,RBR),混合模式复制(mixed-based replication,MBR)。对应的 binlog 文件的格式也有三种:STATEMENT, ROW, MIXED。

        (1) 基于 SQL 语句的复制(statement-based replication,SBR)
        
            这种方式就是记录 SQL 语句在 bin log 中,Mysql 5.1.4 及之前的版本都是使用的这种复制格式。
            
            优点是只需要记录会修改数据的 SQL 语句到 binlog 中,减少了 binlog 日志量,节约 I/O,提高性能。
            
            缺点是在某些情况下,会导致主从节点中数据不一致(比如 sleep(), now() 等)。

        (2) 基于行的复制(row-based replication,RBR)
        
            这种方式是 MySQL master 将 SQL 语句分解为基于 Row 更改的语句并记录在 bin log 中,也就是只记录哪条数据被修改了,修改成什么样。
            
            优点是不会出现某些特定情况下的存储过程、或者函数、或者 trigger 的调用或者触发无法被正确复制的问题。
            
            缺点是会产生大量的日志,尤其是修改 table 的时候会让日志暴增,同时增加 bin log 同步时间。也不能通过 bin log 解析获取执行过的 SQL 语句,只能看到发生的 data 变更。

        (3) 混合模式复制(mixed-based replication,MBR)
        
            MySQL NDB cluster 7.3 和 7.4 使用的 MBR 是以上两种模式的混合,对于一般的复制使用 STATEMENT 模式保存到 binlog,对于 STATEMENT 模式无法复制的操作则使用 ROW 模式来保存,MySQL 会根据执行的 SQL 语句选择日志保存方式。

    5) 基于 GTID 复制

        在 MySQL 5.6 里面,不用再找 binlog 和 pos 点,我们只需要知道主节点的 IP,端口,以及账号密码就行,因为复制是自动的,MySQL 会通过内部机制 GTID 自动找点同步。

        基于 GTID 复制实现的工作原理:

            (1) 主节点更新数据时,会在事务前产生 GTID,一起记录到 binlog 日志中;
            (2) 从节点的 I/O 线程将变更的 bin log,写入到本地的 relay log 中;
            (3) SQL 线程从 relay log 中获取 GTID,然后对比本地 binlog 是否有记录(所以 MySQL 从节点必须要开启 binary log);
            (4) 如果有记录,说明该 GTID 的事务已经执行,从节点会忽略;
            (5) 如果没有记录,从节点就会从 relay log 中执行该 GTID 的事务,并记录到 bin log;
            (6) 在解析过程中会判断是否有主键,如果没有就用二级索引,如果有就用全部扫描;


2. MySQL 读写分离

    读写分离适用的场景:读操作的频率远高于写操作的频率,写操作的耗时长于读操作的耗时,即长耗时低频率的写操作降低了整个数据库的高并发读写能力,同时读操作对数据实时性要求不高,允许一定时间的延时。

    MySQL 读写分离基本原理是让 master 数据库处理写操作,slave 数据库处理读操作,master 将写操作的变更同步到各个 slave 节点。
    
    MySQL 读写分离能提高系统性能的原因:

        (1) 物理服务器增加,机器处理能力提升,拿硬件换性能;
        (2) 主从只负责各自的读和写,极大程度缓解 X 锁和 S 锁争用;
        (3) slave 可以配置 MyIASM 引擎,提升查询性能以及节约系统开销;
        (4) master 直接写是并发的,slave 通过主库发送来的 binlog 恢复数据是异步;
        (5) slave 可以单独设置一些参数来提升其读的性能;
        (6) 增加冗余,提高可用性;

    实现读写分离的方案:

        1) 基于 MySQL Proxy 代理的方式

            MySQL 的代理最常见的是 mysql-proxy、cobar、mycat、Atlas 等。这种方式对于应用来说,MySQL Proxy 是完全透明的,应用则只需要连接到 MySQL Proxy 的监听端口即可。
            
            当然,这样 Proxy 机器可能会单点失效,但可以使用多个 Proxy 机器做为冗余,在应用服务器的连接池配置中配置到多个 Proxy 的连接参数即可。

                (1) mysql-proxy 是一个轻量的中间代理,是官方提供的 MySQL 中间件产品可以实现负载平衡,读写分离,failover 等,依靠内部一个 lua 脚本实现读写语句的判断。项目地址:https://github.com/mysql/mysql-proxy ,该项目已经六七年没有维护了,官方也不建议应用于生成环境。

                (2) cobar 是阿里提供的一个中间件,已经停止更新。项目地址:https://github.com/alibaba/cobar。

                (3) mycat 的前身就是 cobar,活跃度比较高,完全使用 java 语言开发。 项目地址:https://github.com/MyCATApache/Mycat-Server。
            
                (4) moeba(变形虫)是阿里工程师陈思儒基于 java 开发的一款数据库读写分离的项目(读写分离只是它的一个小功能),与 MySQL 官方的 MySQL Proxy 相比,作者强调的是 amoeba 配置的方便(基于XML的配置文件,用 SQLJEP 语法书写规则,比基于 lua 脚本的MySQL Proxy 简单)。更多详细介绍请参考:https://www.biaodianfu.com/amoeba.html , 下载地址:https://sourceforge.net/projects/amoeba/ 。

                (5) Atlas 是奇虎 360 的一个开源中间代理,是在 MySQL 官方 mysql-proxy 0.8.2 的基础上进行了优化,增加一些新的功能特性。 项目地址: https://github.com/Qihoo360/Atlas 。

        2) 基于应用内路由的方式

            基于 Spring 的 AOP 实现: 用AOP 来拦截 Spring 项目的 dao 层方法,根据方法名称就可以判断要执行的 SQL 类型(即是 read 还是 write 类型),进而动态切换主从数据源。

            参考项目:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter

        3) 基于 mysql-connector-java 的 jdbc 驱动方式

            使用 MySQL 驱动 Connector/J 的可以实现读写分离。即在 jdbc 的 url 中配置为如下的形示:

                jdbc:mysql:replication://master,slave1,slave2,slave3/test

            Java 程序通过在连接 MySQL 的 jdbc 中配置主库与从库等地址,jdbc 会自动将读请求发送给从库,将写请求发送给主库,此外,mysql 的 jdbc 驱动还能够实现多个从库的负载均衡。

            关于 jdbc 文档地址:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-jdbc-url-format.html
            关于读写分离文档地址:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-source-replica-replication-connection.html

        4) 基于 sharding-jdbc 的方式

            sharding-sphere 是强大的读写分离、分表分库中间件,sharding-jdbc 是 sharding-sphere 的核心模块,sharding-jdbc 可以与 Springboot 集成。官方网址:https://shardingsphere.apache.org/。

    以上四种方案各有优缺点,基于 MySQL Proxy 代理的方式对于应用来说相对简单,但是在项目稳定性、事务支持性等方面还存在问题;而基于应用内路由的方式固然灵活度比较高,但是也增加了应用逻辑的复杂度;基于 mysql-connector-java 的 jdbc 驱动和 sharding-jdbc 的方式在使用上相对简单,但限制了需要使用 java 开发。


3. 故障转移 (Failover)

    故障转移 (Failover),就是当活动的服务或应用意外终止时,快速启用冗余或备用的服务器、系统、硬件或者网络接替它们工作。简单来说就是当系统某块服务不可用了,系统其它服务模块能够自动的继续提供服务。
    
    对于要求高可用和高稳定性的服务器、系统或者网络,系统设计者通常会设计故障转移功能。比如,作为负载均衡时的 nginx、haproxy 可以支持后端检测和 backup,分布式的数据密集型应用也会包含 Failover。 系统支持 Failover,那必须要保证能有 backup 或者 replics 来作为新 "master" 继续提供服务。
    
    MySQL 自身没有实现 Failover,所以当 master 异常的时候,需要使用中间件来实现 Failover 并处理数据库切换。下面是几个常用的包含 Failover 功能的中间件:

    1) mysqlfailover

        mysqlfailover 是 mysql utilities 工具包中包含的一个重要的高可用命令,用于对主从复制架构进行健康检测以及实现故障自动转移。它会定期按指定的时间间隔探测各节点的健康状态,一旦在捕获到主节点不可用时,将触发故障转移相关动作,自动执行故障切换到当前最佳的从服务器上。同时整个主从架构内的其他从节点将指向新的主节点,自动完成主从拓扑结构更新。

        mysqlfailover 特点:

            (1) 持续监控主从主从拓扑结构健康状况,当主节点不可用时,触发自动故障转移;
            (2) 支持 GTID 全局事务标识符,传统主从模式不支持;
            (3) 支持设置故障转移首选及备选节点,支持投票选举方式选择新的主节点以及仅监测模式(不切换主从);
            (4) 支持自定义时间监测间隔;
            (5) 支持交互模式以及守护进程的模式开启 mysqlfailover;
            (6) 支持在切换前或切换后执行指定的脚本;
            (7) 支持操作记录到日志不同的粒度以及日志老化;

        mysqlfailover 限制条件:

            (1) 主从需要开启 GTID 模式 ( MySQL 使用 5.6.5 以上版本);    
            (2) 所有的 slave 端需要配置以下参数,建议主库也添加 (切换后主从模式变化):
                
                report-host
                report-port
                master-info-repository=TABLE
                relay-log-info-repository=TABLE

            (3) 权限 (mysqlfailover 工具检测及切换期间需要,主从都需要):

                SHOW SLAVE STATUS
                SHOW MASTER STATUS
                STOP SLAVE, START SLAVE, WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS, CHANGE MASTER TO
                REPLICATE SLAVE
                SUPER, GRANT OPTION, RELOAD, DROP, CREATE, INSERT, SELECT 权限

     2) MMM(Master Replication Manager for MySQL)
      
        MMM(Master Replication Manager for MySQL)是在 MySQL Replication 的基础上,对其进行优化。

        官网:https://mysql-mmm.org/

        MMM 是双主多从结构,它是 Google 的开源项目,使用 Perl 语言来对 MySQL Replication 做扩展,提供一套支持双主故障切换和双主日常管理的脚本程序,主要用来监控 MySQL 主主复制并做失败转移。

        注:这里的双主节点,虽然叫做双主复制,但是业务上同一时刻只允许对一个主进行写入,另一台备选主上提供部分读服务,以加速在主主切换时刻备选主的预热。

        优点:

            (1) 自动的主主 Failover 切换,一般 3s 以内切换备机。
            (2) 多个从节点读的负载均衡。

    3) MHA(Master High Availability)

        MHA(Master High Availability)也是在 MySQL Replication 的基础上,对其进行优化。

        MHA 是多主多从结构,它是日本 DeNA 公司的 youshimaton 开发,主要提供更多的主节点,但是缺少VIP(虚拟IP),需要配合 keepalived 等一起使用。

        要搭建 MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当 master,一台充当备用 master,另外一台充当从库。

        优点:

            (1) 可以进行故障的自动检测和故障转移(Failover);
            (2) 具备自动数据补偿能力,在主库异常崩溃时能够最大程度的保证数据的一致性。


4. 负载均衡

    负载均衡的基本思路很简单:在一个服务器集群中尽可能地的平均负载量。基于这个思路,我们通常的做法是在服务器前端设置一个负载均衡器。负载均衡器的作用是将请求的连接路由到最空闲的可用服务器上。

    在一主多从的 MySQL 集群中:一主 (Master) 没有备份的 Master,所以无法做到 Failover,即无法实现高可用。多从,使用 haproxy 实现负载均衡。

    在双主多从的 MySQL 集群中:双主使用 Keepalived 做高可用(当然也可以加上 haproxy 做负载均衡),多从使用 haproxy 做负载均衡。


5. 数据库拆分

    当我们使用读写分离、缓存后,数据库的压力还是很大的时候,这就需要使用到数据库拆分了。
      
    数据库拆分简单来说,就是指通过某种特定的条件,按照某个维度,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面以达到分散单库(主机)负载的效果。

    切分模式: 垂直(纵向)拆分、水平拆分。

    1) 垂直拆分 (专库专用)

        一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面。

        优点:

            (1) 拆分后业务清晰,拆分规则明确;
            (2) 系统之间整合或扩展容易;
            (3) 数据维护简单;

        缺点:

            (1) 部分业务表无法 join,只能通过接口方式解决,提高了系统复杂度;
            (2) 受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高;
            (3) 事务处理复杂;

    2) 水平拆分

        垂直拆分后遇到单机瓶颈,可以使用水平拆分。相对于垂直拆分的区别是:垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中。

        相对于垂直拆分,水平拆分不是将表的数据做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中 的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,主要有分表,分库两种模式。

        优点:

            (1) 不存在单库大数据,高并发的性能瓶颈;
            (2) 对应用透明,应用端改造较少;
            (3) 按照合理拆分规则拆分,join 操作基本避免跨库;
            (4) 提高了系统的稳定性跟负载能力;

        缺点:

            (1) 拆分规则难以抽象;
            (2) 分片事务一致性难以解决;
            (3) 数据多次扩展难度跟维护量极大;
            (4) 跨库 join 性能较差;

    3) 中间件

        sharding-jdbc 是轻量级的 java 框架,增强版的 JDBC 驱动。它不是做分库分表,是操作多个库多个表。sharding-jdbc 的数据分片和读写分离功能,可以简化对分库分表之后的数据相关操作。

        Mycat 实现水平拆分,对前端应用是完全透明的,不用调整前台逻辑;Mycat 不是配置以后,就能完全解决分表分库和读写分离问题。Mycat 支持两个表联表的查询,多于两个表的查询不支持。

       

posted @ 2022-09-24 22:29  垄山小站  阅读(179)  评论(0编辑  收藏  举报