2、数据源优化:接口聚合、数据压缩与增量更新策略
做数据可视化大屏,最怕什么?
我个人最怕的不是图表画不出来,而是数据加载慢。你想想看,大屏一打开,所有图表都在转圈圈,领导站在旁边看着,那场面……嗯,我经历过不止一次。
数据源优化,说白了就是解决「数据怎么来、来多少、多久来一次」这三个问题。今天我就把这三板斧掰开揉碎了讲给你听。
2.1 接口聚合:少跑几趟,比啥都强
先讲个我踩过的坑。有一次做大屏项目,页面上一共12个图表,每个图表单独调一个接口。结果呢?页面加载要发12次HTTP请求,光握手就花掉好几秒。
后来我改了策略——把能合并的接口全合并了。
举个例子。原来你有三个接口:
GET /api/sales/total // 总销售额
GET /api/sales/trend // 销售趋势
GET /api/sales/top10 // 销售TOP10
合并之后变成:
GET /api/sales/dashboard?fields=total,trend,top10
后端返回的数据结构大概是这样的:
{
"total": { "amount": 1234567, "rate": 0.12 },
"trend": [ { "date": "2024-01", "value": 100 }, ... ],
"top10": [ { "name": "商品A", "sales": 50000 }, ... ]
}
这样做的好处很明显:
- HTTP请求从3次降到1次,省掉了2次握手开销
- 后端可以并行查数据库,整体响应时间反而更短
- 前端代码也清爽了,一个接口搞定所有
2.2 数据压缩:瘦身之后跑得更快
接口聚合解决了「次数」问题,但「体积」问题还没解决。
我记得有一次,一个接口返回了2MB的JSON数据。大屏加载时,光下载就花了3秒。你想想看,2MB的数据里有多少是真正有用的?
数据压缩,我一般从两个维度下手:
2.2.1 传输层压缩
这个最简单,后端开启Gzip压缩就行。以Nginx为例:
gzip on;
gzip_types application/json;
gzip_min_length 1024;
开启之后,2MB的JSON能压缩到400KB左右。效果立竿见影。
2.2.2 数据格式精简
这个才是重头戏。我见过太多人把后端返回的数据原封不动往前端丢。其实很多字段根本用不上。
举个例子,原始数据可能是这样的:
[
{
"id": 1001,
"product_name": "智能手表",
"category": "电子产品",
"price": 1299.00,
"sales_count": 500,
"sales_amount": 649500.00,
"created_at": "2024-01-15 10:30:00",
"updated_at": "2024-01-15 10:30:00"
}
]
但大屏上只需要展示「产品名称」和「销售额」两个字段。那为什么不精简一下?
[
{ "name": "智能手表", "amount": 649500.00 }
]
字段名也可以缩短:
[
{ "n": "智能手表", "a": 649500.00 }
]
2.3 增量更新:别每次都全量拉取
大屏上的数据,很多是实时或准实时的。比如每5秒刷新一次销售额。
如果每次都全量拉取,数据量会越来越大,性能越来越差。这时候就需要增量更新了。
增量更新的核心逻辑就一句话:只拿变化的数据。
具体怎么做?我常用的方案有两种:
2.3.1 基于时间戳的增量
前端每次请求时,带上上次更新的时间戳。后端只返回这个时间戳之后变化的数据。
// 前端请求
GET /api/sales/updates?since=2024-01-15T10:00:00Z
// 后端返回
{
"timestamp": "2024-01-15T10:05:00Z",
"updates": [
{ "id": 1001, "sales_count": 510, "sales_amount": 662490.00 }
]
}
2.3.2 基于版本号的增量
这个方案更可靠。后端维护一个全局版本号,每次数据变化就递增。前端拿着上次的版本号来请求,后端返回版本号之间的差异数据。
// 前端请求
GET /api/sales/updates?version=1024
// 后端返回
{
"version": 1030,
"changes": [
{ "type": "update", "id": 1001, "data": { "sales_count": 520 } },
{ "type": "delete", "id": 1003 }
]
}
2.4 三种策略怎么搭配?
这三种策略不是互斥的,而是可以组合使用的。我一般这样搭配:
| 场景 | 接口聚合 | 数据压缩 | 增量更新 |
|---|---|---|---|
| 首次加载 | ✅ 必须 | ✅ 必须 | ❌ 不需要 |
| 定时刷新 | ✅ 推荐 | ✅ 推荐 | ✅ 必须 |
| 实时推送 | ❌ 不需要 | ✅ 推荐 | ✅ 必须 |
举个例子。我最近做的一个大屏项目,首次加载时用了一个聚合接口,返回所有图表的基础数据,同时开启了Gzip压缩。之后每10秒刷新一次,用的是增量接口,只返回变化的数据。整个大屏的加载时间从原来的8秒降到了1.5秒。