← 返回

数据开放平台架构设计(一):从 SQL2API 到数据开放平台的演进

数据开放平台本质上是把"数据变成服务"这件事系统化。我一开始做的是 SQL2API:页面上写 SQL、配置参数,系统自动生成接口。这个工具确实能省掉很多重复 CRUD,但一旦拿去给多个业务方长期使用,问题就不只是"能不能查出数据"了。

真实落地时,大家会马上追问:谁能调这个接口?能看到哪些字段?一次上限查多少?慢查询谁负责?数据源密码放哪?字段改名会影响哪些接口?有没有审计记录?这些都不是 SQL2API 本身能顺手解决的问题。

这一篇聊背景:为什么要从一个 SQL2API 工具,继续往数据开放平台演进,以及平台的架构怎么拆。


一、从 SQL2API 到数据开放平台

SQL2API 解决的是一个很具体的问题:把 SQL 变成 API,省掉 CRUD 的模板代码。 这个能力很有价值,但它只是数据开放链路里的一个环节。

真实场景里,一个完整的数据开放链路长这样:

  1. 数据接入:连各种数据源(MySQL、PostgreSQL、ES、Hive、API),把数据接进来
  2. 数据加工:字段映射、格式转换、数据脱敏、聚合计算
  3. 服务发布:把加工后的数据发布成 API,支持多种协议(REST、gRPC、WebSocket)
  4. 访问控制:AK/SK 认证、租户隔离、流量控制、配额管理
  5. 监控治理:调用链追踪、慢查询告警、数据血缘、审计日志
  6. 运维管理:灰度发布、容灾备份、SDK 生成、文档管理

SQL2API 只覆盖了第 3 步的一部分。要从"工具"升级为"平台",需要把这六步都串起来,而且要能长期运维。

类比一下:SQL2API 是一个厨师,能把食材做成菜。数据开放平台是一个餐厅,从采购(数据接入)、备菜(数据加工)、烹饪(服务发布)、上菜(访问控制)、品控(监控治理)到运营(运维管理),全流程都有。

1.1 为什么不直接开放数据库

有人会问:既然本质是查数据,为什么不直接给业务方一个只读账号,让他们连库查?

这个方案短期速度高,长期更麻烦:

  • 权限太粗:数据库账号一般按库、表授权,很难细到"这个应用只能看订单表里的 5 个字段"。
  • 审计断层:只能看到某个账号执行了 SQL,很难还原到具体应用、具体接口、具体业务场景。
  • 流量不可控:一个全表扫描或者大分页,就可能把线上库拖慢。
  • 数据口径失控:每个调用方自己写 SQL,同一个指标可能有三种算法。
  • 变更风险大:表结构改了,谁受影响靠猜,没人愿意拍胸脯。

所以数据库可以作为底座,但不应该直接暴露给调用方。平台要把权限、限流、审计、口径和 SLA 都挡在前面。

1.2 为什么也不能只靠网关

API Gateway 很重要,但它解决的是入口问题:认证、限流、路由、协议转换。它不知道某个接口背后查了哪些表,也不知道手机号字段该不该脱敏,更不知道这个 SQL 是否会扫全表。

数据开放平台需要补的是数据侧能力:

  • 权限:应用级、API 级、字段级、数据范围级权限。
  • 审计:谁在什么时间,用什么参数,访问了什么数据。
  • 限流与配额:保护平台,也保护底层数据库。
  • 元数据:数据源、表、字段、接口、参数、返回结构都要可管理。
  • 血缘:一个字段变更,要知道影响哪些 API 和调用方。
  • 数据源管理:连接池、密钥、健康检查、备用数据源不能散落在代码里。
  • SLA:接口超时、成功率、慢查询、告警都要有明确指标。

这些能力靠网关很难补齐,必须下沉到数据服务平台本身。


二、整体架构

先把全貌拉出来:

数据开放平台总览图

整体分四层:

  1. 接入层:API Gateway 做统一入口,负责鉴权、限流、路由、协议转换。所有外部请求先过这一层,过滤掉非法流量再转发到服务层。
  2. 服务层:核心业务逻辑,包括 SQL 转 API 引擎、数据转换引擎、数据聚合引擎。这三个引擎各司其职,通过 Pipeline 串联起来。
  3. 数据层:动态数据源路由,支持连接多种数据库。用户配置 API 时选择数据源,运行时自动路由。
  4. 治理层:监控、审计、血缘、配置管理。保障平台的可观测性和可运维性。

三、核心模块说明

3.1 SQL 转 API 引擎

这是平台的核心能力,继承自 SQL2API。用户在页面上写 SQL、配参数、选数据源,系统自动生成 API 端点。

和原来的 SQL2API 相比,做了几个升级:

  • 多数据源支持增强:不只是关系型数据库,还支持 ES、MongoDB 等 NoSQL
  • SQL 模板引擎升级:支持更复杂的 DSL 语法,包括 <choose><trim><foreach>
  • 结果格式化:支持嵌套对象、数组聚合等复杂返回结构
  • 发布前校验:保存 SQL 时做只读校验、参数校验、超时校验,避免问题在调用时才暴露
  • 运行时管控:执行时绑定调用方、租户、限流和审计上下文,接口不是裸跑 SQL

3.2 数据转换引擎

原始数据查出来之后,往往不能直接给调用方。需要做字段映射(下划线转驼峰)、数据脱敏(手机号打星号)、格式转换(日期格式统一)、聚合计算(求和、平均、计数)。

数据转换引擎用规则驱动,用户在界面上配置转换规则,运行时自动执行:

@Component
public class DataTransformEngine {

    @Autowired
    private List<TransformRule> rules;

    public List<Map<String, Object>> transform(TransformConfig config,
                                                 List<Map<String, Object>> rawData) {
        return rawData.stream()
            .map(row -> applyRules(config, row))
            .collect(Collectors.toList());
    }

    private Map<String, Object> applyRules(TransformConfig config,
                                            Map<String, Object> row) {
        Map<String, Object> result = new LinkedHashMap<>(row);
        for (TransformRule rule : rules) {
            if (rule.supports(config)) {
                result = rule.apply(config, result);
            }
        }
        return result;
    }
}

3.3 鉴权与流控

鉴权用 AK/SK 机制:每个调用方分配一对 Access Key 和 Secret Key,请求时带上签名,服务端验签。比单纯的 Token 认证更安全,密钥不在网络上传输,只有签名。

流控用令牌桶算法,按调用方维度限制 QPS。超限直接返回 429,不排队不等待:

@Component
public class FlowControlInterceptor implements HandlerInterceptor {

    private final Map<String, RateLimiter> limiterMap = new ConcurrentHashMap<>();

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String appKey = request.getHeader("X-App-Key");
        if (appKey == null) return true;

        RateLimiter limiter = limiterMap.computeIfAbsent(appKey, key -> {
            FlowConfig config = flowConfigService.getByAppKey(key);
            return RateLimiter.create(config.getQps());
        });

        if (!limiter.tryAcquire()) {
            response.setStatus(429);
            response.getWriter().write("{\"code\":429,\"message\":\"请求频率超限\"}");
            return false;
        }
        return true;
    }
}

3.4 监控与治理

每个 API 调用都记录详细日志:谁调的、传了什么参数、执行了什么 SQL、返回了什么结果、耗时多少。这些日志写入 ELK,可以做全链路追踪。

慢查询自动告警:SQL 执行超过阈值(比如 3 秒)自动触发告警,推送到钉钉/飞书。数据血缘记录每个 API 涉及哪些表和字段,方便做影响分析。

3.5 数据源与元数据管理

这一块很容易被低估。早期我也觉得"存个 JDBC URL 就行",后面才发现数据源管理做不好,平台会变成一堆隐形风险:

  • 数据源密码要加密存储,不能出现在配置文件和日志里。
  • 连接池要按数据源隔离,避免一个慢库拖垮全部接口。
  • 数据源要有健康检查和备用地址,发布 API 前先验证连通性。
  • 表、字段、字段类型、敏感级别要沉淀成元数据,给权限、脱敏、文档和血缘复用。
  • API 要绑定负责人、调用方、SLA、下线时间,不能只存一段 SQL。

这些不是锦上添花。只要平台开始被多个团队依赖,它们就是日常运维的基本盘。


四、技术选型

组件选型理由
应用框架Spring Boot 3.x生态成熟,团队熟悉
数据库访问JdbcTemplate适合运行时动态 SQL,返回结构也够灵活
API GatewaySpring Cloud Gateway和 Spring 生态无缝集成
注册中心Nacos服务注册 + 配置管理一体
缓存Redis高性能,支持分布式锁
监控Prometheus + Grafana业界标准,社区活跃
日志ELK Stack全文检索,分析能力强
限流Guava RateLimiter + Sentinel单机用 Guava,集群用 Sentinel

JdbcTemplate 为什么适合这个场景,上一个系列已经详细分析过了,这里不重复。核心原因就一个:SQL2API 需要在运行时执行 SQL 模板,JPA/MyBatis 都更偏向编译期确定结构,JdbcTemplate 反而更轻。


五、核心设计原则

设计这个平台的时候,有几个原则一直贯穿始终:

配置驱动,代码只写框架。 平台的核心逻辑是通用的(鉴权、流控、数据转换),具体业务通过配置驱动。用户在界面上配置 SQL、参数、转换规则,系统自动生成服务。不写一行业务代码。

分层解耦,可独立扩展。 接入层、服务层、数据层、治理层各自独立,可以单独扩展。比如数据源从 MySQL 扩展到 ES,只需要在数据层加一个 ES 的 Driver,上层代码不用动。

安全第一,默认安全。 所有 API 默认需要鉴权,默认开启 SQL 注入防护,默认开启数据脱敏。安全不是可选项,是默认行为。

可观测性是刚需。 每个 API 调用都有完整日志、指标、追踪。出了问题能快速定位,不用翻代码加日志。


六、和 SQL2API 的关系

简单说,SQL2API 是数据开放平台的一个子模块。数据开放平台在 SQL2API 的基础上,扩展了鉴权、流控、数据转换、监控治理等能力。

原来的 SQL2API 系列文章聊的是"怎么把 SQL 变成 API",这个系列聊的是"怎么把数据变成服务"。范围更大,但核心思想没变,配置驱动、减少重复、提升效率。

后面几篇会逐一深入每个模块的实现细节。


下一篇: 数据开放平台(二):SQL 转 API 引擎