面向数据编程:ECS设计模式在数仓中应用的思考

本文涉及的产品
云原生数据仓库AnalyticDB MySQL版,基础版 8ACU 100GB 1个月
简介: 前言 作为一个从Java转去做大数据的开发,尤其是基于Hiv采用SQL的开发来说,抛弃了使用了很久的OOP,面向对象编程的设计思想后,总觉得有点不习惯。传统的web项目中,对SQL的使用更多还是在数据的增删改查上,而在大数据领域,更多复杂的数据分析,数据交并差的处理,导致SQL代码量急速增加,可维护性大幅降低。而SQL本身就是一个面向过程描述的语言,Java中常见的MVC,MVVP等设计模式也不

前言

作为一个从Java转去做大数据的开发,尤其是基于Hiv采用SQL的开发来说,抛弃了使用了很久的OOP,面向对象编程的设计思想后,总觉得有点不习惯。传统的web项目中,对SQL的使用更多还是在数据的增删改查上,而在大数据领域,更多复杂的数据分析,数据交并差的处理,导致SQL代码量急速增加,可维护性大幅降低。而SQL本身就是一个面向过程描述的语言,Java中常见的MVC,MVVP等设计模式也不适合套用在SQL身上。那么,是不是应该存在一种设计模式,适用于面向过程的编程设计呢?

带着这样的疑问,我开始关注面向数据编程。面向数据编程,核心在于数据。我希望数据可以变得更加灵活,方便开发者对它进行加工。同时,加工过程可以做到高内聚,低耦合。带着这样的需求,查阅了很多资料。直到有一次,无意中看到游戏引擎Unity3D采用的ECS设计模式,突发奇想,意识到这是不是可以满足我的需求呢?

关于游戏开发中的ECS简单介绍

ECS是Entity-Component-System三个单词的缩写。最早是在2002年的Game Dungeon Siege上被提出来,是为了解决游戏设计中,物体直接数据交互和性能的问题。

它在游戏开发中的演变逻辑可以参考这篇文章:https://zhuanlan.zhihu.com/p/32787878

简单的说,Entity、Component、System分别代表了三类模型。

实体(Entity):实体是一个普通的对象。通常,它只包含了一个独一无二的ID值,用来标记它是一个独立的对象。通常使用整型数字作为它的实现。

组件(Component):对象一个方面的数据,以及对象如何和世界进行交互。用来标记实体是否需要进行这一方面的处理,通常使用结构体,类或关联数组实现。

系统(System):每个系统不间断地运行(就像每个系统运行在自己的私有线程上),处理标记使用了该系统处理的组件的每个实体。

 

它跟传统OOP编程有什么不一样呢?

最核心差异点在于:传统OOP编程里,我们会先对编程对象进行虚拟化抽象,将共同的一类数据归到父类或者接口中,子类继承或实现对应的接口。在游戏开发中,父类往往是被锁死的,而一旦需要对逻辑作出修改,要么重写实现,要么继承基类进行覆盖。但游戏策划的创意是天天都可能会变化的。从而造成大量子类重复出现,大幅降低。此外,在对于C++语言中,使用对象池优化时就会造成灾难性的后果——一种类型一个池。

其次,从计算机底层数据传输上来说,传统OOP在传递数据时都是采用对象进行封装。但通常需要用到的数据只是对象中一两个属性。对于大部分web应用上来说,多读取的对象数据影响不大,但对数据密集型计算(例如游戏图像领域),则对性能会产生影响。

 

而ECS就是可以解决以上问题。ECS全写即“实例-组件-系统”的设计模式。简言之,实例就是一个游戏对象实体,一个实体拥有众多的组件,而游戏系统则负责依据组件对实例做出更新。

举个例子,如果对象A需要实现碰撞和渲染,那么我们就给它加一个碰撞组件和一个渲染组件;如果对象B只需要渲染不需要碰撞,那么我们就给它加一个渲染组件即可。而在游戏循环中,每一个系统都会遍历一次对象,当渲染系统发现对象持有一个渲染组件时,就会根据渲染组件的数据来执行相应的渲染过程。同样的碰撞系统也是如此。

也就是说游戏对象需要什么就会给自己加一个组件。而系统会依据游戏对象增加了哪些组件来做出行为。换言之实例只需要持有必要的数据,由系统负责逻辑就行了。由于只需要持有必要数据,因此对于缓存是非常友好的。这也就是ECS模式能和数据驱动很好结合的一个原因。

 

对于ECS在数仓建设应用中的一些思考

对于数仓建设,也是一个面向数据驱动的开发。因此我将ECS和数仓的代码联系起来,思考如何将ECS的设计模式在数仓中应用。我给出了以下的一些想法:

一个基本假设:

在数仓中,如果可以抛弃pk依赖后,一张表就是一群Schema的合集。

这是我对数仓中数据构成的根本假设。如果一张表里的其他Schema被PK约束,自然会导致Schema直接产生逻辑关系。如果没有PK,那么各个Schema互相之间是平等的,Schema之间可以互相组合。表只是由一个个的Schema填充而成的。这样听起来是不是很像Entity和Component之间的关系呢?

所以我大胆的列出一个映射关系。

与ECS的关系映射:

Entity对应于数仓中的Table,Component对应Schema,System对应数仓中SQL逻辑。

image.png

 

对于一张表来说,又若干个Schema构成。对于SQL代码来说,它关心的只是要用到的Schema,而不是表的业务逻辑。一张表可以由多个不同的SQL共同产出。所以依赖关系可以是这样的:

image.png

SQL只需关心它加工逻辑中需要用到什么Schema,产出什么Schema;Table只需要关心,它的业务逻辑是由哪几个Schema组成;而Schema自己只需要关心,自己代表什么原子含义。

ECS模式下的SQL伪代码简单实现

在SQL语言,我们一般代码会写成这样:

Select A1

From Tbale1

Where Condition1

A1代表我们需要的Schema,Table1是表,Condition1是需要满足的条件。

对于ECS架构来说,这样写违背了System不跟Entity交互的原则。理想的ECS实现是:

Select Table1.A1

Where  Condition1

如果不同表中的Schema都是平等的,那么只需要指出使用的是哪个表里的Schema,和对应的加工条件。无需再将表名列入其中。

当然,有人会说,不就是多个From Table嘛,多写这一句话也不会怎么样。

是的,但大多数数仓开发中,并不是简简单单的一张表的处理。往往我们还会遇到很多表之间交并差的情况。这个时候,我们写的最多的代码是:

select t1.a,t2.b

from (

select *

from table1

where condition1

) as t1 left

join table2 as t2

on condition2

 

对于一个ECS架构,我们的实现是:

select table1.a,table2.b

where condition1 and condition2

这样看起来,代码是不是就简洁明了多了呢?(当然,现阶段SQL语法并不支持这种写法)

 

另外,我们在处理表数据的时候,经常还会遇到这样一种情况:

insert into tmp_table1

select a1,a2,a3……a31,a32,cast(a33 as bigint) as b1

from table

where condition1

 

inset into result_table1

select a1,a2,a3……a31,a32,b1+1 as c

from tmp_table1

 

inset into result_table2

select a1,a2,a3……a31,a32,b1+2 as c

from tmp_table1

从a1到a32 一共32个列名,其实是不需要做任何特殊处理的,只需要根据condition1条件筛选出来。之后我们又要带着a1……a32在两张结果表中进行插入。且不提这样复制粘贴列名操作十分麻烦,容易出错,就是我们是否有必要这么做?

我们的诉求可能只是修改某一张表里的某一列值,但不得不把这张表的其他字段反复提取插入。

根据ECS的设计思想,所有列值都是互相平等的。每张表(Entity)只是由列(Component)填充,Sql(System)只是负责逻辑行为。

那么,实际操作应该是:

insert into tmp_table1

select table.a1,table.a2,table.a3……table.a31,table.a32

where condition1

 

insert into tmp_table2

select ,cast(table.a33 as bigint) as b1

where condition1

 

inset into result_table1

from tmp_table1 add colum tmp_table2.b1+1 as c

 

inset into result_table2

from tmp_table1 add colum tmp_table2.b1+2 as c

 

(以上都是伪代码)

这样写看上去代码行数没变化,但好处是,如果table中结构发生变更,只需修改上层tmp_table1的结构即可,对结果表无感知。这一点上反而有点像OOP中的继承关系。

 

总结

思考将ECS设计模式引入数仓设计,本意是希望开发者可以更加关注于逻辑,关注数据如何处理,也就是S的部分。业务则由从列构建表的时候产生。将表结构和数据处理逻辑进行拆分,从而希望能提升SQL代码的可读性和结构性。

SQL本身是一个非常优秀的描述型语言,给数据处理带来了极大的便利。但在表结构越发复杂的今天,我已经感觉到传统的SQL的局限性。希望通过ECS设计模式的思考,可以大家带来更多的启发,可以让SQL代码像其他工程语言一样,简洁优雅。

 

 

 

 

相关实践学习
AnalyticDB MySQL海量数据秒级分析体验
快速上手AnalyticDB MySQL,玩转SQL开发等功能!本教程介绍如何在AnalyticDB MySQL中,一键加载内置数据集,并基于自动生成的查询脚本,运行复杂查询语句,秒级生成查询结果。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
目录
相关文章
|
8天前
|
机器学习/深度学习 人工智能 运维
企业内训|LLM大模型在服务器和IT网络运维中的应用-某日企IT运维部门
本课程是为某在华日资企业集团的IT运维部门专门定制开发的企业培训课程,本课程旨在深入探讨大型语言模型(LLM)在服务器及IT网络运维中的应用,结合当前技术趋势与行业需求,帮助学员掌握LLM如何为运维工作赋能。通过系统的理论讲解与实践操作,学员将了解LLM的基本知识、模型架构及其在实际运维场景中的应用,如日志分析、故障诊断、网络安全与性能优化等。
28 2
|
1天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
4天前
|
设计模式 监控 数据库连接
Python编程中的设计模式之美:提升代码质量与可维护性####
【10月更文挑战第21天】 一段简短而富有启发性的开头,引出文章的核心价值所在。 在编程的世界里,设计模式如同建筑师手中的蓝图,为软件的设计和实现提供了一套经过验证的解决方案。本文将深入浅出地探讨Python编程中几种常见的设计模式,通过实例展示它们如何帮助我们构建更加灵活、可扩展且易于维护的代码。 ####
|
4天前
|
存储 安全 关系型数据库
Linux系统在服务器领域的应用与优势###
本文深入探讨了Linux操作系统在服务器领域的广泛应用及其显著优势。通过分析其开源性、安全性、稳定性和高效性,揭示了为何Linux成为众多企业和开发者的首选服务器操作系统。文章还列举了Linux在服务器管理、性能优化和社区支持等方面的具体优势,为读者提供了全面而深入的理解。 ###
|
5天前
|
存储 缓存 前端开发
如何优化 SSR 应用以减少服务器压力?
如何优化 SSR 应用以减少服务器压力?
|
6天前
|
存储 数据挖掘
服务器数据恢复—用RAID5阵列中部分盘重建RAID5如何恢复原raid5阵列数据?
服务器数据恢复环境: 一台服务器挂接一台存储,该存储中有一组由5块硬盘组建的RAID5阵列。 服务器故障: 存储raid5阵列中有一块硬盘掉线。由于RAID5的特性,阵列并没有出现问题。工作一段时间后,服务器出现故障,用户方请人维修。维修人员在没有了解故障磁盘阵列环境的情况下,用另外4块硬盘(除去掉线的硬盘)重新创建了一组全新的RAID5阵列并完成数据同步,导致原raid5阵列数据全部丢失。
|
12天前
|
设计模式 开发者 Python
Python编程中的设计模式:从入门到精通####
【10月更文挑战第14天】 本文旨在为Python开发者提供一个关于设计模式的全面指南,通过深入浅出的方式解析常见的设计模式,帮助读者在实际项目中灵活运用这些模式以提升代码质量和可维护性。文章首先概述了设计模式的基本概念和重要性,接着逐一介绍了几种常用的设计模式,并通过具体的Python代码示例展示了它们的实际应用。无论您是Python初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和实用的技巧。 ####
|
14天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
19天前
|
Python
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
这篇博客文章是关于如何使用Flask框架上传特征值数据到服务器端,并将其保存为txt文件的教程。
25 0
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
|
8天前
|
机器学习/深度学习 人工智能 边缘计算
高算力服务器的应用场景
【10月更文挑战第18天】高算力服务器作为现代信息社会不可或缺的计算资源,正广泛应用于各行各业。从人工智能到科学研究,从智能交通到数字孪生,它为复杂的计算任务提供了不可替代的支持。
12 0

热门文章

最新文章