分库分表中间件的高可用实践

前言

分库分表中间件在我们一年多的锤炼下,基本解决了可用性和高性能的问题(只能说基本,肯定还有隐藏的坑要填),问题自然而然的就聚焦于高可用。本文就阐述了我们在这方面做出的一些工作。

高可用存在的问题

作为一个无状态的中间件,高可用问题并没有那么困难。但是尽量减少不可用期间的流量损失,还是需要一定的工作的。这些流量损失主要分布在:


(1)某台中间件所在的物理机突然宕机。


(2)中间件的升级和发布。

由于我们的中间件是作为数据库的代理提供给应用的,即应用把我们的中间件当做数据库,如下图所示:

所以出现上述问题后,业务上很难通过重试等操作去屏蔽这些影响。这就势必需要我们在底层做一些操作,能够自动的感知中间件的状态从而有效避免流量的损失。

中间件所在物理机宕机的情况

物理机宕机其实是一种常见现象,这时候应用一瞬间就没了响应。那么跑在上面的sql肯定也是失败了的(准确来说是未知状态,除非重新查询后端数据库,应用无法得知准确的状态)。这部分流量我们肯定是无法挽救。我们所做的是在client端(Druid数据源)能够快速的发现并剔除宕机的中间件节点。

发现并剔除不可用节点

通过心跳去发现不可用节点

自然而然的我们通过心跳来探查后端中间件的存活状态。我们通过定时创建一个新连接ping(mysql的ping)一下然后立马关闭来做心跳(这种做法便于我们区分正常流量和心跳流量,如果通过保持一个连接然后一直发送类似select ‘1‘的sql这种方式的话区分流量会稍微麻烦点)。

为了防止网络抖动造成的偶发性connect失败,我们在三次connect都失败后才判定某台中间件处于不可用状态。而这三次的探活却延长了错误感知时间,所以我们三次connect的时间间隔是指数级衰减的,如下图所示:

为何不在第一次connect失败后,连续发送两次connect呢?可能考虑到网络的抖动可能会有一个时间窗口,如果在时间窗口内连续发了3次,出了这个时间窗口网络又okay了,那么会错误的发现后端某节点不可用了,所以我们就做了指数级衰减的折衷。

通过错误计数去发现不可用节点

上述的心跳感知始终有一个时间窗口,当流量很大的时候,在这个时间窗口内使用这个不可用节点的都会失败,所以我们可以使用错误计数去辅助不可用节点的感知(当然这个手段的实现还在计划中)。

这边有一个注意的点是,只能通过创建连接异常来计数,并不能通过read timeout之类的来计算。原因是,read timeout异常可能是慢sql或者后端数据库的问题导致,只有创建连接异常才能确定是中间件的问题(connection closed也可能是后端关闭了这个连接,并不代表整体不可用),如下图所示:

一个请求使用若干个连接导致的问题

由于我们需要保证事务尽可能小,所以在一个请求里面多条sql并不使用同一个连接。在非事务(auto-commit)情况下,运行多少条sql就从连接池里面取出多少连接,并放回。保证事务小是非常重要的,但是这在中间件宕机的时候会导致一些问题,如下图所示:

如上图所示,在故障发现窗口期中(即还没有确定某台中间件不可用时),数据源是随机选择连接的。而这个连接就有一定1/N(N为中间件个数)的概率命中不可用中间件导致一条sql失败进而导致整个请求失败。我们做一个计算:

假设N为8,一个请求有20条sql,
那么在这个期间每个请求失败的概率就为(1-(7/8)的20次方)=0.93,
即有93%的概率会失败! 

更为甚者,整个应用集群都会经历这个阶段,即每台应用都有93%的概率失败。 一台中间件宕机导致整个服务在十几秒内基本所有请求基本都失败,这是不可忍受的。

采用sticky数据源解决问题

由于我们不能瞬间发现并确认中间件不可用,所以这个故障发现窗口肯定存在(当然,错误计数法会在很大程度上缩短发现时间)。但理想状况下,宕机一台,只损失1/N的流量就好了。我们采用了sticky数据源解决了这个问题,使得在概率上大致只损失1/N的流量,如下图所示:

而配合错误计数的话,总流量的损失会更小(因为故障窗口短)
如上图所示,只有在故障时间内随机选择到中间件2(不可用)的请求才会失败,再让我们看下整个应用集群的情况。

只有sticky到中间件2的请求流量才有损失,由于是随机选择,所以这个流量的损失应用在1/N。

中间件升级发布过程中的高可用

分库分表中间件的升级发布不可避免。例如bug fix以及新功能添加等都需要重启中间件。而重启的时间也会导致不可用,与物理机宕机的情况相比是其不可用的时间点是可知的,重启的动作也是可控的,那么我们就可以利用这些信息去做到流量的平滑无损。

让client端感知即将下线

在笔者所知的很多做法中,让client端感知下线是引入一个第三方协调者(例如zookeeper/etcd)。而我们并不想引入第三方的组件去做这个操作,因为这又会引入zookeeper的高可用问题,而且会让client端的配置更加复杂。平滑无损的大致思路(状态机)如下图所示:

让心跳流量感知下线而正常流量保持

我们可以复用之前client端检测不可用的逻辑,即让心跳的新建连接失败,而正常请求的新建连接成功。这样,client端就会认为Server不可用,而在内部剔除调这个server。由于我们只是模拟不可用,所以已经建立的连接和正常新建的连接(非心跳)都是正常可用的,如下图所示:

心跳连接的创建在server端可以通过其第一条执行的是mysql的ping而正常流量第一条执行的是一条sql来区分(当然我们采用的Druid连接池在新建连接成功以后也会ping一下,所以采用了另一种方式区分,这个细节在这里就不阐述了)。

三次心跳失败后,client端判定Server1失败,需要将连接到server1的连接销毁。其思路是,业务层用完连接返回连接池的时候,直接给close掉(当然这个是简单的描述,实际操作到Druid数据源也是有细微的差别的)。

由于配置了一个connection最长保持时间,所以在这个时间之后肯定会对Server1的连接数为0 由于线上流量也不低,这个收敛时间是比较快的(进一步的做法,其实是主动去销毁,不过我们尚未做这个操作)。

如何判定下线Server再也没有流量

在上述小心翼翼的操作之后,在Server1下线的过程中,是不会有流量损失的。但是我们在Server端还需要判定何时不会再有新的流量,这个判定标准即是Server1没有任何一个client端的连接。 这也是上面我们在执行完sql后销毁连接从而可以让连接数变为0的原因,如下图所示:

当连接数为0后,我们就可以重新发布Server1(分库分表中间件)了。对于这一点,我们写了个脚本,其伪代码如下所示:

while(true){
    count =`netstat -anp | grep port | grep ESTABLISHED | wc -l`
    if(0 == count){
        // 流量已经为0,关掉服务器
        kill Server
        // 发布升级服务器
        public Server
        break
    }else{
        Sleep(30s)
    }
}

将这个脚本接入发布平台,即可进行滚动式上下线了。
现在可以解释下recover_time为何要较长了,因为新建连接也会导致脚本计算出来的 connection count数量增加,所以需要一个时间窗口不去建立心跳,从而能让这个脚本顺利运行。

recover_time其实是非必要的

如果我们将心跳创建的端口号和正常流量的端口号分开,是不需要recover_time的,如下图所示:

采用这种方案的话,会在很大程度上降低我们client端代码的复杂度。
但是这样无疑又给client端增加了一个新的配置,对使用人员就又多了一个负担,还得在网络上多一次开墙的操作,所以我们采取了recover_time的方案。

中间件的启动顺序问题

前面的过程是一个优雅下线的过程,但我们发现我们的中间件才上线的时候在某些情况下也不会优雅。即在中间件启动时候,如果对后端数据库刚建立的连接建立上去后由于某些原因断开了,会导致中间件的reactor线程卡住一分钟左右,这段时间无法服务,造成流量损失。所以我们在后端数据库连接全部创建成功后,再启动reactor的accept线程从而接收新的流量,从而解决这一问题,如下图所示:

总结

笔者个人感觉高可用比高性能还要复杂。因为高性能可以在线下反复的去压测,通过压测的数据去分析瓶颈,提高性能。而高可用需要应付线上各种千奇百怪的现象,本篇博客讲述的高可用方案只是我们工作的一小部分,还有很大一部分精力是处理中间件本身的问题上。但只要不放过任何一个点,将问题都能够分析清楚并解决,就会让系统越来越好。

https://mp.weixin.qq.com/s/vkvYJnKfQyuUeD_BDQy_1g

获取更多学习资料,可以加群:473984645或扫描下方二维码

原文地址:https://www.cnblogs.com/lemonrel/p/11715202.html

时间: 2024-08-02 18:03:20

分库分表中间件的高可用实践的相关文章

分库分表中间件Sharding-JDBC

数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据和高并发对数据库的冲击,是各大互联网公司不可避免的问题. 虽然很多公司都致力于开发自己的分库分表中间件,但截止目前,仍无完美的开源解决方案覆盖此领域. 分库分表适用场景 分库分表用于应对当前互联网常见的两个场景——大数据量和高并发.通常分为垂直拆分和水平拆分两种. 垂直拆分是根据业务将一个库(表)拆分为多个库

为什么要分库分表?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?

为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?如何对数据库如何进行垂直拆分或水平拆分的? 其实这块肯定是扯到高并发了,因为分库分表一定是为了支撑高并发.数据量大两个问题的.而且现在说实话,尤其是互联网类的公司面试,基本上都会来这么一下,分库分表如此普遍的技术问题,不问实在是不行,而如果你不知道那也实在是说不过去! 为什么要分库分表?(设计高并发系统的时候,数据库层面该如何设计?) 说白了,分库分表是两回事儿,大家可

分库分表中间件sharding-jdbc的使用

数据分片产生的背景,可以查看https://shardingsphere.apache.org/document/current/cn/features/sharding/,包括了垂直拆分和水平拆分的概念.还有这个框架的目标是什么,都写得很清楚 Sharding-JDBC与MyCat: 解决分库分表的中间件.但是定位不同,Sharding-JDBC定位是轻量级Java框架,以jar包的方式提供服务,未使用中间件,使用代码连接库.MyCat相当于代理,MyCat相当于数据库,直接访问MyCat就可

数据库分库分表中间件 Sharding-JDBC 源码分析 —— 分布式主键

关注**微信公众号:[芋道源码]**有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 本文主要基于 Sharding-JDBC 1.5.0 正式版 1. 概述 2.KeyGenerator 2.1 D

MyCat 读写分离 数据库分库分表 中间件 安装部署,及简单使用

MyCat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里. MyCat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL.SQL Server.Oracle.DB2.P

Windows环境Mycat数据库分库分表中间件部署

下载地址MYCAT官方网站 jdk安装配置 首先去oracle官网下载并安装jdk8,添加环境变量,JAVA_HOME设置为D:\Worksoftware\Java\jdk1.8 CLASSPATH设置为.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar path系统变量追加%JAVA_HOME%\bin; Mycat安装配置 首先添加Windows环境变量,MYCAT_HOME设置为安装目录E:\WorkSoftWare\MycatServer1.5 为了降

Mysql系列五:数据库分库分表中间件mycat的安装和mycat配置详解

一.mycat的安装 环境准备:准备一台虚拟机192.168.152.128 1. 下载mycat cd /softwarewget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz 2. 解压mycat tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz 3. 剪切mycat到/usr/local目录下

数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(六)之删除SQL

关注微信公众号:[芋道源码]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 本文主要基于 Sharding-JDBC 1.5.0 正式版 1. 概述 2. DeleteStatement 3. #p

MySQL 高可用:mysql+mycat实现数据库分片(分库分表)

本文引用于http://blog.csdn.net/kk185800961/article/details/51147029 MySQL 高可用:mysql+mycat实现数据库分片(分库分表) 什么是MYCAT: 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务.ACID.可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群 一个融合内存缓存技术.NoSQL技术.HDFS大数据的新型SQL Server 结合传统数据库和新型分布式数