如何用AI技术降噪? QCon 广州“音视频架构实践”专场给你答案! 了解详情
写点什么

DAS 与 DAL 对比

  • 2020 年 6 月 29 日
  • 本文字数:9066 字

    阅读完需:约 30 分钟

DAS与DAL对比

概述

拍拍贷 DAS 是拍拍贷自研的数据库访问框架,支持数据库管理,ORM,并内置了分库分表引擎。为了快速交付能力,DAS 在研发初期并没有从头开发新产品,而是以携程 DAL 框架为基础做深入的定制化改造。DAS 在不断的演化升级中逐渐重构和替换掉携程 DAL 原有代码,目前除了客户端最底层的部分代码外,DAS 已经是一个全新的产品。


DAS 与 DAL 的定位基本相同,站在使用者的角度看,DAS 对 DAL 的改进主要体现在以下几个方面:


  • 增强的分库分表策略

  • 简洁高效的 DAO 设计

  • 具备元数据的 Entity

  • 灵活方便的 SqlBuilder


由于 DAL 也是我参与开发的项目,因此这个对比也是一篇自我回顾,自我总结的文章。颇有些我“杀”了我的感觉。



分库分表策略改进

分库分表策略定义

分库分表策略是支持数据库分片的数据库访问框架的核心。其作用是判断用户给出的 SQL 语句要在那些数据库或表分片上执行。判断 SQL 对应的分片范围很有技术挑战。完美的解决方案应该是:


1. 解析 SQL,确定所有的表达式,表达式包括但不限于以下>, >=, <, <=, <>, between,not between, in, not in (…), like, not like, is null, is not null,等等。


2. 计算每个表达式对应的分片范围。


3. 根据一定的规则合并各自的分片范围来生成最终的集合。


分库分表策略定义是否全面合理,直接决定了数据库访问框架的能力上限。接下来我们来对比 DAL 和 DAS 各自的策略定义。


携程 DAL 的策略接口核心定义如下:


public interface DalShardingStrategy {    String locateDbShard(DalConfigure configure, String logicDbName, DalHints hints);    String locateTableShard(DalConfigure configure, String logicDbName, String tabelName, DalHints hints);    }
复制代码


其中 hints 参数会传递表达式中参数的集合,但不会传递参数对应的表达式的操作符(=,>,<之类)具体是什么;同时接口的返回值定义为 String,因此而返回值仅能指定最多一个分片。


这种策略定义导致只有包含相等表达式或者赋值类操作的 SQL 才能准确的判断分片范围。


该策略可以支持的语句如下:


SELECTE * FROM PERSON WHERE AGE = 18


当然由于 IN 可以看做是一系列相等操作,因此经过变通也可以支持 IN,所以下面的语句也支持:


SELECTE * FROM PERSON WHERE AGE IN (18,19,20)


但是用户的 SQL 语句不仅仅只是相等或者 IN 判断,所以这种策略定义在实际使用中有较大限制。


接下来我们看一下 DAS 策略接口的核心定义:


public interface ShardingStrategy {    Set<String> locateDbShards(ShardingContext ctx);    Set<String> locateTableShards(TableShardingContext ctx);    }
复制代码


其中 ShardingContext 参数中包含了 ConditionList 属性。该属性定义了表达式集合,以及表达式之间的关系(AND,OR,NOT)。同时策略的返回值允许是分片集合,而不是某个特定分片。


这种策略定义可以支持几乎所有的表达式。可以处理表达式间的与或非关系,以及括号和嵌套括号。例如:


SELECTE * FROM PERSON WHERE (AGE > 18 OR AGE <20) AND (AGE IN (18,19,20) OR AGE BETWEEN [0,100])


通过对比我们可以了解 DAS 的策略适用于更普遍的场景,对用户的限制更少,用法更灵活,更符合用户习惯。具体设计可以参考:


https://github.com/ppdaicorp/das/wiki/DasClient-%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E7%AD%96%E7%95%A5%E8%AE%BE%E8%AE%A1


DAO 改进

DAO 是用户使用数据库访问框架的主要途径,通过 DAO,用户得以完成对数据库的增删改查操作。DAO 设计的好坏直接影响了用户的使用体验。


DAL 有着较复杂的 DAO 类层次结构。要使用 DAO,用户需要先通过 DAL console 生成标准,构建和自定义 DAO 的代码:


1. 标准 DAO 包含了最常用的单表操作,与特定表相关联。


2. 构建 DAO 包含针对单表的自定义的操作,生成的时候会跟同一表名的标准 DAO 的代码合并


3. 自定义 DAO 包装用户提供的自定义 SQL,用于跨表查询或者语法特殊的 SQL


标准 DAO 和构建 DAO 基于基础 DAO 类 DalTableDao。自定义 DAO 基于基础 DAO 类 DalQueryDao。如果涉及到事务操作,需要调用底层接口 DalClient。关系如下所示:



即使要完成最简单的数据库操作,用户也需要先生成 DAO。同时在某些特殊场景下还需要调用预定义的 DAO,步骤繁琐,学习成本高。我印象中,用户多有吐槽。


DAS 对 DAO 做了大幅优化。将 DalTableDao, DalQueryDao,DalClient 的功能合并在 DasClient 一个类并暴露给用户直接使用。要做数据库操作时,用户可以直接使用 DasClient,再也无需先生成任何 DAO 代码:



除了简化 DAO 类设计,DAS 还做了以下优化:


1. 简化 API 设计,降低学习成本。例如 DAL 中的 DalTableDao 和 DalQueryDao 一共有 34 个 query 方法,DasClient 里完成全部功能只用了 7 个。


2. 简化 Hints 的用法,去掉了 DAL 中不常用的 hints,例如 continueOnError、asyncExecution 等等,以在功能的灵活性,可理解性和系统复杂度方面取得平衡。


3. 增强 DAS 功能。例如重新设计了 SqlBuilder 类和表实体,可以让用户类似写原生 SQL 的方式自定义 SQL 语句。下面的章节里会专门介绍


DAS 在 DAO 设计上相比 DAL 有很显著的改进。与 DAL 相比,DAS 的类层次更简洁,API 设计更合理,显著降低了用户上手门槛,用起来很顺手。


在 DAL 的落地中我们原来收到的反馈是用户强烈希望 DAO 不要绑死在某张表上面,因此我们将 DAL 的 DAO 简化为 DAS 的形式。但在 DAS 落地过程中,却有用户反馈希望提供针对单表的 DAO 以方便继承,还提出希望为记录逻辑删除操作提供便利。于是我们又增加了 TableDao 对 DasClient 做了简单的封装,参数化了实体类型来满足用户自定义需求。并基于 TableDao 提供了 LogicDeletionDao 来支持逻辑删除操作。一顿操作猛如虎之后发现貌似又回到了开头,真是万万没想到啊。



Entity 改进

Entity 是数据库中的表或数据库查询结果的 Java 对应物,一般称为实体。其中表实体可以直接用于数据库的 CRUD 操作,查询实体仅用于表示查询结果。这两种实体一般通过 console 生成。实体的主要结构是字段属性,表类型的实体还会包含表名信息。


DAL 表实体

DAL 的 entity 里仅包含可赋值的了表字段,通过注解标明了对应的表字段结构。


@Entity(name="dal_client_test")public class ClientTestModelJpa {    @Id    @Column(name="id")    @GeneratedValue(strategy = GenerationType.AUTO)    @Type(value=Types.INTEGER)    private Integer id;        @Column(name="quantity")    @Type(value=Types.INTEGER)    private Integer quan;。。。    public Integer getId() {        return id;    }     public void setId(Integer id) {        this.id = id;    }     public Integer getQuantity() {        return quan;    }     public void setQuantity(Integer quantity) {        this.quan = quantity;    } 
复制代码


DAS 表实体

DAS 扩充了 DAL 表实体的定义。在普通的属性字段定义外,还新增了表结构元数据定义。下面的例子中,Person 实体代码里面的 PersonDefinition 定义表结构的元数据,其包含的 PeopleID 等定义表字段元数据。


@Tablepublic class Person {    public static final PersonDefinition PERSON = new PersonDefinition();        public static class PersonDefinition extends TableDefinition {        public final ColumnDefinition PeopleID;        public final ColumnDefinition Name;。。。        public PersonDefinition as(String alias) {return _as(alias);}        public PersonDefinition inShard(String shardId) {return _inShard(shardId);}        public PersonDefinition shardBy(String shardValue) {return _shardBy(shardValue);}         public PersonDefinition() {            super("person");            setColumnDefinitions(                    PeopleID = column("PeopleID", JDBCType.INTEGER),                    Name = column("Name", JDBCType.VARCHAR),。。。                    );        }            }    @Id    @Column(name="PeopleID")    @GeneratedValue(strategy = GenerationType.AUTO)    private Integer peopleID;        @Column(name="Name")    private String name;....    public Integer getPeopleID() {        return peopleID;    }    public void setPeopleID(Integer peopleID) {        this.peopleID = peopleID;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }
复制代码


这些元数据提供了可以生成非常丰富和全面的表达式的 API。基于这些 API,用户可以按照符合 SQL 语法的方式通过 Sqlbuilder 构建动态 SQL。比 DAL 构建 SQL 的方式要自然和简洁得多。


例如:


PersonDefinition p = Person.PERSON;p = p.inShard("0");builder = SqlBuilder.selectAllFrom(p).where(p.Name.eq(name)).into(Person.class);Person pk = dao.queryObject(builder);
复制代码


可以看到使用 DAS 的 entity 可以方便的获取表名,列名,创建表达式,指定表分片。


表达式方法除了全称,还有简写。例如 eq 和 equal 是等价的方法。下面是一个包含所有表达式全称与简写的例子:


query(selectAllFrom(p).where(p.PeopleID.eq(1)), i, 1);query(selectAllFrom(p).where(p.PeopleID.equal(1)), i, 1);query(selectAllFrom(p).where(p.PeopleID.neq(1)), i, 3);query(selectAllFrom(p).where(p.PeopleID.notEqual(1)), i, 3);query(selectAllFrom(p).where(p.PeopleID.greaterThan(1)), i, 3);query(selectAllFrom(p).where(p.PeopleID.gteq(1)), i, 4);query(selectAllFrom(p).where(p.PeopleID.greaterThanOrEqual(1)), i, 4);query(selectAllFrom(p).where(p.PeopleID.lessThan(3)), i, 2);query(selectAllFrom(p).where(p.PeopleID.lt(3)), i, 2);query(selectAllFrom(p).where(p.PeopleID.lessThanOrEqual(3)), i, 3);query(selectAllFrom(p).where(p.PeopleID.lteq(3)), i, 3);query(selectAllFrom(p).where(p.PeopleID.between(1, 3)), i, 3);query(selectAllFrom(p).where(p.PeopleID.notBetween(2, 3)), i, 2);query(selectAllFrom(p).where(p.PeopleID.notBetween(2, 4)), i, 1);query(selectAllFrom(p).where(p.PeopleID.in(pks)), i, 3);query(selectAllFrom(p).where(p.PeopleID.notIn(pks)), i, 1);query(selectAllFrom(p).where(p.Name.like("Te%")), i, 4);query(selectAllFrom(p).where(p.Name.notLike("%s")), i, 4);query(selectAllFrom(p).where(p.Name.isNull()), i, 0);query(selectAllFrom(p).where(p.Name.isNotNull()), i, 4);
复制代码


SqlBuilder 改进

DAL 的 SqlBuilder 比较复杂,分为单表,多表和批处理三大类,共 7 种:



可以 DAL 里面 Builder 类划分过细。


在 DAS 中,上面所有的 builder 除了 MultipleSqlBuilder 外,在 DAS 里都用一个 SqlBuilder 取代了。


同时为了简化和规范操作,DAS 增加了专门用于批量查询,更新的 BatchQueryBuilder,BatchUpdateBuilder 以及专门用于存储过程调用的 CallBuilder 和 BatchCallBuilder。如下所示:



此外,SqlBuilder 增加了生成语句的静态方法,可以让用户以符合 SQL 语法的方式写创建动态 SQL。


示例如下:


import static com.ppdai.das.client.SqlBuilder.*; // 查询SqlBuilder builder = selectAllFrom(p).where(p.PeopleID.eq(j+1)).into(Person.class);Person pk = dao.queryObject(builder); builder = selectAllFrom(p).where(p.PeopleID.eq(j+1)).into(Person.class).withLock();Person pk = dao.queryObject(builder); SqlBuilder builder = select(p.Name).from(p).where().allOf(p.PeopleID.eq(k+1), p.Name.eq("test")).into(String.class);String name = dao.queryObject(builder); SqlBuilder builder = select(p.PeopleID, p.CountryID, p.CityID).from(p).where(p.PeopleID.eq(k+1)).into(Person.class);Person pk = dao.queryObject(builder); // 插入SqlBuilder builder = insertInto(p, p.Name, p.CountryID, p.CityID).values(p.Name.of("Jerry" + k), p.CountryID.of(k+100), p.CityID.of(k+200));assertEquals(1, dao.update(builder)); // 更新SqlBuilder builder = update(Person.PERSON).set(p.Name.eq("Tom"), p.CountryID.eq(100), p.CityID.eq(200)).where(p.PeopleID.eq(k+1));assertEquals(1, dao.update(builder)); // 删除SqlBuilder builder = deleteFrom(p).where(p.PeopleID.eq(k+1));assertEquals(1, dao.update(builder));
复制代码


总结

本文主要介绍策略,DAO,entity 和 SqlBuilder 的对比。


今天写这个 DAS 和 DAL 的对比让我非常感慨。我在 2013 年到 2018 年作为产品负责人和 Java 客户端主力开发,与团队一起打造了携程 DAL。 DAL 目前还在继续完善并作为主力框架产品支撑着携程每天亿万的数据库请求。我为我的团队和产品感到万分自豪。


但在 DAL 的研发中,也有很多遗憾。因为是第一次开发如此复杂,使用量如此大的产品,同时由于经验不足,我们有些使用场景假设是错误的,一些设计也存在考虑不周的情况。在使用中,我们不断收到用户的反馈。虽然尽心尽力的改进着,但由于框架产品的特殊性,一旦发布就会被所有上游代码所依赖,我们很难调整 API 来实现所有改进,有时候权衡再三,最终还是不得不放弃了一些想法。


这些遗憾在打造信业框架 DAS 框架的时候得到了弥补。几乎是奇迹般的,我有一个全新的能将所有好的想法,用户的反馈和积累的全部经验付诸实施的机会。为了做出完美的设计,易用的功能,节省用户每一步操作,我们开发团队付出了巨大的努力。DAS 凝结了我们所有的心血,在公司内部获得普遍认可和好评。现在公司将其贡献给开源社区回报社会。愿大家用起来顺手之余,心满意足的 star 我们的产品:


DAS 除了客户端外,还包括 DAS Console 和 DAS Proxy Server。其中 DAS Console 的功能是管理数据库配置和生成 Entity 类。DAS Proxy Server 可以和 DAS Client 配合使用,透明的支持本地直连和基于代理的数据库连接模式,允许用户在数据库不断增长的情况下平滑升级整体架构。关于这些的介绍请持续关注信也科技的拍码场技术公众号。


作者介绍

赫杰辉,信也科技基础组件部门主管、信也 DAS 产品负责人、布道师。图形化构建工具集 x-series 的作者。曾主持开发携程开源数据库访问框架 DAL。对应用开发效率提升和分布式数据库访问机制拥有有多年研究积累。


2020 年 6 月 29 日 07:011416
用户头像
刘燕 InfoQ 技术编辑

发布了 875 篇内容, 共 299.4 次阅读, 收获喜欢 1649 次。

关注

评论

发布
暂无评论
发现更多内容

如何在 Linux 中更改主机名?运维工程师应该都知道吧!

Ethereal

Linux 运维

【Spring Boot 快速入门】三、Spring Boot集成JUnit

小阿杰

SpringBoot 2 JUnit 内容合集 签约计划第二季

国产分布式数据库StarDB核心技术大揭秘一:内核分解之数据分片

京东科技开发者

改变生物学研究进程:AI模型打开生命信息密码

脑极体

精髓 一文带你了解VMware vSphere 网络、vSwitch、端口组!

Ethereal

Go 语言快速入门指南:第二篇 变量与常量

宇宙之一粟

golang 常量 变量 签约计划第二季 12月日更

精彩回顾 | Serverless Developer Meetup 12.04 深圳站

阿里巴巴云原生

阿里云 开发者 云原生 severless 线下活动

【Spring Boot 快速入门】四、Spring Boot集成Swagger UI

小阿杰

SpringBoot 2 swagger 内容合集 签约计划第二季

完善跨境金融区块链服务平台,支持区域开放创新和特殊区域建设

CECBC

元宇宙100讲-0x004

hackstoic

【教程直播第4期】揭秘数据迁移之 OceanBase CDC & OMS 社区版能力

OceanBase 数据库

数据库 开源 直播 课程 oceanbase

重新定义分析 - EventBridge实时事件分析平台发布

阿里巴巴云原生

阿里云 云原生 EventBridge

为什么人们不喜欢 PHP?

Ethereal

国产分布式数据库StarDB核心技术大揭秘二:智能运维管控

京东科技开发者

【Spring Boot 快速入门】五、Spring Boot集成Lombok

小阿杰

SpringBoot 2 lombok 内容合集 签约计划第二季

RingCentral铃盛技术干货精选合集

RingCentral铃盛

敏捷 前端 框架 技术专题合集

问题远比答案珍贵

mtfelix

28天写作

Dubbo3 Triple 协议简介与选型思考

阿里巴巴云原生

阿里云 云原生 dubbo HTTP 协议

5分钟详解什么是Redis?

Ethereal

数据库 nosql redis

【docker 总结】第三篇 - Container 容器

Brave

,docker 12月日更

京东云ClickHouse和ES双引擎设计在零售选品中的应用实践

京东科技开发者

手把手快速入门Spring Boot实战系列

小阿杰

SpringBoot 2 内容合集 签约计划第二季

言简意赅!什么是工业交换机?

Ethereal

KubeDL 0.4.0 - Kubernetes AI 模型版本管理与追踪

阿里巴巴云原生

阿里云 AI Kubernetes 云原生

用户增长模型:AARRR

石云升

AARRR 28天写作 增长黑客 12月日更

14 位大咖导师集结完毕,阿里云云原生加速器就等你来

阿里巴巴云原生

阿里云 云原生 加速器 招募活动

【Spring Boot 快速入门】二、Spring Boot集成MyBatis可以连接数据库啦!

小阿杰

SpringBoot 2 mybatis配置 内容合集 签约计划第二季

微信朋友圈高性能复杂度

ren

架构师训练营 4 期

【分布式技术专题】「OSS中间件系列」从0到1的介绍一下开源对象存储MinIO技术架构

浩宇の天尚

OSS Minio Minio 集群 文件服务器 12月日更

实验 | OSPF HMAC-SHA 扩展身份验证

Ethereal

OSPF 网络技术 HMAC-SHA 扩展身份验证

9倍转让,外网疯抢:科技儿童汽车的前景与“钱景”

脑极体

「云智公开课」百度沧海·存储

「云智公开课」百度沧海·存储

DAS与DAL对比_大数据_赫杰辉_InfoQ精选文章