java后端开发面经(BSave - 数据切面工程在字节的大规模实践)

java后端开发面经(BSave - 数据切面工程在字节的大规模实践)
BSave - 数据切面工程在字节的大规模实践

前言

数据切面工程(Data Aspect Engineering),是我们团队在字节跳动内规模化落地 BSave 后总结出的理念,其核心思想是在数据存储的边界引入对业务透明的操作层,将原本需要业务关注的通用数据操作,转移到一系列透明操作层中。

从在线到离线,基于数据切面工程,我们可以做到在业务无需投入编码人力的情况下,实现数据透明加解密、数据透明压缩、数据访问追踪等能力,也可辅助数据沙盒构建、数据质量检查等项目。对于公司内要推动大规模数据治理、合规治理、成本治理的团队来说,做好数据切面工程,可大规模降低业务落地成本,提高推动效率。

BSave 名称意象来自于 Bytedance Save / Be Safe / Bytedance Storage,是我们在数据切面工程上落地的具体系统。目前 BSave 覆盖 2000 个以上在线服务,1300 条以上数据同步链路,2400 张以上数据表,流经 BSave 的数据请求峰值 QPS 超过 145 万。

这篇文章的目的,在于为其它团队的研发提效、数据治理、合规建设提供新的思路,分享我们在数据切面工程的经验以及技术细节。

演进思路

数据切面工程的大规模实施,需要以实际业务价值做牵引,不同企业情况会有差异,但具有共性的着力点是:

  1. 数据保护;

  2. 资源成本;

  3. 稳定性;

BSave 在立项时,就是为了大规模解决字节跳动企业内部高敏数据的加密落库问题,因此首先建设了在线数据切面,拿到初步的业务认可后,又产生了离线数据保护、传输保护的需求,随后 BSave 扩展建设离线数据切面,以及数据同步切面。

后来又发现了业务经常碰到 KV 大 Value 导致的稳定性抖动问题,BSave 基于自身的 KV 全双工数据处理、请求拆并能力,实现大 Value 自动压缩机制,帮助业务大幅提升稳定性的同时,将 KV 存储成本优化为原来的 30% 以内。

还有如跨合规区进行数据同步的需求,公司规定两个合规区不能共用一套密钥(密钥不允许跨区域传输),因此高敏数据要做先解密再加密的动作,我们通过数据同步切面透明地支持了这种场景。

在技术选型上,应选择与企业基建发展吻合的方向,例如字节跳动内大规模落地了 Service Mesh,那 BSave 的在线数据切面主要依托 Sidecar 机制实现,一套逻辑即可对接不同编程语言的服务;而在数据同步切面的建设上,BSave 则要与字节跳动内 ByteDTS、Dorado、Dsyncer 等团队对接,构建出合适的方案。

真实的业务需求是数据切面工程落地的前提,合适的技术选型则是效果的关键。

技术实现

我们以 BSave 为例,详解其中的技术实现,即便这套实现做不到无缝适配所有企业,也能给大家带来不少参考价值。

在线数据切面

为什么需要 BSaveAgent

想象一下这个场景:你是一个后端开发,某天收到安全团队的紧急需求——“所有存储用户手机号的字段必须加密存储”。打开代码一看,这个字段被 47 个服务读写,分布在 Java、Go、Python 三种语言的代码里。你开始在心里默算:改代码、发布、灰度、回归测试……至少得一个月,还得祈祷不出 P0。

有没有一种方式,能让这件事变得像“开关”一样简单?

这正是在线数据切面要解决的问题——在业务代码和存储之间,插入一个“透明层”,让加解密、审计、规则校验这些能力对业务无感知地生效,将这些与业务逻辑无关的“通用能力”从业务代码中彻底剥离,交由一个统一、专业的基础设施“BSave 在线数据切面 BSaveAgent”来解决。

在架构中的位置与形态

在线数据切面,本质上是一个位于业务应用和存储系统之间的“智能代理”。所有的数据访问流量都会先经过这个切面,在这里,我们可以像“安检”一样,对流量进行检查、分析、改写甚至拦截,然后再放行到真正的存储目的地。这个过程对业务应用来说是完全透明的。

这种切面主要以两种形态存在:

  1. Sidecar:这是我们更推崇的模式。它作为一个独立的进程,与业务应用部署在同一个主机或容器 Pod 中,就像一个挂在主应用旁边的“边车”。业务流量通过本地回环网络(localhost)发送到 Sidecar,再由 Sidecar 转发给下游存储。

    为什么选择 Sidecar?它的最大优势在于语言无关。无论你的应用是 Go、Java、Python 还是 Node.js,都可以共享同一个 Sidecar 实现。这使得构建一套统一的数据治理体系成为可能。此外,Sidecar 与业务进程隔离,其自身的升级、故障不会直接拖垮主应用,稳定性更高,运维也更标准化。这正是服务网格(Service Mesh)理念在数据访问层的延伸(Database Mesh)。

  2. SDK:对于技术栈高度统一的团队(例如:整个公司都使用 Java),也可以选择在语言内部实现切面,比如通过 Java 的拦截器(Interceptor)或 AOP(面向切面编程)。这种方式的好处是减少了一次网络跳跃,性能开销更低。但它的通用性较差,每种语言都需要独立实现和维护一套逻辑,长期来看成本更高。

简单对比:

维度

Sidecar 方案

SDK 方案

DB Proxy

信息完整度

⭐⭐⭐⭐ 需扩展协议透传

⭐⭐⭐⭐⭐ 与服务集成,易获取 context

⭐⭐⭐ 需要扩展 DB 协议来实现 context 透传

信息安全性

⭐⭐⭐⭐ 与代码隔离,基本无法篡改

⭐ 易被业务代码篡改

⭐⭐⭐⭐⭐

与服务部署隔离,无法篡改

接入效率

⭐⭐⭐⭐ 配置即可接入

⭐⭐⭐ 需升级 SDK

⭐⭐⭐⭐⭐ 默认部署,业务无需关注

实现效率

⭐⭐⭐⭐ 语言无关,一次开发

⭐ 需多语言各自实现

⭐⭐⭐ 非通用需求,默认不支持对 DB 团队外的 plugin

多存储兼容

⭐⭐⭐⭐⭐ 统一框架支持

⭐⭐⭐ 需按存储分别实现

⭐ 需要不同 DB Proxy 各自实现

总的来说,在企业级数据治理场景下,Sidecar 的“与业务隔离、语言无关、统一治理”优势,远大于 SDK 方案的“信息完整度”优势,而当技术栈单一且追求极致性能时,SDK 也是一个可选方案。

通用能力与插件生态

概述

流量架构

在业务访问存储时,请求流量会被 BSave Sidecar 透明劫持。劫持后,流量首先进入 Protocol 层,经过协议解析与转换,还原为结构化的数据对象,便于系统理解与处理。

随后,系统基于流量特征(如命令类型、数据大小、目标实例等)通过 Plugin Loader 调度层 动态决策应执行的插件链路。该过程可结合规则引擎等机制,实现智能化的插件路由与策略匹配。

确定插件链后,请求依次经过各 Plugin Solution 插件进行数据加工,例如加密(TBE)、大 Key 压缩、访问控制等。处理完成后,数据被重新封装并通过原链路向下传递至后端存储(Downstream)。

同理,来自存储的响应(Response)也会被 Sidecar 捕获,经过反向的协议解析、插件处理(如解密、脱敏)和数据重构,最终返回至业务服务(Upstream)。

需说明的是,图中所示为单向处理流程(如 Request 从 Upstream 到 Downstream),实际数据路径为双向闭环。此处的 Upstream 与 Downstream 并不特指业务或数据库,而是根据请求方向动态定义:请求阶段,业务为 Upstream,存储为 Downstream;响应阶段则反之。

执行细节

在线数据切面的强大之处,在于其围绕“语义解析 → 策略编排 → 请求/响应改写”这一通用方法构建的插件化生态。

它不是一个简单的 TCP 代理,而是能深度理解存储协议的“翻译官”。

  • 语义解析:它能解析 MySQL、Redis 等协议的报文,知道一条 SQL 语句具体是在“查询”还是“更新”,操作了哪个表的哪些字段。

  • 策略编排:基于解析出的语义,切面会根据从控制中心获取的策略,动态地决定执行哪些功能插件。例如,检测到对“用户密码”字段的写操作,流量就被分发到“加密插件”里处理。

  • 请求/响应改写:插件可以对原始请求或存储返回的响应进行修改。比如:一个“透明加密”插件,会在数据写入数据库前自动加密指定字段,在数据被读取出来时再自动解密,整个过程对上层业务完全透明。

通过这种方式,我们可以像搭乐高一样,将各种数据访问能力构建为可插拔的插件,例如:

  • 数据加解密:实现字段级的透明加密,满足数据静态安全要求。

  • 访问审计:精细化记录“谁、在何时、用何种方式、访问了什么数据”,满足合规审计需求。

  • 规则校验:例如,禁止不带 WHERE 条件的 DELETE 操作,防止误删全表。

  • 流量追踪与染色:为每一条数据库访问请求注入追踪 ID,方便问题排查。

  • 大 Key 治理:对于 Redis,可以在切面层自动对大 Key 进行压缩或拆分,降低存储成本。

    java后端开发面经(BSave - 数据切面工程在字节的大规模实践)

核心特性

支持多种通信模式

根据不同存储的协议特性,选择不同的通信模式:

  • MySQL:通信方式是典型的 Ping-Pong 方式,适合使用半双工通信方式

  • Redis:Redis 的 Pipeline 无边界标识,并有较高的性能要求,全双工能避免半双工的串行等待,吞吐更稳、尾延迟更好

MySQL 上下文管理能力

一个 MySQL 请求可能会被拆分为多个数据包进行传输,例如当一个请求包大于 16MB 时,传输协议将会将它拆分成无数个 (2^24 - 1) + 4 (playoad length + sequence id) 的包直到 payload length 不为 2^24 - 1 (ff ff ff) 为止。

一个 MySQL 请求也可能需要和前置请求组合生效,例如我们使用 COM_STMT_PREPARE 命令预编译一个 SQL,再通过 COM_STMT_EXECUTE 命令携带参数触发 SQL 执行。

我们需要将数据在不同的场景聚成满足需求并且大小合适的包,并获取到一次请求的足够上下文给到插件。

普通 Query

Prepare Statement

当数据超大时,上游可能使用COM_STMT_SEND_LONG_DATA 命令下发数据,为了保证系统稳定性,一个完整的调用会被分多次调到插件,并且每次在上下文中携带前置数据包的元数据(非原始 MySQL 数据包)给到插件,辅助插件完成业务逻辑

Redis 的可编程扩展能力

BSave Agent 数据切面建立了标准化的插件生态接口规范,实现计算逻辑与存储协议的彻底解耦。针对不同的业务场景提供两个流量重塑能力:

  • 支持插件一对一改写命令及 KV 内容,适用于压缩、加密等场景

  • 支持插件一拆多的指令复合分发与聚合,适用于拆 Key、双写等场景

对于一拆多场景,业务感知依旧仅为一个命令的执行,而实际上在存储端是多条命令的执行。

不管是一对一的命令改写,还是一拆多的指令拆分,对于插件开发者,只需完成如下两个接口的逻辑改写:

  • HandleRequest:改写请求包

  • HandleResponse:改写响应包

以把 set key value 改成 set bsave_key bsave_value 为例,插件逻辑只需要完成下面结构体的改写即可

接入与运维

推广一套新范式,接入成本和运维体验至关重要。在线数据切面追求的是对业务的“低侵入”甚至“零改造”。

  • 接入路径:在理想情况下,业务团队只需要确保其使用的存储 SDK 升级到支持 Database Mesh 的版本,然后在服务的部署配置中开启 Sidecar 的注入即可。整个过程无需修改一行代码。

  • 平台化运维:为了支撑大规模应用,我们配套建设了一个一站式的运维平台。开发者可以在平台上自助完成服务的接入、配置策略(例如,为哪些字段开启加密)、查看监控仪表盘、设置告警等。所有操作都应该是可视化的,无需手动修改底层配置文件。

性能与稳定性

在核心链路中增加一个节点,性能和稳定性是绕不开的话题。

  • 性能开销设计:引入 Sidecar 会增加一次网络跳跃,带来额外的延迟。但通过现代网络技术(如 Unix Domain Socket)、共享内存通信以及对代理本身的极致优化,可以将这部分开销控制在极低的水平(例如,P99 延迟增加在 1ms 以内),对于绝大多数应用是可以接受的。

  • 性能优化发现:在落地的过程中,针对整体网络性能进行分析,暴露了如下问题(以及排查问题手段):

  • 性能优化解决方案落地:针对发现的问题,进行如下架构改造

  1. 架构层性能优化(Recycle / Channel / Fast Path)

  • Recycle 架构设计与落地,通过 Context 承载 Recycler + Buffer Linker 链表登记对象,实现请求级对象统一回收。(主要解决 Go 没有析构函数的问题)

  • Channel 透传与 Streaming 化:将 Proxy–Protocol 从 Ping-Pong 模型演进为共享 Channel 的流式模式,减少 chansend 和囤包扩容。

  • Fast Path 方案:通过 Traffic Filter 在前置节点过滤无需处理流量,显著减少协议解析等冗余开销。

  1. 代码级通用性能优化组件建设

  • 对象池化方案体系化落地:Object Pool、Slice Pool、Goroutine Pool 等,开发相关 SDK 满足其他架构使用。

  • Buffer Linker 内存列优化,用于不可预估长度的场景替代频繁 append,在大数据拼接场景下获得稳定收益。

  • unsafe 零拷贝转换、IO 快速序列化 / 反序列化路径,将历史进程间 [] byte 交互演进到函数内 struct 直连,减少一次完整序列化链路。

  1. IO 与日志等细节优化与规范化

  • Buffer IO + Slice Pool + Recycle 组合使用,实现 IO 读取缓冲的内存复用,降低 GC 压力与大内存频繁申请。

  • 针对日志场景,推动 logs.V2 (字节自研)替代 fmt.Sprintf,并参与 WithCtx 性能问题优化 MR,降低日志调用系统开销。

  • 稳定性保障:

  • 灰度发布:无论是切面自身的升级,还是某个插件的策略变更,支持了小流量、分阶段的灰度发布能力,以控制风险。

  • 降级回退:提供了“一键降级”的开关。一旦线上出现问题,可以快速绕过切面或禁用某个插件,实现“秒级止血”。

  • 容量评估:平台提供了工具,帮助业务方在接入前评估引入 Sidecar 带来的额外资源开销(CPU、内存),并给出扩容建议。

  • 全链路监控:提供了从业务应用到切面再到存储的端到端监控视图,是快速发现和定位问题的基础。

总结与展望

在线数据切面并非一个孤立的技术,它是构建跨存储、多语言、可扩展的数据访问层(DAL)新范式的重要一步。它将数据访问的共性问题,从分散的业务逻辑中沉淀到统一的基础设施层,让业务开发者更专注于业务本身。

通过拥抱 Sidecar 模式和插件化生态,任何团队都可以借鉴这套思路,逐步构建起属于自己的、可“拿来即用”的数据治理基座,最终让整个技术组织的生产力得到解放。

如果你所在的公司也想建设类似能力,有几点建议:

  1. 找准切入场景:我们选择了“敏感数据加解密”作为第一个场景,因为它足够痛、价值足够大、边界足够清晰。

  2. 技术栈适配:Sidecar 不是唯一答案。如果你是纯 Java 技术栈,用 Java Agent 或 Interceptor 机制可能更合适;核心理念是“切面化”,而非“必须用 Sidecar”。

  3. 先跑通再优化:性能损耗是大家最关心的问题。我们的实践表明,端到端延迟增加可以控制在 1ms (P99) 以内,对绝大多数业务可以接受。

回顾过去,数据访问层经历了从"直连裸写"到"ORM 封装"再到"中间件代理"的演进。每一次跃迁,都是为了解决新出现的复杂性——多数据源、读写分离、分库分表。展望未来,我们正站在下一个拐点:AI 原生的数据访问时代

有三个值得关注的方向:

方向一:数据访问的智能化

当前的 BSave Agent 依赖预定义规则执行策略。但规则总是滞后于变化。未来,我们期望引入 AI 能力实现:

  • 自动敏感数据发现:通过语义分析自动识别未标记的敏感字段

  • 动态风险评估:根据访问上下文实时调整安全策略强度

  • 异常模式检测:从海量访问日志中学习正常模式,发现潜在数据泄露风险

方向二:从"被动治理"到"主动服务"

数据访问层不应只是"守门人",更应成为"服务者":

能力

当前状态

未来愿景

数据发现

业务自行查找

切面层推荐可用数据源

Schema 演进

人工协调变更

自动兼容性检测与迁移建议

性能诊断

事后分析慢查询

实时识别并建议优化方案

数据血缘

离线批量构建

实时链路追踪

方向三:跨存储的统一语义层

业务开发者可以不关心数据存在 MySQL 还是 Redis,是关系型还是文档型。统一语义层提供:

数据访问层的本质,是在业务效率数据治理之间寻找平衡点。

过去,这两者常常对立——治理越严,业务越慢;效率越高,风险越大。

BSave Agent 代表的新范式,试图证明:通过架构创新,治理与效率可以兼得。

而这仅仅是开始。当 AI 能力深度融入数据访问层,当语义理解取代硬编码规则,当"被动拦截"升级为"主动服务"——数据访问层将从基础设施演变为企业的智能数据中枢。

数据同步切面

数据孤岛与“安全枷锁”下的数据同步困局

如今,大部分高速发展的企业都面临同一个悖论:一方面,数据被视为 “新石油”,企业需要打破数据孤岛,汇聚各业务系统的数据以驱动业务增长;另一方面,数据安全与隐私监管不断趋严,数据一旦流动,就会牵连加密、脱敏、权限、审计以及跨区域传输等一整套风控要求。

前面章节,已经通过在线数据访问切面,在访问入口建立了统一的安全管控能力,守住了实时读写的第一道安全防线。在此基础上,本章节聚焦 “数据如何在存储与业务系统间高效流转”这一核心命题,旨在构建一条安全可控、可观测、可追责的数据同步通道,确保分散的数据汇聚并释放价值的同时,全程合规可溯源,真正实现 “能流动、管得住”。

一个看似简单的诉求 ——“把 A 系统(如在线交易系统)的数据实时同步到 B 系统(如数据分析平台)做业务洞察”—— 在实际推进过程中,却常常因加密方案选型、敏感数据界定、GDPR/CCPA 及内部合规标准对齐、对线上系统的性能影响评估等一系列问题,演变为多团队间的长期协调和博弈,我们建设 BSave Tube(BTube)数据同步切面的初衷,就是在安全和合规的前提下,让这类数据流动变成可标准化复用的基础能力。

BTube 的诞生:我们为何要自研一个数据同步切面?

在业内,成熟的数据同步和集成工具并不少,无论是开源的 DataX、SeaTunnel,还是云厂商提供的各种 DTS 服务,都提供了强大的数据搬运能力。在我们的公司内部,也已经有了稳定运行多年的数据集成产品。

为什么我们还需要“重复造轮子”,去建设一个新的同步工具呢?

答案在于,我们面临的挑战并非简单的“数据搬运”,而是在“高敏感”、“异构存储”、“跨合规区”这三个核心约束下,实现安全、高效、可控的数据同步链路。通过对内部现有工具的调研,我们发现:

  • 它们都缺少一个统一的“安全加工切面”:加解密、脱敏等安全逻辑分散在各个业务的消费端,难以复用和审计。当安全策略需要升级时,需要推动多个上下游系统进行改造,成本较高。

  • 它们对异构存储和跨区域传输的支持不够“端到端”:一个完整的数据链路往往需要串联多个产品,比如用 A 工具做数据库的变更数据捕获(CDC),用 B 工具做跨机房的数据镜像,再自己写一段 Spark/Flink 任务做数据加工和写入。链路冗长、难以统一管控。

这就好比我们拥有一支强大的物流车队(各种数据同步工具),但缺少一个集“安检、海关、加工”于一体的“转运中心”。所有货物(数据)无论发往何处、发的是什么,都必须在这个中心完成标准化的处理。

因此,我们的选型策略非常明确:在底层充分复用公司内成熟的数据捕获-订阅能力和数据集成通道能力,在其之上构建一个 BTube 数据同步切面,集中承载加解密、解压转码、异构写入、跨域策略等通用能力。

BTube 的核心定位,正是为多业务线提供一个统一、可复用、兼顾安全与效率的高敏感数据流批一体数据集成基建。

架构设计:“切面化、插件化、平台化”的三板斧

面对上述复杂挑战,BTube 的整体架构设计思想可以概括为“切面化、插件化、平台化”。我们通过构建一个统一的数据处理引擎,将复杂的数据加工、加解密逻辑与底层的同步工具彻底解耦。

  1.  切面化:将 “加工逻辑”与 “执行引擎”分离

BTube 提供了两种核心的数据处理切面,以应对不同的业务场景:

  • 服务切面 (FaaS):主要用于在线到在线的低延迟、高并发同步场景。它借助 FaaS(函数即服务)的弹性伸缩能力,处理对实时性要求极高的数据流。

  • 大数据切面 (Flink / Spark):这是 BTube 的核心链路,适用于大数据量的实时集成与跨区域传输。依托 Flink 强大的流处理能力和 Spark 在批处理上的优势,我们能够高效地实现海量数据的加工与同步。

通过切面的方式,业务方只需关心“我需要对数据做什么处理(如加密、转换)”,无需关注“具体由哪个引擎执行、在哪个环节落地”,实现了业务逻辑与底层实现的解耦。

  1. 插件化:让通用能力“即插即用”

BTube 的核心优势之一是其插件化的扩展能力。无论是通用的加解密插件,还是业务特定的数据过滤、转换逻辑,都可以被封装成独立的插件。

这些插件就像一个个乐高积木,可以在数据处理任务运行时被动态加载和编排。这意味着:

  • 新能力的快速上线:当需要支持一种新的加密算法或一种新的数据格式时,我们只需要开发一个新的插件,而无需改动 BTube 的核心代码。

  • 业务逻辑的灵活组合:业务方可以像在购物车里挑选商品一样,自由组合需要的插件来构建自己的数据处理流程,极大地提升了灵活性和复用性。

  1. 平台化:为“跨越鸿沟”铺设轨道

针对最棘手的跨合规区数据同步场景,我们面临一个核心难题:不同合规区的密钥管理系统是相互独立的,数据在一个区域加密后,到了另一个区域就变成了无法解开的“乱码”。

为此,BTube 设计了一套“四步走”的安全链路,从平台层面解决了密钥不可跨区域使用的问题:

  1. 源端解密:在数据离开源端系统前,使用源端密钥将其解密成明文。

  2. 传输加密:使用一个临时的、双方都认可的传输密钥,对明文数据进行加密,确保传输过程的安全。

  3. 目标端解密:数据到达目标区域后,使用传输密钥将其解密回明文。

  4. 目标端加密:最后,使用目标区域的密钥,对数据进行加密存储。

通过这套标准流程,我们为跨区域数据流动铺设了一条安全、合规的“高速铁路”。

BTube 的核心优势

未来展望:迈向更智能、更无缝的数据集成

BTube 的建设虽然取得了阶段性的成果,但我们深知,数据集成领域的探索永无止境。面对未来,我们规划了几个关键的演进方向:

  • 性能与可观测性的极致追求:我们将持续优化核心引擎的性能,进一步降低数据同步的延迟。同时,构建更完善的可观测性体系,提供端到端的链路追踪、数据血缘和性能监控,让每一次数据流动都清晰可见。

  • 数据同步策略智能化:当前多数数据自动同步策略仍依赖人工配置。未来计划引入 AI 能力,结合数据内容、血缘关系及使用场景,智能推荐或自动生成同步策略。

  • 更广泛的跨域合规协同:我们将把 BTube 的能力更紧密地与集团的统一数据治理平台集成,实现跨区域、跨业务线的合规策略协同和联动,构建一个更加立体化、主动式的数据安全防护网。

数据流动的终点,是业务价值的起点。BTube 的故事,是一个在安全、效率、成本“不可能三角”中寻找最优解的工程故事。我们相信,通过技术创新,可以打破束缚数据流通的枷锁,让数据更好地为业务创新服务。

离线数据切面

数仓在数据驱动的决策链路中扮演着“中央厨房”的角色,每日通过数以万计的流批计算,对海量的数据进行着 ETL,为下游的报表、分析和机器学习提供养料,数仓的离线数据体现数据链路长、数据量大、访问入口复杂等特点。离线数据消费是发挥数据价值的核心场所,但公司内没有统一的能力可以在访问链路上加入业务所需策略。往往各业务团队需要对接不同的存储平台以实现对数据使用的审计、改写、监控。所以,建设一个“透明层”允许业务定制化的自己的策略逻辑势在必得。

技术架构与核心设计

围绕着字节内部的离线研发套件,结合离线体系的存算分离特性,BSave 基于 Spark、Flink 等计算层建设了离线切面系统,涵盖了 Hive、Paimon、Clickhouse 等常用存储,整体建设分为:Plan 解析层和 Data 处理层

  • Plan 改写层:主要是构建特定的加解密计算范式,通过 SQL 改写或执行计划改写等手段,构建出一个安全的计算表达;

  • Data 处理层:主要是对于构建出来的执行计划,其中对应表达式或算子的数据处理单元,用于数据加密或者解密;

HiveUDF 主要支撑了 Spark、Presto 等计算引擎中明密文数据转换,我们随着加密能力增强需要一些灰度发布能力来保障 UDF 组件的稳定性,同时根据数仓研发现实场景,分为:核心表、非核心表等。

我们非常容易想到的是:利用多个 UDF 函数进行轮转udf1=>udf2=>udf3=>udfN=>udf1=>...,通过改写系统将不同版本的 udf 应用到各个不同表中

-- 用户原始SQLselect t1.c1, t2.c1 from t1 leftjoin t2 on t1.id = t2.id-- 灰度版本udf改写后-- t1表应用v1版本udf、t2表应用v2版本udfselect t1.c1, t2.c1 from (select id, dec_udf_v1('t1', c1) from t1) t1 leftjoin (select id, dec_udf_v2('t2', c1) from t2) t2on t1.id = t2.id

但是上述方式会带来众多问题

  • 随着版本变多,需要格外注册多个版本函数到 HiveMetaStore 中,导致 UDF 管理杂乱;

  • 类隔离不彻底,如果两个版本包含了不兼容的一些公共包,会导致 NoSuchMethodError 等错误;

函数动态加载方案:利用 BSave 的插件管理能力,动态加载解密处理函数,支持 UDF 高可靠的灰度发布能力

  • 按照表粒度指定灰度策略:Batch01(t1,t2,t3) => Batch02(t4,t5,...) => Batch03(core_t1,core_t2,..) => 全量

  • 运行时数据面,通过不同的 ClassLoader 去隔离执行不同字段数据的加解密

-- 用户原始SQLselect t1.c1, t2.c1 from t1 leftjoin t2 on t1.id = t2.id-- 灰度版本udf改写后select t1.c1, t2.c1 from (select id, dec_udf('t1', c1) from t1) t1 leftjoin (select id, dec_udf('t2', c1) from t2) t2on t1.id = t2.id

性能优化

离线场景中,宽表是数仓中常见的模型,当面临数据合规要求,引入 BSave 离线解决方案会对现有的数据链路产生比较多的性能问题:

  • 敏感字段多导致了加密写入表的时效变慢。

  • 围绕的表维度处理加解密,无法感知任务上下游密文处理方式,导致了数据解密加密嵌套处理影响任务时效性。

手段一:执行计划优化

  • 场景解释:在数仓加工链路中,很多敏感字段只透传到下游,不参与 SQL 中间计算

  • 优化手段:BSave 在 Spark 优化器集成了相应的优化规则,避免 SQL 计算过程中需要先解密后加密重复计算

  • 优化规则简单介绍如下

createtable person_base_tbl (id string,name string, -- 敏感字段age int,region string)createtable person_china_tbl (id string,name string, -- 敏感字段age int)-- 用户ETL SQLinsertinto person_china_tblselect id, name, agefrom person_base_tblwhere region ='china'
-- 未优化InsertIntoStatement(Relation[`default`.`person_china_tbl`, DataCols=[id, name, age]])+- Project(id AS id, bsave_encrypt(name) AS name, age AS age)   +-Filter(region='china')      +- Project(id AS id, bsave_decrypt(name) AS name, age AS age, region AS region)         +- Relation[`default`.`person_china_tbl`, DataCols=[id, name, age, region]]
-- 优化过程-- 1. Project下推&Filter下推InsertIntoStatement(Relation[`default`.`person_china_tbl`, DataCols=[id, name, age]])+- Project(id AS id, bsave_encrypt(bsave_decrypt(name)) AS name, age AS age)   +-Filter(region='china')      +- Relation[`default`.`person_china_tbl`, DataCols=[id, name, age, region]]
-- 2. 同Project中加解密嵌套,则去除嵌套返回原始字段,比如一个字段解密又加密InsertIntoStatement(Relation[`default`.`person_china_tbl`, DataCols=[id, name, age]])+- Project(id AS id, name AS name, age AS age)   +-Filter(region='china')      +- Relation[`default`.`person_china_tbl`, DataCols=[id, name, age, region]]

手段二:数据处理框架优化

将数据攒批去执行,会提高 CPU 执行性能,比如避免 CPU Cache 的 miss 等。

  • HiveUDF 在 SparkSQL 引擎中充当一个 Expression,体现出来的是“行调用”,Expression 计算核心方法org.apache.spark.sql.catalyst.expressions.Expression#eval

  • 根据 SparkExtension 的org.apache.spark.sql.SparkSessionExtensions#injectColumnar,扩展了相关批量执行的算子和表达式(类似 apache-gluten)

  • 同样的计算资源分配下,对比两个相同 Stage 的 75 分位耗时,Batch 模式能提升 50%以上性能

_

Java-Row

Java-Batch

优化明显

75 分位:1.2min

75 分位:30s

小结与展望

整个离线数据切面,都是围绕着轻量级的 PlanRewriter 和 OperatorExec 建设切面,通过计算引擎的插件机制完成敏感数据的透明加解密。后面离线切面还是围绕着:数据处理透明化、离线链路稳定性、计算性能优化等方面建设。

  • Flink-Paimon-Connector 支持维表敏感字段加解密处理能力

  • Hive 表加工数据封闭域,保障跨业务线数据加工的安全性

  • 支持更多的结构化的存储,eg:BMQ、Doris 等

  • Clickhouse 的透明加解密支持 ChPartWriter 等更多高效的导入手段

  • Spark、Flink 加解密集成 NativeLib 提升加解密数据处理速度

插件管理体系

为什么需要插件管理

想象一下这个场景:你的团队刚刚完成了一个透明加解密插件的开发,现在需要将它部署到上千个服务上。打开运维手册一看——需要跨越 10+ 个平台、执行 30+ 个手工步骤,每个服务接入耗时至少 30 分钟。粗算一下,全量推广至少需要近一千人天,还得祈祷过程中不出问题。

这正是插件管理体系要解决的问题——将插件的接入、发布、升级从分散的手工流程,统一收敛为一套自动化、可灰度、可回滚的标准化基建,让插件的变更像"配置开关"一样简单可控。

架构设计:统一抽象,底层兼容

为了解决上述问题,我们的核心思路是定义一套与技术栈无关的插件标准,将"插件描述"与"插件实现"分离——就像"插座标准"与"电器本身"的关系,如下图。

  • “核心发布层”负责统一抽象。满足业务诉求。

  • “适配层”负责底层兼容。作为我们的插件标准层,兼容底下的各种异构插件系统;

业务需求是多样的,“核心发布层”完成抽象的关键是统一了灰度发布模型。不同系统的灰度策略差异大,若逐一定制会导致逻辑碎片化。我们将灰度策略解耦为两层:

层级

职责

示例

策略原子

定义单一灰度维度的实现

按机房、按库表、按 UDF

策略编排

组合多个原子形成完整灰度流程

小流量 → 单机房 → 全流量

接入质量保障

插件接入是业务方最敏感的环节——会不会影响业务服务稳定性? 我们通过事前、事中、事后三道防线来保障:

阶段

检测项

实现方式

事前拦截

框架版本检测

校验 Metric 上报的框架版本是否满足最低要求

插件冲突检测

检测目标服务是否已安装互斥插件

容量评估

检测 CPU/内存余量是否满足插件运行要求

事中监控

质检检查

小流量、单机房、全流量阶段检测框架打点是否异常

事后应急

紧急回滚

一键回退到上一稳定版本

版本召回

批量圈选受影响版本,统一升级

插件的最小 CPU/内存要求由插件提供方通过压测确定,写入插件元数据:

评估公式:插件最低资源要求+24hCPU/Mem峰值≤当前业务资源上限×80%

实践效果:容量评估保守估计至少挡住了 5 次业务容量不足导致的 OOM 风险。

  • 服务端降级

    采用一级缓存 + LocalCache 方案,优先返回缓存数据,异步回源更新

优势:最大程度不阻塞读请求,可抗读流量洪峰

权衡:牺牲一定数据新鲜度(最终一致窗口约10秒)

  • 客户端降级

    当 RPC 请求失败时,SDK 自动从 Redis 容灾链路获取数据:

关键问题:服务端因最终一致可能返回旧版本,导致 SDK 版本回退

解决方案:SDK 本地缓存最新版本号,返回数据前做版本比对,确保版本单调递增

大规模发布一致性

25 年工单达到 10000+单、涉及上千服务,由于单量大,服务多,发布的一致性成为核心挑战:

  • 流水线中途失败或取消,不同阶段版本不一致

  • 多人并行发布同一服务,版本反复横跳

  • 发布后线上版本不收敛,埋下故障隐患

  • 一致性问题示意

我们从两个层面保障一致性:

层面

问题

方案

流水线级

并行发布同一服务导致冲突

按 [插件+服务] 粒度加锁 + 兜底对账

发布系统级

大规模批量发布状态混乱

严格状态机驱动 + 发布计划锁

所有状态流转严格按状态机执行,杜绝"幽灵状态"。

发布计划状态流转

子流程任务状态流转

插件管理体系总结

维度

改进前

改进后

单服务接入耗时

30~60 分钟

10~30 分钟

人工操作时间

30 分钟

3 分钟

大规模发布耗时

数小时

数分钟

数据面可靠性

未量化

99.99%(理论值)

P 级故障

-

0 起

累计完成 10000+ 变更工单,支撑上千服务稳定运行。

插件管理并非只有一种"标准答案"。如果你所在的公司也想建设类似能力,以下是一些选型建议,核心理念是"标准化的插件变更管理",而非"必须用特定技术栈":

场景特征

推荐方案

理由

多语言技术栈(Go/Java/Python 混用)

Sidecar + 统一管理平台

语言无关,一次开发多处复用

单一技术栈(如纯 Java)

Java Agent/Interceptor + AOP

减少网络跳跃,性能更优

插件数量少(<10 个)

轻量级流水线 + 配置中心

避免过度设计

大规模推广(100+ 服务)

完整的发布系统 + 灰度策略

一致性和可回滚是刚需

插件管理体系的本质,是将"插件变更"这一高风险、高频次的操作,从分散的手工流程收敛为统一的自动化基建。它不是一个孤立的工具,而是支撑整个数据切面能力大规模落地的关键基础设施。

对于希望建设类似能力的团队,有几点建议:

  1. 找准切入场景:选择"透明加解密插件推广"作为第一个场景,因为它痛点足够大、边界足够清晰

  2. 技术栈适配:Sidecar 不是唯一答案,核心理念是"标准化管理"

  3. 先跑通再优化:稳定性建设可以逐步迭代,但统一抽象的架构设计应尽早确立

  4. 一致性是刚需:大规模发布场景下,状态一致性问题会被急剧放大,必须提前设计

业务解决方案

透明加解密方案(TBE)

背景

作为数据切面工程的核心业务解决方案之一,透明加解密(TBE)旨在为业务提供“零代码改造”的数据加密存储能力,有效应对日益严格的数据安全与合规挑战。传统的数据库加密方案要么只能在 DB 内部透明加密,要么需要应用改造代码进行适配。前者的保护范围局限在存储层面,无法在数据库本身提供有效保护;后者则局限于应用场景,对复杂和闭源类应用的改造难度极大。而在 TBE 方案中,它通过在各种数据切面中插入一个加解密逻辑,自动对身份证、手机号等高敏感字段进行加解密,即可保障数据从落库到使用的全生命周期安全。

解决方案

能力概述

全链路数据保护与治理

TBE 的目标是给业务提供 “几乎零代码改造”的数据加密能力:应用照常用 SQL 读写数据库,但数据在写入前自动加密 、在读取后自动解密,让敏感数据在存储与流转过程中始终处于受保护状态,对业务研发侧尽量 “无感”。同时,TBE 不只覆盖在线 MySQL 访问,也会打通数据同步与离线计算链路(例如进数仓 / 数据湖后仍保持密文),离线侧通过 UDF 在权限受控下完成解密,形成端到端闭环。

TBE 的核心在于其 Sidecar 插件形态,它接管应用的 MySQL/Hive/ClickHouse 流量,通过深度解析 SQL 语义,识别出需要保护的敏感字段,并在数据写入数据库前进行加密,在数据读取时再解密后返回给应用。整个过程对业务完全透明,开发者无需修改任何业务代码,即可获得统一、可靠的加密能力。

TBE 不仅覆盖了在线服务的数据访问,还打通了离线和同步链路。通过与 BSave Tube(数据同步切面)等组件联动,TBE 能够确保加密数据在从 RDS 同步至数据湖(Bytelake)、数仓(Hive, Paimon)等下游系统时,依然保持加密状态。离线侧则通过 UDF(用户自定义函数)提供受控的解密能力,实现了端到端的数据安全闭环。

接入与治理要点

  • 自助式接入:开发者可通过平台化工具,以工单形式自助完成加密字段的配置与上线,整个过程高度自动化。

  • 统一管控:为避免加密策略被绕过或破坏,平台提供了管控策略,如建议禁止对加密表执行 RENAME 操作,并确保服务的 DB Mesh 开关始终处于开启状态。

  • 性能开销可控:引入 Sidecar 会带来微小的性能开-销(如 P99 延迟增加在 1ms 以内,CPU 资源有少量额外占用),但对于绝大多数应用而言,这种开销在可接受范围内,换来的是安全合规能力的极大提升和研发成本的大幅降低。

总而言之,TBE 方案通过将加解密能力下沉到基础设施,实现了“安全左移”和“统一治理”的目标。它让业务团队从繁琐且易出错的加密改造工作中彻底解放出来,更专注于核心业务逻辑的实现,是数据切面工程理念在安全合-规领域一次成功的规模化实践。

技术架构

MySQL TBE 核心原理

Hive UDF 核心原理

CK Rewriter 核心原理

核心特性

多级密钥管理

DEK :Data Encryption Key,用于加密明文数据的密钥

KEK :Key Encryption Key,Tokenizer 服务用于加密数据加密密钥的密钥

透明 SQL 改写

采取双列机制做明密文存储,用于故障切换、兼容原类型加密等。

  • 在线读密文 (复杂度中低)

方案一 (推荐):-- 改写前select d from (select d from t2 where a=1) t-- 改写后select d from (select _d as d from t2 where a=1) t
方案二:-- 改写前select d from (select d from t2 where a=1) t-- 改写后select _d from (select _d from t2 where a=1) t

-- 改写前select d from (select d from t2 where a=1) t-- 改写后select _d,d from (select _d,d from t2 where a=1) t
-- 改写前select t2.b1 from (select a as a1 from t) t1 join (select d as b1 from t) t2 on t1.id = t2.id  -- 改写后select t2.b1,t2._d from (select a as _d,a as _d from t) t1 join (select d as b1,_d as _d from t) t2 on t1.id = t2.id

离线 Hive 在 ODS 层既可以通过直传的方式同步源端加密方式,也可以接入 BTube 后调整人仓后的加密状态。而在一般的 ETL 阶段,Hive 字段可以使用 UDF 加隐藏列的方式在原列开启加密。

ClickHouse 侧则采取与 MySQL 一样的 SQL 改写能力,不同的是使用 UDF 将加解密操作下沉到数据库内核。而不是和 MySQL 一样在网关运行加解密算法。

  • Hive 和 ClickHouse 的 UDF 方案

-- 改写前select a+1,b from t;select func(a),b from t;select a,biz_udf(b) from t;select a from t where a =1;
-- 改写后select bsave_decrypt(a,enc_a)+1as `a+1`,bsave_decrypt(b) as b from t;select func(bsave_decrypt(a,enc_a)) as `func(a)`,bsave_decrypt(b) as b from t;select bsave_decrypt(a,enc_a) as a,biz_udf(bsave_decrypt(b)) as biz_udf(b) from t;select bsave_decrypt(a,enc_a) as a from t where bsave_decrypt(a,enc_a) =1;

落地效果

TBE 加密保护已覆盖上千个 MySQL、Hive、CK 库表 ,数万字段,千级微服务接入了 TBE 插件稳定运行。

KV Solution 之透明解 Redis 大 key

背景

什么是大 Key

类型

定义

string

Value 长度超过 10KB

hash/list/set/zset

元素个数大于 5000 或总 Value 字节数大于 10MB 即为大 Key

为什么要解大 Key

  1. 阻塞主线程:Redis 单线程模型下,大 Key 的读写、扩容或删除操作耗时极长,会直接阻塞后续请求,引发高延迟甚至超时。

  2. 网络拥塞:单次传输数据量过大(如 MB 级别),极易打满网卡带宽,导致同一服务器上的其他服务受损。

  3. 数据倾斜:在集群模式下,导致特定分片内存占用过高,造成负载不均,增加 OOM(内存溢出)风险。

  4. 运维风险:导致数据迁移、备份(RDB/AOF)及恢复过程缓慢,容易在扩缩容时引发超时失败。

为什么会有大 Key

  1. 设计缺陷:业务未进行数据拆分,将大量关联数据(如数百万元素的 List/Hash)塞入同一个 Key。

  2. 缺乏清理:未设置过期时间或定期删除策略,导致无效数据(如历史日志、累积的粉丝列表)无限增长。

  3. 存储大值:直接在 Value 中存储超大文本、JSON 串或二进制数据(如图片 Base64),而非仅存引用或拆分存储。

业界大 Key 分布广泛

大 Key 危害极大,对 Redis 集群的「系统稳定性」和「资源使用率」影响深远。大 Key 分布广泛,已成为现代大规模 Redis 集群中的结构性风险源,也是各大云厂商、互联网公司长期治理的核心难题。

我们决定启动透明解大 Key 的建设,解耦业务逻辑与通用的治理需求,提供统一、透明的企业级 Redis 大 Key 解决方案,提升 Redis 集群的稳定性与成本效率

解决方案

能力概述

BSave 团队提供的透明解 Redis 大 Key 方案,帮助用户在「一行代码都不用修改,多语言支持,极低成本」的情况下,解决「Redis 大 Key 资源降本」问题。

  • 部署形态:mesh sidecar 的一个插件

  • 核心价值:系统稳定性提升、资源降本

技术架构

透明解 Redis 大 Key 方案整体分为三大功能模块:

  1. Proxy:向上承接业务进程的 Redis 流量,向下连接 Redis 服务端,负责读写 Redis 协议的数据包

  2. Redis Protocol:负责将 Redis 协议数据包解析为应用层插件可理解的接口数据,以及将插件改写后的接口数据编码为 Redis 协议数据包

  3. Plugin Framework:定位为 BSave Agent 在线数据切面的插件框架,大 key 压缩/解压缩插件作为一个解决方案,搭载在整个数据切面上

大 key 压缩/解压缩插件工作原理

  1. Redis 写入流程:透明压缩

  1. Redis 读取流程:透明解压缩

核心特性

正确性保障

压缩和解压数据进行 CRC32 一致性校验,解压缩失败则复用业务回源逻辑找回正确数据

  • 计算时机:在压缩前对原始数据计算 CRC32 校验和

  • 存储格式:头部共 9 字节,0-1 字节存储魔数、第 2 字节版本号、第 3 字节协议头长度、第 4 字节存压缩算法类型、5-8 字节存储 CRC32 值

0

1

2

3

4

5

6

7

8

'B'

'S'

0x01

0x09

0x01

CRC32 校验和

魔数

版本

协议头的长度

算法类型

数据完整性校验

  • CRC32 存储方式:采用大端序(BigEndian)存储,确保跨平台兼容性

  • 校验时机:解压时进行 CRC32 校验验证

  • 容错机制:校验不通过返回 Redis.Nil 复用业务回源逻辑,从存储中重新查询正确数据

性能加速

针对特大 key 采取并行压缩方式以提升压缩性能

  • 小数据:最低延迟,快速路径,避免不必要的并行开销

  • 大数据:最大吞吐量,充分利用多核性能,根据 CPU 核心数自动调整并发数量。

稳定性保障

针对各类异常场景,实现多种自动降级措施

  • 自动降级闭环:压缩 / 解压失败、超时或数据异常时,自动切回原始数据或触发回源 DB,保证主流程写入成功、数据一致且不中断业务

  • 多维熔断防护:毫秒级超时、CPU / 内存阈值监控,将故障与过载隔离在压缩层,不扩散到业务层

  • 无停机恢复与风险准入:在线数据清洗 + 回源机制,实现插件下线 / 故障时 0 停机平滑恢复;发布前容量与权限准入 + Redis ACL 巡检,避免压缩数据污染下游报表

落地效果

该项目成功实现了技术层面的深度 “降本减负”,并直接转化为业务层面的 “稳定提速”

  1. Redis 技术视角(降本与性能基础)

  • 极致压缩: 实现了 80%-95% 的超高压缩率,将存储的 Value 大小降低了一个数量级。

  • 资源释放: 存储和计算资源消耗大幅降低,内存占用最高下降 63%,CPU 负载优化超 50%。

  • 性能底座: 在节省资源的同时,显著提升了 Server 端的响应速度和稳定性,消除了底层的性能毛刺。

  1. 业务视角(用户体验与稳定性)技术指标的优化直接带来了服务质量的提升:

  • 时延更稳: Redis P99 长尾耗时稳中有降(最多下降 3ms)。

  • 服务更稳: 极大消除了业务端感知的耗时毛刺,整体服务的稳定性得到明显增强。

某业务敏感数据同步链路管控(BTube)

背景

某核心业务系统承载企业核心敏感数据,需要同步到离线数仓进行数据分析。该业务的同步链路存在以下痛点:

  • 同步链路累计达 800+条,且技术栈陈旧、部署分散;

  • 改造风险高,直接重构不仅成本高昂,还可能影响现有业务正常迭代;

  • 业务定制功能多,例如数据抽取方式多样,有 30+不同加密方式,有租户过滤等业务定制逻辑;

项目核心诉求为:在不中断业务迭代的前提下,实现敏感数据全链路强管控。

解决方案

可视化管理

使用上文的数据同步切面搭建产品页面,实现数据同步链路全生命周期的可视化管理,无需做任何代码改动即可快速新建受保护的传输链路,极大的提升业务同学接入效率。

插件编排实现定制功能

通过业务开发插件加一方插件一起实现传输链路加工,做到平台与业务的解耦,业务与业务的隔离。

资源集约优化

某业务原有链路基于 MQ2ByteLake 实现,其“一任务一表”的模式导致创建了超过 800 个 Flink 任务,带来了显著的资源浪费(部分小增量表仍独占资源)和高昂的运维成本。

BTube Flink 任务是自行拼接 Sink 语句,可以在一个任务中 Sink 到多个目标表,切换到 BTube 后任务数量从 800+ 减少到  100  左右。

迁移自动化

将迁移步骤拆分阶段实施,每一阶段都有明确的验收标准,切换阶段通过自动化工具压缩切换耗时。

  • 事前准备:完成数据对账校验,基础能力对齐,讨论明确实施方案与节奏。按优先级“先低后高”迁移

  • 让业务方协助打标链路的优先级,迁移分批按优先级执行。

  • 按照链路优先级,制定严格的迁移计划,尽量降低迁移的风险。

  • 实施过程:提前发布变更通知,分批切换控制故障影响,实时监控服务/数据/业务指标(周知数仓同学一起观察)。自动化迁移脚本核心逻辑

  • 切换前:检查过渡态与终态任务的 MQ Lag、SQL 解析对比及加解密标签,完成迁移前置条件校验。

  • 切换动作:修改 sink 后,执行 double check 校验过渡态与终态字段的 SQL mapping。

  • 切换后:持续监控数据与服务状态,异常时触发应急回滚机制,支持老链路快速切换或新链路数据回溯。

  • 事后闭环:紧急情况能够止损,持续观察核心指标 1-2 周,完成质量验收后回收资源与权限,并提供后续支持。

全方位对账保障

建设多维度对账方案,配套切换回滚能力,保障链路的稳定性。

  • BTube 链路数据行数对账、内容对账,累计发现多起字段用错问题。

  • 切换过程目标表流量来源对账,保障切换的流量覆盖全部以新链路为准。

  • 过渡态与终态对账,保障切换之前的数据稳定,schema 没有新的变更。

落地效果

资源与成本效益显著提升

将 800+条分散任务收敛至约 100 个聚合任务,大幅提升集群资源利用率与任务运行稳定性;同时实现年化成本节省数十万元,兼顾效率与经济性。

新老链路升级无感高效

单任务切换耗时从数小时压缩至分钟级,极大缩短业务影响窗口;多层对账机制与一键回滚能力,确保迁移过程中业务无感、数据零偏差,实现新老链路平滑过渡。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

相关阅读