中间件middleware
内容管理
- intro
- 数据访问层
- why use DAL中间件
- 主流DAL中间件方案
- DAL浅析
本文从理论上介绍一下服务化背景下的DAL中间件的理论并浅析相关中间件
cfeng之前work的时候产品发展到分离服务不分库的阶段,所以根本不需要DAL中间件,也没有分布式事务的实践。实际上很多产品因为业务数据量比较庞大都是分库的,所以需要相关的DAL中间件进行协助
intro
数据是公司的重要资源,数据持久化也是产品的基本需求。数据库是常用的数据持久化的介质。随着业务规模的扩大,分布式架构下数据库通常也会进行分离,为了解决数据库访问规范性、并进行数据库资源的保护、安全问题预防、
并且随着业务的增长,现在的产品架构很少是单体架构的方式,也就是伴随着服务化,当产品数据量增长到一定的量级,单一的数据库便不能在支撑快速的查询,便需要进行分库分表,分库之后我们就不可能再简单使用ORM框架解决问题,而是需要配合DAL中间件进行问题的解决。
数据访问层
三层的软件架构通常分为:表示层,业务逻辑层和数据访问层。 按照单一职责,数据访问层应该是唯一可以和数据库进行交互的部分。
数据访问层需要具备的功能包括: 数据的CRUD、 事务transaction的支持【保证数据一致性】
数据库作为一种软件产品也是有很多的,为了统一不同厂商的数据库访问方式,主流的开发语言一般都定义了各自的数据库访问规范。 比如java的就是JDBC规范【定义了数据库操作接口和类,数据库厂商为了获得该类语言的用户,就会去实现这些接口,作为各自的JDBC驱动】 ----- 这样用户就可以使用标准的SQL来访问不同的数据库。
直接通过JDBC进行数据库访问还是比较麻烦的,cfeng之前的blog也提到过,就是典型的JDBC编程6步,其中很多步骤像获取驱动,获取Connection连接等都是固定的流程,实际关键的只有编写SQL语句进行解析。
使用原色JDBC的几个典型缺陷:
- 每次访问数据库需要获取Connection连接,这是一个重对象,开销大。总是创建新Connection会增加数据库压力。 为了更高效的治理这种大对象,就有池化技术, 数据库连接池以数据源的形式暴露给用户 ---- 从数据库连接池获取一个连接即可,避免浪费,常见的包括Druid等
- JDBC面向SQL,而java本身是面向对象的。 也就是我们系统中处理的是对象及属性,需要进行对象关系映射。 因此就需要对象关系映射(Object-Relation Mapping)ORM框架 ---- 将对象数据转化为SQL再通过JDBC执行, 常见包括Mybatis系列、Hibernate等
成熟的数据访问层需要具有对象关系映射和 数据源管理的能力, 在此能力基础上提供CRUD和事务能力。
why use DAL中间件
ORM框架和数据源对JDBC的底层操作进行一定程度的封装和抽象,让操作数据库变得十分便捷。那为什么还需要DAL中间件呢?(中间件是一个成品)
中间件主要意义就是封装
抽象, 通过DAL中间件,开发者不需要关注数据访问的底层实现,就只需要使用DAL中间件完成功能即可,更关注逻辑 【其实中间件功能都一样,更好的封装性、更便捷的使用,不关注底层实现,专注逻辑】
单体架构的项目数据量不大,在业务量不大的情况下,尤其是整体的综合要求低的情况下,单一数据源 + ORM框架就可以解决问题,没有必要上DAL中间件增加成本
当数据量增加之后,特别是多数据库情况下,那么就有必要使用DAL中间件来解决问题, 一个成熟的DAL中间件应该具有以下功能:
数据库资源管理
数据库连接创建和销毁的正确管理,保持数据库服务的性能和SQL执行效率;一般都支持数据库连接池的配置参数化来保证不同条件下的性能最佳
数据安全控制
除了常见的SQL注入(预编译),还有其他的一些安全控制像API校验、数据库连接字符串几种管理,数据脱敏…
读写分离
大部分产品的查询读取频率是高于写频率的,但是数据库读写会有相关的锁机制会限制读操作的想你。 读操作要求较高的场景下,就需要考虑读写分离: 主写从读,并且各个数据节点进行数据同步。 多个数据源的管理就涉及到数据源选择,也需要DAL中间件提供支持
分库分表
业务量增加,存储的数据增加,单表的性能会严重下降,往往就会进行分库分表: 使用一定算法对数据库表的数据进行分组,数据均匀落在不同的分片上(shard),分片可以是不同的数据库,也可以是不同的表; 这个情况下就需要进行路由控制, 良好的DAL中间件可以提供分片算法和分片路由; 而这些细节不需要用户考虑,用户直接像操作单表一样操作DAL即可
数据访问HA
数据是最重要的资产。核心数据必须考虑HA,数据库故障自动恢复和转移能力。比如MHA: 主库故障时,自动选择一个从库作为一个新的主库。 所以应用的后台数据源可能是动态变化的,也需要DAL来进行数据源的治理
主流DAL中间件方案
上面提到,对于小型单体项目而言, ORM框架 + 数据源 就可以满足数据访问需求。
-----------------------------
| client |
| ORM框架 ----> DataSource | -------> DB
|----------- ----------------
但是大型的分布式系统,单数据库是不能满足庞大的业务需求的,往往就需要进行数据库的扩展并伴随着HA问题,需要考虑如何进行读写分离、分库分表、数据源动态切换等问题
读写分离、分库分表、数据源的动态切换等问题的核心是多数据源的管理和切换, 多数据库访问的基本模式主要有两种: 客户端模式和代理模式
-
客户端模式 : 客户端模式中间件内部为目标数据源维护了一个数据源(连接池),数据源管理、请求路由、结果集拼装都可以在业务应用服务器上直接完成。 应用服务器直接和目标数据库建立连接。 客户端模式的中间件通常封装jar包给业务应用对接, 所以业务应用就是客户端, 客户端修改代码进行适配
实现简单、嵌入到业务应用中,不需要考虑额外组件的高可用问题,同时,也增加了耦合度
-
代理模式: 代理模式中间件 就是 独立部署了一套代理服务(就自己的server),业务应用将SQL请求发到这个代理服务中, 代理服务进行SQL的处理、路由和结果封装等事情,代理服务直接访问所有的数据库,业务应用就像访问单库一样访问代理服务即可
跨平台、跨语言支持、服务端维护升级很容易、和业务耦合度低,操作更简单
但是像我们平时使用的MQ、Redis、ELK....大多都是独立的服务,需要进行单独的server部署的,直接嵌入的比较少,因为嵌入带来的问题就是耦合,导致升级、使用较麻烦
现在市面上流行的DAL中间件还是比较多的,像MyCat、ShardingSphere等
cfeng自己浏览Github上的DAL和Sharding-Sphere, 其中Sharding-Sphere实现较为完善并且一直在进行更新维护,而DAL上次维护时间比较久,整体上感觉DAL更小一些,所以接下来介绍以下DAL,Sharding-Sphere后面也会进行详细的实战分享
GitHub - apache/shardingsphere: Distributed SQL transaction & query engine for data sharding, scaling, encryption, and more - on any database. 【Sharding-Sphere开源地址】
https://github.com/ctripcorp/dal 【DAL开源地址】,当然Youtube的Vitess也不错,后面有机会去调研一下
DAL浅析
Trip.com的DAL相比Sharding-Sphere比较小,其是基于客户端模式实现的,耦合度当然高一些,客户端模式的DAL我们其实可以把其看作一个功能更综合的超级ORM框架,整个框架包含DAL生成器和DAL客户端组件。 用户通过DAL代码生成器在线生产代码和配置,嵌入到自己的项目中,通过DAL客户端完成数据库的访问操作。
可以看到其实实现的关键还是DAL代码生成起,其需要获得DB信息生成DAL代码嵌入到用户的业务系统中。
当然其如果综合运用,可以将所有的数据源全部整合起来,生成向导指引,每个团队维护自己的DAL项目,生成自己团队需要的DAL代码。 生成器的好处就是统一的代码风格,维护更容易
嵌入到用户系统的SDK的DAL客户端由基本的ORM和DataSource两部分组成
ORM就和其他的Mybatis等类型一样负责进行对象关系映射,数据源就进行数据源治理。DAL除了提供基本的CRUD和事务功能之外,还包括上面提到过的分库之后的一些解决方案:
-
读写分离: DAL支持默认读写分离策略和智能读写分离策略。 默认就是主写从读,多个从库,随机选择或者指定。 智能读写分离针对的是对数据延迟有要求的场景,可以指定数据的新鲜度,然后就会在延迟小于新鲜度的从库选择数据【延迟低】
-
分库分表: 分片策略内置的是取模策略,用户可以配置取模的列名、模数、分片表名等参数, 支持自定义策略; 分片执行上: 批量数据自动分组、跨分片并行执行、结果集合并,整个流程如下:
-
全局唯一ID生成: 分布式场景下需要主键跨库唯一, 那比如单库主键自增方案失效, DAL也提供了一种全局唯一ID生成方案,自动生成使用分库分表的主键。DAL全局唯一ID基于的snowflake算法实现,客户端IDGen client负责提供API给用户用于获取ID,服务端IDGen Server负责生成
-
数据源动态切换: DAL客户端创建数据源后会监听服务端的连接字符串的变更,当因数据库集群主库故障发生主从切换时, DAL客户端会在收到变更通知后进行数据源动态切换实现HA — DAL使用两层数据源对象,外层是经过封装可切换数据源,内层才是真正的物理数据源。 用户直接获取的是外层数据源,当切换数据源,会创建新的物理数据源,替换当前的物理数据源,外层的引用不变
-
多渠道日志监控: 数据库的数据很重要,DAL支持监控功能,有日志记录器接口和实现类,用户可以根据需求选择不同的日志记录器。 DAL会记录组件初始化过程、连接池活动状态、SQL请求要素、数据源切换动作…的行为数据。 DAL还对接了相关的加农系统,像ES…,便于检测数据库操作性能指标和后续的数据维度统计
-
数据库连接字符串管理: 数据库连接字符串集中管理,并进行权限控制,在DAL客户端配置数据库唯一标识访问指定数据库,DAL内部完成数据库连接字符串的获取。
-
多数据库差异化封装: DAL支持MySQL、Oracle…多种数据库访问,对用户屏蔽底层实现细节,无需关注数据库类型,统一编写业务逻辑代码
因为DAL是ctrip实现的,ctrip之前主要是C#,之后转为java,所以DAL的客户端组件也是支持java和C#的。
整体上来说,DAL可能比较小众,但是毕竟是开源的,还是有很大的研究价值。
后续在进行深入探讨的时候可能还会进行源码分析和其他的DAL中间件比如Sharding-Sphere的研究,Sharding-Sphere毕竟是Apache的顶级开源项目,后续还是会分享一下的🌲