导读: 传统行业面对数字化转型往往会遇到很多困难,比如缺乏数据管理体系、数据需求开发流程冗长、烟囱式开发、过于依赖纸质化办公等,美联物业也有遇到类似的问题。本文主要介绍美联物业基于 Apache Doris 在数据体系方面的建设,以及对数据仓库搭建经验进行的分享和介绍,旨在为数据量不大的传统企业提供一些数仓思路,实现数据驱动业务,低成本、高效的进行数仓改造。
作者|美联物业数仓负责人 谢帮桂
美联物业属于香港美联集团成员,于 1973 年成立,并于 1995 年在香港联合交易所挂牌上市(香港联交所编号:1200),2008 年美联工商铺于主板上市(香港联交所编号:459), 成为拥有两家上市公司的地产代理企业。拥有 40 余载房地产销售行业经验,业务涵盖中、小型住宅、豪宅及工商铺,提供移民顾问、金融、测量、按揭转介等服务,业务遍布中国香港地区、中国澳门地区和中国内地等多个重要城市。
本文主要介绍关于美联物业在数据体系方面的建设,以及对数据仓库搭建经验进行的分享和介绍,旨在为数据量不大的传统企业提供一些数仓思路,实现数据驱动业务,低成本、高效的进行数仓改造。
考虑隐私政策,本文不涉及公司任何具体业务数据。
美联物业早在十多年前就已深入各城市开展房地产中介业务,数据体系的建设和发展与大多数传统服务型公司类似,经历过几个阶段时期,如下图所示

我们的数据来源于大大小小的子业务系统和部门手工报表数据等,存在历史存量数据庞大,数据结构多样复杂,数据质量差等普遍性问题。此外,早期业务逻辑处理多数是使用关系型数据库 SQL Server 的存储过程来实现,当业务流程稍作变更,就需要投入大量精力排查存储过程并进行修改,使用及维护成本都比较高。
基于此背景,我们面临的挑战可以大致归纳为以下几点:
针对上述的⼏个需求,我们在平台建设的初期选⽤了 Hadoop、Hive、Spark 构建最初的离线数仓架构,也是比较普遍、常见的架构,运作原理不进行过多赘述。

我们数据体系主要服务对象以内部员工为主,如房产经纪人、后勤人员、行政人事、计算机部门,房产经纪在全国范围内分布广泛,也是我们的主要服务对象。当前数据体系还无需面向 C 端用户,因此在数据计算和资源方面的压力并不大,早期基于 Hadoop 的架构可以满足一部分基本的需求。但是随着业务的不断发展、内部人员对于数据分析的复杂性、分析的效率也越来越高,该架构的弊端日益越发的明显,主要体现为以下几点:
基于上述业务需求及痛点,我们开始了架构升级,并希望在这次升级中实现几个目标:
经过调研了解以及朋友推荐,我们了解到了 Apache Doris ,并很快与社区取得了联系,Apache Doris 的几大优势吸引了我们:
足够简单
美联物业及大部分传统公司的数据人员除了需要完成数据开发工作之外,还需要兼顾运维和架构规划的工作。因此我们选择数仓组件的第一原则就是"简单",简单主要包括两个方面:
极速性能
Doris 依托于列式存储引擎、自动分区分桶、向量计算、多方面 Join 优化和物化视图等功能的实现,可以覆盖众多场景的查询优化,海量数据也能可以保证低延迟查询,实现分钟级或秒级响应。
极低成本
降本提效已经成为现如今企业发展的常态,免费的开源软件就比较满足我们的条件,另外基于 Doris 极简的架构、语言的兼容、丰富的生态等,为我们节省了不少的资源和人力的投入。并且 Doris 支持 PB 级别的存储和分析,对于存量历史数据较大、增量数据较少的公司来说,仅用 5-8 个节点就足以支撑上线使用。
社区活跃
截止目前,Apache Doris 已开源数年,并已支持全国超 1500 企业生产使用,其健壮性、稳定性不可否认。另外社区非常活跃,SelectDB 为社区组建了专职的技术支持团队,任何问题均能快速反馈,提供无偿技术支持,使用起来没有后顾之忧。

在对 Apache Doris 进一步测试验证之后,我们完全摒弃了之前使用 Hadoop、Hive、Spark 体系建立的数仓,决定基于 Doris 对架构进行重构,以 Apache Doris 作为数仓主体进行开发:
1)纵向分域
房地产中介行业的大数据主题大致如下,一般会根据这些主题进行数仓建模。建模主题域核心围绕"企业用户"、“客户”、“房源”、"组织"等几个业务实体展开,进行维度表和事实表的创建。

我们从前线到后勤,对业务数据总线进行了梳理,旨在整理业务实体和业务活动相关数据,如多个系统之间存在同一个业务实体,应统一为一个字段。梳理业务总线有助于掌握公司整体数据结构,便于维度建模等工作。
下图为我们简单的梳理部分房地产中介行业的业务总线:

2)横向分层
数据分层是最常见的 5 层结构主要是利用 Apache Doris + Apache DolphinScheduler 进行层级数据之间 DAG 脚本调度。
存储策略: 我们在 8 点到 24 点之间采用增量策略,0 点到 8 点执行全量策略。采用增量 + 全量的方式是为了在ODS 表因为记录的历史状态字段变更或者 CDC 出现数据未完全同步的情况下,可以及时进行全量补数修正。

3)增量策略
增量的 SQL 语句不使用 where="业务时间当天"的原因是为了避免数据漂移情况发生,换言之,调度脚本之间存在时间差,如 23:58:00 执行了脚本,脚本的执行周期是 10 分钟/次,但是源库最后一条数据 23:59:00 才进来,这时候 where="业务时间当天" 就会将该数据漏掉。
where >= "辅助表记录ID"如果 Doris 表使用的是 Unique Key 模型,且恰好为组合主键,当主键组合在源表发生了变化,这时候 where >=" 业务时间-1天"会记录该变化,把主键发生变化的数据 Load 进来,从而造成数据重复。而使用这种自增策略可有效避免该情况发生,且自增策略只适用于源表自带业务自增主键的情况。
如面对日志表等基于时间的自增数据,且历史数据和状态基本不会变更,数据量非常大,全量或快照计算压力非常大的场景,这种场景需要对 Doris 表进行建表分区,每次增量进行分区替换操作即可,同时需要注意数据漂移情况。
4)全量策略
先清空表格后再把源表数据全量导入,该方式适用于数据量较小的表格和凌晨没有用户使用系统的场景。
ALTER TABLE tbl1 REPLACE WITH TABLE tbl2 表替换这种方式是一种原子操作,适合数据量大的全量表。每次执行脚本前先 Create 一张数据结构相同的临时表,把全量数据 Load 到临时表,再执行表替换操作,可以进行无缝衔接。


EXECUTE CDCSOURCE demo_doris WITH (
'connector' = 'mysql-cdc',
'hostname' = '127.0.0.1',
'port' = '3306',
'username' = 'root',
'p assword' = '123456',
'checkpoint' = '10000',
'scan.startup.mode' = 'initial',
'parallelism' = '1',
'table-name' = 'ods.ods_*,ods.ods_*',
'sink.connector' = 'doris',
'sink.fenodes' = '127.0.0.1:8030',
'sink.username' = 'root',
'sink.password' = '123456',
'sink.doris.batch.size' = '1000',
'sink.sink.max-retries' = '1',
'sink.sink.batch.interval' = '60000',
'sink.sink.db' = 'test',
'sink.sink.properties.format' ='json',
'sink.sink.properties.read_json_by_line' ='true',
'sink.table.identifier' = '${schemaName}.${tableName}',
'sink.sink.label-prefix' = '${schemaName}_${tableName}_1'
);



除以上之外,在容灾恢复、集群监控、数据安全等方面也有应用,比如利用 Doris 备份实现容灾恢复、Grafana+Loki 对集群进行指标规则告警、Supervisor 对节点组件进行守护进程监控,开启 Doris 审计日志对执行 SQL 效率进行监控等,因篇幅限制,此处不进行详细说明。
我们使用 DataX 进行离线数据导入,DataX 采用的是 Stream Load 方式导入,该方式可以通过参数控制导入批次流量,DataX 导入不需要借助计算引擎,开箱即用的特点非常方便。另外,Stream Load 导入是同步返回结果的,其他导入方式一般是异步返回结果,针对我们的架构来说,在 Dolphinscheduler上执行异步导入数据会误以为该脚本已经执行成功,影响其正常运行。如采用其他异步导入方式,建议在 Shell 脚本中 执行 show load 再利用正则过滤状态进行判断。
我们所有层级的表模型大部分采用 Unique Key 模型,该模型可有效保证数据脚本的结果幂等性,Unique Key 模型可以完美解决上游数据重复的问题,大家可以根据业务模式来选择不同的模型建表。
Catalog 方式可以使用 JDBC 外表连接,还可以对 Doris 生产集群数据进行读取,便于生产数据直接 Load 进测试服务器进行测试。另外,新版支持多数据源的 Catalog,可以基于 Catalog 对 ODS 层进行改造,无需使用 DataX 对ODS 层进行导入。
尽量把非字符类型(如 int 类型、where 条件)中最常用的字段放在前排 36 个字节内,在点查表过程中可以快速过滤这些字段(毫秒级别),可以充分利用该特性进行数据表输出。
利用 Doris 自带的 information_schema 元数据制作简单的数据字典,这在还未建立数据治理体系前非常重要,当部门人数较多的时候,沟通成本成为发展过程中最大的“拦路虎”,利用数据字典可快速对表格和字段的全局查找和释义,最低成本形成数仓人员的数据规范,减少人员沟通成本,提高开发效率。
目前我们已经完成数仓建设的初期目标,未来我们有计划基于 Apache Doris 进行中台化的改造,同时 Apache Doris在用户画像和人群圈选场景的能力十分强悍,支持 Bitmap 等格式进行导入和转换,提供了丰富的 Bitmap 分析函数等,后续我们也将利用这部分能力进行客户群体分析,加快数字化转型。
最后,感谢 Apache Doris 社区和 SelectDB 团队对美联物业的快速响应和无偿支持,希望 Doris 发展越来越好,也希望更多的企业可以尝试使用 Apache Doris。
目录:一、简介二、HQL的执行流程三、索引四、索引案例五、Hive常用DDL操作六、Hive常用DML操作七、查询结果插入到表八、更新和删除操作九、查询结果写出到文件系统十、HiveCLI和Beeline命令行的基本使用十一、Hive配置一、简介Hive是一个构建在Hadoop之上的数据仓库,它可以将结构化的数据文件映射成表,并提供类SQL查询功能,用于查询的SQL语句会被转化为MapReduce作业,然后提交到Hadoop上运行。特点:简单、容易上手(提供了类似sql的查询语言hql),使得精通sql但是不了解Java编程的人也能很好地进行大数据分析;灵活性高,可以自定义用户函数(UDF)和
.gitmodules:记录子模块信息在父项目新建submodule添加:gitsubmoduleadd 子模块仓库地址 子模块在父模块目录下的存储路径。注意:路径不能以/结尾(会造成修改不生效)、不能是现有工程已有的目录(不能順利Clone)删除:首先删除.gitmodules文件下的对应子模块信息,然后 gitrm–cached 克隆下来的项目有submodule拉取submodule: gitsubmoduleupdate--init--recursive更新submodulegitsubmoduleupdate--remote或者在submodule打开gitbash,然后用gitp
我正在尝试从组织的私有(private)存储库中检索所有问题,但运气不佳。我正在使用rubygem“github_api”,但我也尝试过使用curl访问它到目前为止,我能够检索分配给我或订阅的所有问题,但不是该特定repo存在的所有问题。使用github_apigem(https://github.com/peter-murach/github),我已经走到这一步了@github.issues.issues(:filter=>"subscribed")仅供引用:我是组织和仓库的所有者查看githubapi(问题),似乎没有一个过滤器可以返回我需要的内容。这可能吗?谢谢!
假设我有一个表单:/something/somewhere操作不会返回完整的html页面,而只是一个片段。我想让提交按钮完成它的发布工作,但是捕获这篇文章的结果并将它注入(inject)到DOM中的某处。jQuerysubmit发生在表单实际提交之前。它如何工作的一个例子是:$('#myForm').posted(function(result){$('#someDiv').html(result);});有什么办法吗? 最佳答案 描述您可以为此使用jQuery.post()和.serialize()方法。.post()Loadda
0.1环境背景互联完公司的产品项目,离不开多名工程师的协同开发,以及离不开Git仓库。Git在线仓库众多,但是对于大点的需求,大概率要付费,最大的风险还是源码不在自己手上。随着互联网的安全监管越来越多,越来越全,**************************总之内,如果是个大公司,项目源码在自己手上是最安全的;如果是小公司,搭建一个内部的Git仓库是非常方便的,降低成本。偶尔Git仓库需要外网,只需要映射到公网即可。那我们这次就来学习,搭建一个私有Git仓库,并接入公网访问。0.2思路分析私有Git仓库,需要多账号管理、多账号管理、权限管理、数据存储等等,一个Git仓库该有的功能,都应该
Angular.js路由创建如下URL:http://cooldomain.com:3000/#/searchhttp://cooldomain.com:3000/#/docs在我的文档url中,我希望有一个很长的页面部分和带有anchor链接的传统目录,以便用户可以在页面上跳转从概念上讲,目录会产生大量无效的URL,例如http://cooldomain.com:3000/#/docs#coolAPIFunction由于双哈希,这当然行不通那么-是否可以在具有路由的Angular.js应用程序中使用anchor链接? 最佳答案 您
【1】创建一个文件夹:GitResp:【2】打开Git终端:GitBashHere:进入以后先对字体和编码进行设置:在Git中命令跟Linux是一样的:(1)查看git安装版本:(2)清屏:(3)设置签名:设置用户名和邮箱:gitconfig--globaluser.name“用户名”查看用户名是否设置成功:gitconfiguser.namegitconfig--globaluser.email“邮箱”查看邮箱是否设置成功:gitconfiguser.email(4)本地仓库的初始化操作:.git目录是隐藏的:可以调出来查看:查看.git下内容:注意事项:.git目录下的本地库相关的子目录和
我正在开发一个相当大的JavaScript库(Formula.js)函数(450+)。它们中的大多数相互独立且完全独立,或者使用著名的第三方库(例如Moment.js)。为了在功能级别而不是库级别支持讨论和管理贡献,我创建了一个Gist每个函数(Cf.CONVERTGist)和一个repository对于整个图书馆。这使得在函数文档中包含函数代码变得容易(参见CONVERT文档)。我的问题是:如何使主存储库与Gists保持同步?解决方案应该:允许从主存储库和单个Gists进行更改自动将版权header包含在各个Gist中自动将与第三方库相关的评论包含在各个Gist上其他想法:我找不到很
传统图像分割——分水岭算法(watershed)文章目录传统图像分割——分水岭算法(watershed)前言一、什么是分水岭算法?二、经典的分水岭求解算法1.定义2.算法流程总结前言本篇文章主要梳理分水岭算法的原理,不涉及编程实现一些经典的分水岭算法文献:[1]VincentL,SoilleP.Watershedsindigitalspaces:anefficientalgorithmbasedonimmersionsimulations[J].IEEETransactionsonPatternAnalysis&MachineIntelligence,1991,13(06):583-598.[
小区物业管理系统是为了对小区物业实行计算机化的管理以提高工作效率且方便用户。主要功能是对物业费用、停车场管理、水电气费用管理、物业设备维修情况等进行管理和基本资料管理以及对系统自身的用户权限管理。通过本系统增强了小区管理人员与住户之是的沟通,使小区管理人员能及时了解住户的需求,并帮助住用户解决一系列的问题,从而提高了小区管理的效率。这个系统在MyEclipse软件条件下进行编写的,数据库采用的是MysqL数据库软件。使用J2EE的三大框架和mysql以其内置的数据复制功能、强大的管理工具与Internet的紧密集成和开放的系统结构为广大的用户和开发人员和系统集成商提供了一个出众的数据库平台。系