本文首发于掘金,从架构、数据集成、存储、调度、前后端选型,再到真实部署中的踩坑与调优,全面拆解一款开源 BI 的技术细节。如果你正在自研数据平台或选型 BI,欢迎收藏交流。
一、这是一篇什么文章?
先交代背景:JVS-BI 是一款 私有化部署、全源码开放 的自助式数据分析平台。我因为项目需要,深度研究了它的技术栈,并在测试环境完整部署过一遍。
这篇文章不是官方文档的复读机,而是我从选型对比、实际配置到生产化改造中积累的 技术笔记 + 踩坑实录。全文 3500 字左右,建议先赞后看。
你会看到:
- 一套完整的 BI 系统分层架构(附官方架构图)
- 为什么选 Spring Cloud Alibaba + DataX + Doris + XXL-JOB?
- DataX 从 MySQL 同步到 Doris 的完整配置(含 JSON 示例)
- Doris 建表与查询的实战 SQL
- 前端大屏设计器:Vue3 + 拖拽组件 + 图层管理
- 独家踩坑:DataX OOM、Doris 分区设计失误、XXL-JOB 重复执行
二、整体架构概览(先看图)
JVS-BI 采用经典的四层架构:
- 基础框架层:JVS 统一底座(用户、认证、消息、微服务治理)
- 数据接入层:JDBC / API / 文件 多源连接,屏蔽差异
- 数据加工层:可视化 ELT + DataX 抽取 + Doris 数仓存储
- 数据应用层:图表、报表、大屏、API 服务
三、后端核心组件选型与详解(含代码)
3.1 微服务底座:Spring Cloud Alibaba
| 组件 | 具体技术 | 作用 |
|---|---|---|
| 服务注册/配置 | Nacos | 动态配置、服务发现 |
| 服务调用 | OpenFeign | 声明式 HTTP |
| 网关 | Spring Cloud Gateway | 路由 + 鉴权 |
为什么不是 Dubbo 或纯 Spring Boot?
- Nacos 同时搞定配置和服务发现,比 Eureka 香。
- BI 系统主要是 IO 密集型(查数据),RPC 调用不频繁,OpenFeign 足够轻量。
- 对中小团队来说,Spring Cloud 生态学习成本更低。
3.2 数据抽取:DataX(附 JSON 配置)
DataX 是阿里开源的异构数据同步神器,JVS-BI 将其封装成了界面化任务。
一个真实的 MySQL → Doris 同步配置(从页面导出的 JSON): json
{
"job": {
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "root",
"password": "123456",
"connection": [{
"jdbcUrl": ["jdbc:mysql://localhost:3306/sales"],
"table": ["orders"]
}]
}
},
"writer": {
"name": "doriswriter",
"parameter": {
"feLoadUrl": ["fe_host:8030"],
"beLoadUrl": ["be_host:8040"],
"database": "jvs_bi_ods",
"table": "orders",
"maxBatchRows": 500000,
"maxBatchByteSize": 104857600
}
}
}
],
"setting": {
"speed": {
"channel": 3,
"byte": 1048576
}
}
}
}
3.3 数据存储:Apache Doris(含建表 SQL)
Doris 是 MPP 列存数据库,支持 MySQL 协议,学习成本极低。
选型对比(个人实践后的结论):
| 特性 | Doris | ClickHouse | StarRocks |
|---|---|---|---|
| SQL 兼容性 | ✅ 高 | ⚠️ 有差异 | ✅ 高 |
| 实时写入 | ✅ 高吞吐 | ✅ 高吞吐 | ✅ 高吞吐 |
| 主键更新 | ✅ Unique Key | ❌ 需重建 | ✅ 支持 |
| 运维复杂度 | 低(FE/BE) | 中 | 低 |
| 社区活跃度 | Apache 顶级 | 高 | 中 |
建表示例(聚合模型,按天分区):
sql
CREATE TABLE sales_daily (
sale_date DATE,
product_id INT,
region VARCHAR(20),
amount DECIMAL(18,2)
) AGGREGATE KEY(sale_date, product_id, region)
PARTITION BY RANGE(sale_date) (
PARTITION p20260101 VALUES [('2026-01-01'), ('2026-01-02')),
PARTITION p20260102 VALUES [('2026-01-02'), ('2026-01-03'))
)
DISTRIBUTED BY HASH(product_id) BUCKETS 10
PROPERTIES (
"replication_num" = "1",
"storage_format" = "V2"
);
为什么不用 MySQL 直接分析?
- 业务库行存,复杂聚合直接打挂 OLTP。
- Doris 列存 + 向量化,亿级数据秒级响应。
3.4 调度引擎:XXL-JOB
- 每个数据集对应一个 XXL-JOB 任务,通过
@XxlJob注解触发 DataX 或 Doris SQL。 - 支持前置/后置任务依赖(比如先同步 ODS,再加工 DWD)。
- 提供 Web 界面,可以手动触发、查看日志。
3.5 消息队列:RabbitMQ
- 用途:异步处理任务状态更新、发送通知(邮件/钉钉)、记录执行日志。
- 为什么不选 Kafka?BI 系统的任务量级远小于日志系统,RabbitMQ 更轻,且支持复杂的路由。
四、前端大屏设计器技术实现(掘金前端读者必看)
JVS-BI 的大屏设计器是基于 Vue3 + Element Plus + 自研拖拽组件 实现的。我重点拆解三个核心能力:
4.1 拖拽布局
- 使用
vue3-draggable-resizable库实现组件的自由拖拽和缩放。 - 每个组件对应一个 JSON 配置(x, y, w, h, type, props)。
- 画布是一个绝对定位容器,组件通过
transform或top/left定位。
简化代码示意:
vue
<template>
<div class="canvas" :style="{ width: canvasWidth, height: canvasHeight }">
<draggable-resizable
v-for="item in widgets"
:key="item.id"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
@resize="onResize"
@drag="onDrag"
>
<component :is="item.type" v-bind="item.props" />
</draggable-resizable>
</div>
</template>
4.2 图层管理(z-index 堆叠)
- 每个组件有一个
zIndex属性。 - 左侧图层树展示所有组件,支持拖拽排序、显示/隐藏、锁定。
- 通过
EventBus实现画布组件和图层树的双向同步。
4.3 母版(Master)功能
- 类似 PPT 的母版:在母版页添加的组件,会在所有普通页面自动出现。
- 技术实现:两个独立的画布数据源(masterSlides 和 slides),渲染时先渲染母版组件,再渲染普通组件。
- 母版组件被锁定,普通页面不可编辑。
这一套前端方案,基本可以满足企业级大屏 90% 的需求,而且代码完全开放,值得借鉴。
五、独家踩坑与调优(真实生产环境)
坑1:DataX 同步大表 OOM
现象:同步 2000 万行数据时,DataX 进程直接 OOM。
原因:默认 channel 数为 5,每个 channel 会缓存全部数据。
解决:限制 channel 数量和单 channel 内存:
json
"setting": {
"speed": {
"channel": 1,
"byte": 1048576
},
"errorLimit": {
"record": 0
}
}
同时增加 JVM 堆内存:-Xms2g -Xmx4g。
坑2:Doris 分区键设计不合理导致查询慢
现象:按 sale_date 分区,但查询常用 region 过滤,分区裁剪失效。
原因:Doris 的分区裁剪只针对分区键。
解决:将常用过滤字段作为 DISTRIBUTED BY HASH 的键,并创建 Bloom Filter 索引。
sql
ALTER TABLE sales_daily SET ("bloom_filter_columns" = "region");
坑3:XXL-JOB 分片任务重复执行
现象:配置了分片(shardIndex=0,1),但两个节点都处理全量数据。
原因:代码中没有根据 shardIndex 和 shardTotal 拆分数据范围。
解决:在任务方法中获取分片参数,用取模拆分数据:
java
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
// where mod(id, shardTotal) = shardIndex
坑4:前端大屏拖拽时卡顿
现象:画布上超过 30 个组件后,拖拽缩放非常卡。
原因:vue3-draggable-resizable 在每次拖拽时触发大量重绘。
解决:使用 requestAnimationFrame 节流 + 开启硬件加速(transform: translateZ(0))。另外对非可视区域组件做虚拟滚动(未完全实现,但官方正在优化)。
六、部署与运维(Docker Compose 一键启动)
JVS-BI 提供完整的 docker-compose 脚本,以下为核心步骤:
bash
# 1. 克隆部署项目(示例地址,实际以官方为准)
git clone https://gitee.com/software-minister/jvs-docker-compose.git
cd jvs-docker-compose
# 2. 修改本地 IP(替换 ${IP} 为你的服务器内网IP)
sed -i 's/server-ip/192.168.1.100/g' ./mysql/nacos.sql
# 3. 启动基础中间件(MySQL, Nacos, RabbitMQ, XXL-JOB)
docker-compose -f docker-compose-db.yml up -d
docker-compose -f docker-compose-base.yml up -d
# 4. 启动 Doris(需先调优系统参数,见官方文档)
docker-compose -f docker-compose-doris.yml up -d
# 5. 启动 BI 服务
docker-compose -f docker-compose-data-platform.yml up -d
七、总结与互动
JVS-BI 的技术栈没有盲目追新,而是选择了 成熟、可维护、开源 的组件:
- 微服务:Spring Cloud Alibaba(Nacos + Gateway)
- 数据集成:DataX(稳定,支持限速、断点续传)
- 数仓:Doris(MPP,兼容 MySQL,易运维)
- 调度:XXL-JOB(轻量,支持分片)
- 前端:Vue3 + 自研拖拽设计器
如果你也在做类似的数据平台,欢迎参考这套架构。同时,我也很想知道:
- 你会选择 Doris 还是 ClickHouse?为什么?
- 在数据抽取中,你遇到过哪些坑?如何解决的?
- 你还想了解 JVS-BI 哪个模块的源码实现?
如果这篇文章对你有帮助,请点赞、收藏、评论三连支持!
📌 相关资源
- JVS-智能BI Gitee 仓库(示例):gitee.com/software-mi…
- 官方技术文档:doc.bctools.cn