写点什么

Weir:原生 TiDB 支持的数据库中间件

  • 2022 年 7 月 11 日
  • 本文字数:4581 字

    阅读完需:约 15 分钟

原文来源:https://tidb.net/blog/bf9f3adc


作者介绍:


徐成选,伴鱼基础架构负责人。后端 8 年,基础架构 6 年,传统、互联网行业均有涉猎,曾就职于小米、阿里、初创公司等, 目前在伴鱼负责基础架构团队,从 0 到 1 组建了伴鱼服务器基础架构、大数据基础架构和数据应用等团队。


本篇文章整理自 TiDB 的 TOC 成员、伴鱼基础架构负责人徐成选在 PingCAP Infra Meetup 上的演讲实录,演讲视频回顾可以在这里观看。 本文主要介绍了伴鱼基于 TiDB 自主开发的数据库中间件 Weir 的技术细节,文章将从 Weir 系统架构、核心特性、未来规划等三个方面进行分享。


背景概况


伴鱼从 2018 年开始 all in TiDB。但伴鱼在使用 TiDB 的过程当中遇到一系列 TiDB 早期的问题,比如不合理的 SQL(应用上有问题的 SQL )、索引失效(有索引,但是它不走索引)等。这些问题会带来数据库不可用或者恢复特别慢等情况,对于伴鱼来说是比较严重的。


伴鱼是基于互联网的在线学习平台,在线教育这个行业比较特殊,有明显的流量波峰和低谷,在上课时如果出现问题,不是简单地恢复服务就可以,它是要赔钱的,不但要赔钱,老师的工资还要照付,这个是比较棘手的问题。


于是伴鱼就在想如何尽量提高数据库的稳定性,解决这类问题。直接能想到的解决方法是基于伴鱼的 SDK 进行包装。



基于 SDK 进行包装,能够精准到库表级别进行控制,但是要是精准到 SQL 范式级别是很困难的。它能解决一部分问题,但不足之处有以下三个方面:


  • 第一,熔断粒度不够细。不能精确到 SQL 范式或者叫 SQL 指纹层面。

  • 第二,影响范围不可控。基于 SDK 做包装,很多服务集成了 SDK,分别去做熔断,可能大家的配置都一样,但是因为分散在各个服务的实例里面,其范围是比较大的,对于保护 TiDB 来讲效果上会有一定的折扣。

  • 第三,多语言如何兼容。公司内的算法团队经常有 Python 、 Java 的程序需要访问数据库,也有熔断的需求。


所以后面就想将熔断能力下沉到中间层,架构形式如下图:



上述 架构带来了许多优点


  • 第一,可以按照库表进行熔断。

  • 第二,可以按照 SQL 的范式或指纹统一熔断,这样控制力更充足,并且控制的粒度也足够精细。

  • 第三,能够满足多语言的诉求。


同时,如果某一 SQL 范式出现了熔断,但其他 SQL 范式,就算对应相同的表,也是可以正常通过的。熔断之后误杀的几率或者范围会更小。


基于此思想,伴鱼开发出了一款不一样的开源数据库中间件—— Weir。


为什么说 Weir 是不一样的数据库中间件呢?


  • 首先, Weir 是面向 NewSQL 的。

  • 其次, Weir 是一款侧重数据库治理的中间件。


当然,它同样具有分库分表解决容量问题 (基于 TiDB) 以及作为连接池保护数据库的能力。


Weir 的系统架构


组件构成


Weir 主要分为三部分。



从图中可以看到标黄的部分是 Weir 的三个组件


  • 最左边的 DB platform,是 Weir 的 web 管控平台。

  • 中间是 Weir Controller,主要负责一些代理的配置或者后端 TiDB 的配置管理。

  • 最主要的部分是 Weir Proxy,主要承接线上 SQL 流量。伴鱼在这薄薄的一层 Proxy 做了很多功能,包括路由、熔断、限流、SQL Waf 等。SQL Waf 主要是指限定某类 SQL 请求,比如某些有 update 或者 delete 操作,但是又不带着 where 条件的请求。当然还可以做更多的功能,像影子库压测之类的。


配置管理的部分,有加载配置,还有触发配置变更等操作,这个主要与配置的形式有关。伴鱼把配置存在 etcd 里面,但是没有用其 Watch 机制,更多是采用主动触发的方式发一遍通知,同时得到配置变更的反馈以及验证配置的指纹来保证所有实例的配置保持一致。


模块构成


下面将分别介绍这三个组件。上面是从请求的角度分析,这里是从模块的层次角度再把 Proxy 展开一下。



最上层是各个应用,包括了 Go、Java 等语言,最底层是 DB 层,是 TiDB 的各个集群。中间是 Weir 的几个关键组件,右边标蓝的三部分是周边管理组件,包括监控、Web 平台、管控服务等,最左侧中间这一块是 Weir 的 Proxy 层。


最上面的部分由会话层做连接接入,最下面有后端连接池,以起到保护的作用。中间上下行都会涉及到 MySQL 协议解析层,因为伴鱼主要用 TiDB,所以这里以 TiDB 为例展开讲。


中间会有租户授权的功能,这里除了做 MySQL 协议的握手,还会有租户的分配,再就是 SQL 解析。在 SDK 层面,想做 SQL 解析是比较重的,但是在 Proxy 层,SQL 解析就比较好做一些。


图中间橙色的部分就是功能层。当伴鱼做了以上各种功能以及 SQL 解析之后,还要把请求转发到后面。 Proxy 基于 SQL 的状态机来驱动逻辑处理 ,比如根据事务开启的状态及其当前的状态,以及接到的命令决定下一步做什么,失败的时候转到什么状态等,基于这样状态机去驱动请求直接转发到后端。


绿色代表请求方向,浅蓝色代表应答方向。这个过程和传统的 MySQL 中间件的还不太一样,许多传统的中间件在结果处理的时候会做结果的拼接。而 Weir 没有那么重,它可以直接判断结果的类型,除了网络错误,还可以根据 MySQL 逻辑层面的 Error Packet 进行错误计数触发熔断。不管是 TiDB 还是 MySQL 都会有 text 文本类型的应答以及 binary 应答,处理两种逻辑(分库分表时)还是比较麻烦的。但是在 Weir 里面不需要做这类事情,因为不需要做结果拼接。


Weir 的核心特性


Weir 有 四个核心特性


  • 第一,柔性多租户,区别于 K8s 这种会分配物理资源。

  • 第二,自适应的熔断和限流,这个功能是打造 Weir 这个项目的初衷。

  • 第三,连接池,这个是一般中间件都会有的功能。

  • 最后,周边 SQL 的统计、监控,比如一些基于 SQL 的统计可以做像 SQL 审计等功能。


柔性多租户



这里有几个概念:第一,把 Namespace 作为租户,同时,Weir 的集群可以有多个租户。这样一来,一套 Weir Proxy 集群可以代理多个 TiDB 的集群,部署起来会更加简单。


第二,租户的资源,每个租户对应一个 TiDB cluster,一个 TiDB cluster 可以对应多个租户。这里的意思是,租户代表 TiDB 集群,当 TiDB 集群被多个应用访问的时候,也可以分配多个 Namespace 来分别被不同的应用去使用。


第三,租户的关联。MySQL 在认证的时候有几个要素,用户名、密码、IP 等等,Weir 是根据用户来绑定到租户的。伴鱼没有相同的用户名,不存在用户名冲突的现象,也可以做成根据用户名加密码来绑定租户。


自适应的熔断与限流


熔断过程本质上归纳为三个状态机: 开启状态、关闭状态、半开启状态 。半开启状态是指,触发了熔断之后要有恢复过程,不能一直熔断。



下图是 Weir 的 SQL 熔断过程,图中标注了熔断的粒度。Weir 与基于 SDK 的熔断最不一样的一点是:能够精确到 SQL 范式的精度,粗粒度可以达到 Namespace 的级别。



图中最上面是正常的请求,后面开始产生一些报错。这个报错有两类,一类是网络报错,第二类当前没有做,但可以基于 MySQL 的 Error packet 来做逻辑上的报错统计。


当报错达到到一定次数的时候,就开始触发熔断(中间红色部分)。触发熔断之后,请求在 Proxy 层会返回固定的错误,而不会打到后端的 TiDB cluster。同时,熔断触发之后可以尝试去看有无恢复,如果没有恢复,即仍停留在熔断状态,继续阻断往 TiDB 的请求。


如果发现 TiDB 慢慢恢复了,可以开始继续往下走正常的流程,然后转发到 TiDB 集群,这里跟伴鱼在 RPC 层面的熔断是类似的,起到防止雪崩、防止事情变得更坏的作用。这避免了之前出现的直接把集群 CPU 打满、恢复特别慢的问题,一有间歇性报错,就立刻恢复了,剩下的需要去找原因。限流的情况也是类似的,这里就不展开了。


连接池


连接池在中间件里面很通用,但是他们之间又有所不同,左边是普通命令的连接池,右边是事务 + Prepare 的连接池。大家如果了解 MyCat 这一类中间件会发现它们对于 Prepare 也会单独做处理,尤其是涉及到分库分表。


  • 普通命令连接池


先讲普通命令,普通命令是当请求从客户端进来之后,获取到了连接,然后可以去后端 TiDB 集群对应的连接池中,任意取得一个连接,比如前端可能是 1,后端可能取的是 26,然后来完成请求,再应答就可以了。但是对于事务的连接来讲就不一样了,因为事务是有状态的,这时候就需要持有这个事务,所以下图右边就不太一样,前面拿到 1,后面可能就一直要以 client 1‘ 这样的连接来处理事务里面的 SQL 语句。



  • 事务 + Prepare 连接池


在事物 + Prepare 连接中,Prepare 是二进制协议的,跟普通的 Query 比如 SQL 还不太一样,如果是在分库分表中实现 Prepare,往往是在每个命令 Prepare 阶段的时候 session 持有这个连接,在执行阶段将其转成文本协议走分库分表的判断逻辑。如果不这样做,Prepare 就会有一坨分库分表的逻辑处理,转化成文本协议和直接处理 prepare 的分库分表逻辑都是比较复杂的。但是因为 Weir 不做分库分表,所以我们可以简化处理,Prepare 的前后端连接处理与事务连接处理保持一致。绑定连接后对于 Prepare 各阶段的每个命令都是直接转发,最后关闭连接,实践起来很简单,并且正确性容易验证,最重要的是不涉及协议的转换,也就是文本协议和二进制协议互相转的问题。


成熟的管控平台


以下将分享一下我们的管控平台。有了这样的管控平台,可以做到流程化和规范化,业务在平台申请,DBA 在平台上分配,分配完成后,业务可以拿着平台提供的信息去使用。同时平台还可以提供监控的链接,可以直接跳转查看 Namespace 的监控情况。



SQL 的统计监控的这里有两部分,一是 Proxy 集群本身的监控,如 QPS、时延、Namespace 、连接数、报错等。



下面是某 Namespace 的监控,里面也包含了具体业务的表,QPS、SQL 的连接数、事务的数量等等一些统计,对于排查问题提供了很大的便利。



Weir 的未来规划


前面主要介绍了 Weir 的功能特性,下面主要介绍一下 Weir 的未来规划。下图蓝色的部分是伴鱼当前已经实现的功能,绿色是当前正在做的一些功能,黑色是未来要做的事情。



首先,Waf 功能。当需要针对某个 SQL 范式做禁止的时候,DBA 就可以在 Web 平台上配置、下发,之后可以针对这个 SQL 去做禁止。或者,当 update 或者 delete 操作时没有带 Where 条件,默认不予执行。


第二,多机房的感知和路由,在机房双活路由的时候感知本地机房能够提升一部分效率。


正在做的有 SQL 的审计、慢 SQL 的统计、影子库压测等。影子库压测是比较有用的特性,现在压测经常需要造一批假数据单独标记出来,或者要做单独的 TiDB 集群等等,但是如果有影子库压测的话一方面业务可能感知不到这些事情,另外管理上会更加集中和方便。


最后是部署形态上,Weir 还想做 Database 的 mesh 层,进一步往云原生靠,这能够带来一些性能的提升,这也是未来行业发展的一个趋势。


结语


Weir 的 GitHub 地址是 https://github.com/tidb-incubator/weir,伴鱼整个的开发过程是基于这个仓库来完成的,这个项目跟伴鱼内部用的是完全一样的,感兴趣的同学可以关注。


拓展阅读




💡 更多 TiDB、TiKV、TiSpark、TiFlash 等技术问题可登录 AskTUG.com ,与全球 TiDB User 随时随地交流使用心得~ 另外,AskTUG 「 认证功能 」最新上线,完成团队认证可额外获得 +200 经验值和 200 积分 ,授予 “认证会员”徽章。更有问题处理「 加急 」特权等你~详情了解请点击「 阅读原文 」!


Ps:发布技术文章可获得 100-200 经验值及积分奖励。内容深度、文章结构完整度、图文并茂都是加分项哟~


发布于: 10 分钟前阅读数: 3
用户头像

TiDB 社区官网:https://tidb.net/ 2021.12.15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
Weir:原生 TiDB 支持的数据库中间件_实践案例_TiDB 社区干货传送门_InfoQ写作社区