关于 sql-jdbc-access-design
面向数据平台产品设计者的技能,用于规划客户通过实时 SQL 查询托管数据的方式。它涵盖架构选择、引擎级租户隔离、模式稳定性、BI 连接、查询治理、凭证和定价。
主要功能
- 01根据扫描频率选择查询架构
- 02在查询引擎中实施租户隔离
- 03制定模式稳定性契约
- 04规划 BI 连接与驱动分发
- 05设置查询治理与成本上限
- 06设计凭证和定价方式
适用场景
- 01为客户提供托管数据的实时 SQL 访问
- 02按查询频率比较共享与复制架构
- 03验证外部 SQL 访问不会泄露其他租户数据
- 04为 BI 仪表板制定稳定的数据模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ developer-platform-skills --skill sql-jdbc-access-design安装后在 Claude Code 中输入/sql-jdbc-access-design确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/bi-connectivity-and-credentials.mdrls-failure-mode-catalog.mdvendor-case-studies.md详细介绍
SQL / JDBC 访问设计
你是一名数据平台产品设计师。设计 SaaS 产品如何通过实时 SQL 向客户开放其托管数据——例如数据仓库共享、只读端点,或 BI 工具连接的 JDBC/ODBC 句柄——确保这一访问面相互隔离、稳定、受治理,并在首位客户连接之前就确定定价。
风险并不对称:API 出错会返回错误响应,但实时 SQL 句柄的隔离出错会返回其他客户的数据行,而失控的客户查询则会让你的基础设施账单暴涨。
从需求侧来看,按照 a16z 的说法:应用正日益基于数据仓库重建,因此受治理的 SQL 访问正在成为一个产品类别,而非小众功能——这只是分析师对“为什么是现在”的定位,并非以下任何实现选择的依据。
记忆(建议): 如果记忆存储在文件中,可考虑使用 developer-platform-context.md;如果使用其他记忆系统,则以该系统为准。该文件仅供参考,并非强制要求。将不同任务的信息分别放在不同章节中。完成的任务应予以移除。为任务添加日期;通用项目背景无需添加日期。不同任务的部分访谈答复可能有所不同。
澄清问题
设计之前先询问这些问题;每个答案都会影响后续步骤。将问题合并提问——这是一个战术性设计任务,而非战略访谈。
- 实际要解决的是哪类问题?这些问题常被混称为“数据共享”,但只有一类属于本技能的完整范围:
- (a) 与合作伙伴_组织_共享数据——会复用此处的数据共享机制。
- (b) 在自有产品的分析功能中为每个租户隔离计算资源——这是内部架构问题,本技能的隔离选项会涉及它,但定价和 BI 部分不会。
- (c) 为_客户_提供可查询其自身数据的 SQL 端点——这是本技能的完整范围。
- 数据目前存放在哪里:云数据仓库(Snowflake、Databricks、BigQuery)、OLTP 数据库,还是自行管理的 OLAP 集群?(这决定了哪些架构档位几乎不需要额外成本。)
- 扫描频率的证据是什么:典型客户每周进行几次临时查询,还是让仪表板持续连接,每天反复扫描许多次?扫描的表有多大?(这会决定架构选择——参见步骤 2。)
- 多租户情况如何:有多少客户账户,规模差异有多大,数据存储是池化的,还是已经按租户隔离?
- 客户实际需要哪些 SQL 使用方式:Tableau/Power BI/Looker、
psql和笔记本,还是由嵌入式应用或 Agent 发起查询?(这会影响 BI 连接能力的投入预算。) - 如何开放架构:原始业务表,还是经过整理的数据集市层?(开放原始数据意味着每次内部重构都会成为影响客户的变更——参见步骤 4。)
- 定价意图是什么:免费功能、仅限更高套餐使用,还是按量计费的附加项——查询计算成本应由谁承担?(参见步骤 8;还要询问标准的三个重新排序问题——上线日期、一次性收益还是复合收益、投入上限。)
如果你的运行环境具有持久记忆,请保存已确定的设计决策(架构档位及其升级条件、隔离机制和身份函数、架构稳定性契约及破坏性变更的判定标准、凭证梯级、治理上限、定价模式),这样后续运行——例如新增使用方、架构变更或重新定价——就能从现有设计出发,而不必从头推导。
消费者类型和扫描频率
两项因素决定方案:
- 消费者类型:
- 使用 Tableau 或 Power BI 的 BI 分析师 需要经过认证的驱动程序、稳定的列名和可预测的类型——他们看不到你的文档,只会看到工具的连接对话框。
- 使用
psql、dbt 或笔记本的 数据工程师 能接受一些不足,但会发起规模最大的查询。 - 嵌入式应用或 Agent 会发起高频的机器生成查询,没有人工审核——这最需要设置严格的费用上限,因为在账单出来之前,没人会注意到异常的查询模式。
- 扫描频率。 每周读取两次的同一张表,与供早间仪表板上的 40 位查看者使用的同一张表,需要不同的架构(第 2 步)。决定经济效益的因素是频率,而不是公司规模。
针对现有消费者中要求最高的类型进行设计,并根据测量得出或如实估算的扫描频率决定架构——绝不要根据购买产品的组织架构来决定。
工作流
- 确认问题并梳理消费者。
- 根据扫描经济效益确定架构。
- 在引擎中强制实施租户隔离。
- 发布模式稳定性契约。
- 设计 BI 连接方式和驱动程序分发方案。
- 配置查询治理和费用上限。
- 发放凭证。
- 选择定价模式。
下文按顺序介绍每个步骤。步骤 3 和 6 都必须通过各自的门槛,才能向任何客户提供访问凭证;其他事项可以分阶段进行。
1. 确认问题并梳理消费者
- 明确回答问题 1,并记录答案。“数据共享”“多租户隔离”和“客户 SQL 访问”指的是彼此相关但不同的架构,如果设计回答了错误的问题,最终采用的隔离机制也会出错。
- 列出具体的消费者(问题 5)及其预期查询模式:仪表板刷新、临时探索、定时提取、Agent 流量。这份清单是下文所有分级选项的依据。
- 说明面向读者的边界:此接口提供_实时查询_。如果消费者实际需要的是定期文件或将数据送入他们自己的数据仓库,请先转交给批量交付配套技能(见参考资料),再进行此处的设计。
2. 根据扫描经济性选择架构
三种架构,按优先级排序。决定哪种架构胜出的是扫描频率,以及数据当前所在的位置,而非个人偏好。
-
效率:
零拷贝共享 > 复制副本或读取端点 > 按租户隔离计算 -
工作量:
按租户隔离计算 > 复制副本或读取端点 > 零拷贝共享 -
价值:
按租户隔离计算 > 零拷贝共享 > 复制副本或读取端点 -
默认选择:零拷贝数仓共享——适用于数据已位于共享云数仓中,且客户很少扫描的情况。共享只是授予元数据访问权限,并不复制数据:构建成本最低,数据始终保持最新,治理仍集中在数据源端。需要注意的是成本模型:实时访问按查询扫描量计费(另加跨区域出口流量费用),因此只有扫描频率较低时,经济性才成立。
-
当扫描频率改变成本计算时,转向复制副本或专用读取端点。
- 一位湖仓实践者的经验法则(Alex Merced——请将其视为个人经验法则,而非基准测试):复制副本按刷新次数付费,实时访问则按查询次数付费。
- 对一张约 200 GB 的表来说,每天扫描约 20 次时,实时重复扫描的出口流量每月成本就超过复制副本一年的成本。
- 每天早上有 40 人加载的仪表板已超过这一临界点。每天查询两次的两位分析师则没有。
- 选择这一方案的代价:数据会停留在最近一次刷新的状态,还需要治理第二份数据副本(包括其中的 PII)。
- 切勿让副本直接指向实时运营架构——参见第 4 步。
-
升级为按租户隔离计算——每个租户拥有自己的查询引擎(在共享存储上运行的按租户实例,统一由一个端点提供服务)。这是资源最紧张的选项:
- 价值最高:从架构上消除吵闹邻居问题,将失控查询的影响范围限制在单个租户,并清晰地按租户归集成本。
- 工作量最大:需要构建并运维一组引擎,因此在效率方面的每轮比较中都处于劣势。
- 但以下两种情况会促使你选择这一方案:多个租户以仪表板或 Agent 的频率运行任意客户编写的 SQL;或者共享集群中最坏情况下的查询已经会威胁产品本身。
- PostHog 两种情况都遇到了:它判断客户查询不受限制,共享集群“基本无法维持”,于是改为使用按组织划分的引擎,并通过 Postgres wire protocol 端点提供服务(参见 references/vendor-case-studies.md)。
此排序是默认建议,而非铁律。根据访谈结果重新排序:
- 已经使用 Snowflake/Databricks/BigQuery,意味着零拷贝几乎不需要额外成本(这也是它成为默认方案的原因)。
- 只有 OLTP、没有数仓的技术栈,应直接移除零拷贝这一选项,而非将其降级。
- 交付期限紧迫时,优先选择平台当前能提供的方案。
- 面向 Agent 消费者,或有复利资产的要求时,即使工作量较大,也应优先考虑隔离方案。
3. 在引擎中强制执行租户隔离
隔离必须由查询引擎实现,绝不能放在应用代码中。应用层的 WHERE tenant_id = ? 筛选条件是这一场景中的典型失败类别:使用外部 SQL 句柄时,已没有应用层可以拦截查询,因此哪怕任何地方漏掉一次筛选条件,也会泄露其他客户的数据。引擎级策略则会让省略筛选条件的查询不返回任何结果。
- 在数据仓库共享中,只公开安全视图/动态视图。这些视图将私有基础表与私有授权表连接起来,并依据_引擎所见的调用者身份_进行筛选——例如 Snowflake 的
current_account()、Databricks Delta Sharing 的current_recipient()。基础表和映射表绝不能纳入共享。 - NULL 上下文陷阱:安全视图跨越账户间共享后,
CURRENT_ROLE()和CURRENT_USER()会返回NULL。基于这些函数编写的策略会悄无声息地匹配不到任何内容;更糟的是,具体结果还取决于谓词的形式,可能匹配所有内容。只有支持共享的身份函数才能安全地跨越共享边界。 - 将接收方属性_分区筛选_视为性能优化,绝不能将其用作访问控制——Delta Sharing 文档明确说明,这种筛选仅尽力而为。真正的隔离只依赖动态视图中的谓词。
- 对于 Postgres 系列引擎,使用带
FORCE ROW LEVEL SECURITY的行级安全性,并在服务器端根据经过验证的令牌将租户身份注入会话上下文——绝不能使用客户端提供的输入。RLS 有四种文档充分说明的失效模式(策略递归、缺少WITH CHECK、宽松策略的 OR 组合、所有者特权绕过视图),此外还有性能规则;编写任何策略之前,请先仔细阅读 references/rls-failure-mode-catalog.md。 - 从消费者一侧而不是提供方一侧验证隔离:以另一个租户的身份运行探测查询(使用第二个测试账户,或数据仓库的模拟消费者会话模式),并要求返回的外部租户数据为零。这是一项门禁检查(见 Measurement),每次更改策略后都要重新运行。
4. 发布架构稳定性契约
客户的仪表板一旦开始查询你的表,你的架构就成了 API:列是字段,表是端点,仪表板是客户端。与 API 版本控制相比,架构变更的不对称之处在于,它们往往是_意外发生的_——例如源系统更新、为了可读性而重命名——随后便会向下游传播,却没有弃用标头提醒任何人。
-
绝不公开实时运营架构。提供从记录系统复制而来的精选数据集市层,让内部架构变动止步于复制边界,而不是传递到客户的仪表板中。
-
采用只增不改规则:可自由添加列和表;绝不直接重命名、更改类型或删除。通过扩展/收缩来演进:
- 添加新列。
- 在文档中弃用旧列。
- 迁移消费者。
- 只有在没有任何依赖时才移除旧列。
Amplitude 的事件架构是一个已发布的成功案例:随着新事件类型的出现,它只会添加表和列,因此天然保持向后兼容。
-
在 CI 中强制执行契约,而不是依赖惯例。只写在文档里的契约不过是一种希望:
- dbt 模型契约(1.5+)会在列变更导致不兼容时使运行失败。
- 开源的
datacontractCLI 会在合并前验证 PR 是否符合契约。
-
将契约写成四个部分,因为 BI 仪表板、dbt 作业和嵌入式应用各自依赖不同的子集:
- 架构:列、类型、可空性。
- 质量:完整性阈值。
- 新鲜度:刷新 SLA。
- 可用性:正常运行时间/延迟。
-
真正无法避免的不兼容变更,必须通过弃用机制发布,并遵守通知期限——版本控制的相关技能负责规定这一流程;本契约则定义哪些变更算作不兼容。
5. 设计 BI 连接能力和驱动分发
支持 BI 工具是一项持续的认证成本,而非一次性集成工作——应将其作为持续工程投入纳入预算,并让问题 5 决定你愿意为此投入多少。
-
如果你的引擎支持,成本最低的做法是使用现有线协议。Postgres 线协议端点可以免费支持所有 BI 工具、
psql和所有 ORM,因为它们都自带 Postgres 驱动——这正是 PostHog 为何用一个 Postgres 线协议端点接入其每租户引擎。代价是方言保真度受限:你只支持 Postgres 方言,不支持任何特殊方言。 -
自定义方言正是这一层要避免的反模式:PostHog 将自有 HogQL 列为无法直接开放的原因之一——“这不是一种已得到广泛支持的语言”,意味着每种 BI 工具集成都得从零开始。
-
自行发布驱动意味着:
- 提供符合规范的 Type-4 JDBC 驱动,并确保 SQLSTATE 准确无误(Tableau 警告称,凭据错误时若返回通用错误而非 SQLSTATE 28000,会破坏连接器行为)。
- 提供各工具专用的签名连接器包;这些包的证书会过期,必须续期。
- 遵循各工具的认证计划及其相关政策;这些政策会将一线连接支持责任交给你,而非 BI 供应商。
各工具的详细信息和认证要求:references/bi-connectivity-and-credentials.md。
-
为每个受支持的客户端发布连接设置页面。没有文档说明的连接字符串会让每位客户都提交支持工单:
- URL 模板。
- 驱动类或下载链接。
- 明确说明 TLS 要求,而不是依赖客户端默认设置。
- 身份验证方式。
6. 配置查询治理和成本上限
所有数仓成本控制都默认关闭;上线 GA 前,必须针对每个外部数仓或引擎逐一配置。它们与 API 速率限制的执行机制不同:你要限制的是长时间运行的引擎的计算秒数和支出,而不是在网关处统计请求数。在此处设计这些限制,并确保请求类限制(连接数、端点每分钟查询数)与 API 配套技能中的策略保持一致。
- 单查询熔断机制:设置语句超时,让失控查询能够终止,而不是运行整夜;另设排队语句超时,避免查询无限期等待可用槽位。
- 按租户或工作负载设置并发上限,避免某个客户的并行仪表板刷新占满共享引擎。
- 单独于超时机制设置支出上限:使用资源监视器(或引擎等效的预算警报机制),在达到信用额度预算时暂停数仓。超时限制单条查询,监视器则限制整个月的支出。
- 读者账户陷阱:为计算费用由你承担的消费者账户(Snowflake 读者账户,或第 8 步中任何由供应商付费的模式)配置资源时,默认情况下,其数仓可消耗无限信用额度,费用计入你的账户。为每个此类账户配置资源监视器,是此服务面的首要成本控制措施,而非可选的加固措施。
- 升级信号:如果某个租户经常让其他租户的查询排队,或触发监视器,就应将其迁移到隔离计算资源或更大的计算资源;绝不要为了迁就单个租户而提高所有租户共用的上限。
7. 签发凭据
业界正在淘汰静态数据库密码——Snowflake 通过分阶段弃用计划强制推行,将所有服务用户迁移到密钥对、OAuth、PAT 或工作负载身份——因此,应按照这一方向设计凭据签发机制,而不是继续沿用密码。
优先顺序:短期令牌 > 托管密钥 > 静态密码;在连接字符串中内嵌凭据是反面模式,任何文档化路径都不应这样做。
- 服务账户和自动化:使用密钥对认证签署短期 JWT,这样密码就不会经由网络传输;将机器身份标记为服务类型,以杜绝回退到密码认证。
- BI 工具:使用 OAuth(桌面工具采用带 PKCE 的授权码模式),这样 BI 工具就不会存储数据库密码。注意:不同客户端对令牌刷新的支持各不相同——无法保存可刷新的令牌的 BI 工具应改用密钥对或 PAT 服务账户,因此认证顺序必须能够平稳降级,而不能假定所有场景都支持 OAuth。
- 从设计上确保每项凭据只能访问租户自身的数据(采用第 3 步中的权限映射),明确要求使用 TLS,并让客户能够自行撤销凭据。密钥格式、轮换体验和密钥管理规范参见凭据相关技能;此场景的具体内容见 references/bi-connectivity-and-credentials.md。
8. 选择定价模式
将 SQL 访问作为付费层级或附加项提供,绝不要设为免费功能。已推出的先例(Stripe Sigma、Heap Connect、Mixpanel Data Pipelines)表明,客户愿意为这项能力付费;而定价本身也是一种治理手段,能管控这个容易造成过度服务、成本高昂的功能面。以下是三种模式,按优先级排序:
-
效率:
固定附加项或层级门槛 > 按用量 / 量级分级 > 提供方承担计算费用后转嫁 -
工作量:
按用量 / 量级分级 > 提供方承担计算费用后转嫁 > 固定附加项或层级门槛 -
价值:
按用量 / 量级分级 > 提供方承担计算费用后转嫁 > 固定附加项或层级门槛 -
默认选择:固定附加项或层级门槛(Heap Connect、Mixpanel 采用的模式)——在更高套餐中解锁 SQL 访问,或将其作为固定价格的附加项。计费工程工作量几乎为零;门槛本身就能限制随意滥用。其风险在于,重度用户的计算成本可能超过固定价格;只有在落实第 6 步的限制后,这才是可接受的。
-
**当你自身承担的成本按用量计费,或客户的数据量相差几个数量级时,升级为按用量 / 量级分级。**这是最少被采用的选项:
- 价值最高:收入随成本变化,因此不会有客户在不知不觉中变成亏损客户。
- 工作量最大:需要用量计量、分级表和超额计费逻辑,因此在满足上述两个条件之一之前,它在效率方面不占优势。
- 计费指标应选择买家已经理解的业务单位(Stripe Sigma 按月交易额分级,查询不限量),绝不要使用非技术买家无法预测的数仓积分。
- Sigma 也表明,指标可以调整:它在产品推出后修改过自身的定价模式。
-
提供方承担计算费用后转嫁与其说是一种自由选择,不如说是采用读取器账户和市场列表时自然形成的模式:你支付计算费用,再按成本加利润向客户开票。只有在每个账户都配备资源监控器(第 6 步)的情况下才采用;另请注意,同一优先级下还有由消费者付费的替代模式:HubSpot 的 Snowflake 共享会将计算费用记入客户自己的账户,并且仅对其最高层级开放。
此排序是默认建议,并非硬性规定——应结合问题 7 和三项访谈结果重新排序:
- 若目标是打造复利资产,应尽早采用按用量计费,因为在固定价格已推出后再补上计量功能,就意味着要面向客户重新定价。
- 若交付期限紧迫,应采用固定门槛,这是唯一无需计费工程的模式。
定价先例及其特定时点的数据见:references/vendor-case-studies.md——向客户引用前,请核实当前定价。
失败模式
反模式清单——每一项都代表一项直接审计发现:
- 使用应用层的
WHERE子句而不是引擎策略来实现租户隔离。 - 共享视图策略使用
CURRENT_ROLE()/CURRENT_USER(),而不是支持共享的身份函数(这两个函数在共享中会返回 NULL)。 - 将分区过滤视为访问控制。
- 允许客户 SQL 接入共享生产集群——这就是 PostHog 所说的“不可持续”情形。
- 直接暴露实时运营架构,导致内部重构破坏客户仪表板。
- 直接上线重命名或重设类型的更改,而不是采用扩展 / 收缩流程,且没有 CI 契约来发现问题。
- 读取器账户(或任何由提供方承担计算费用的模式)没有资源监控器。
- 有标准线协议可用时,却选择自定义查询方言。
- 为了隔离而选择每租户一个架构,结果在连接池和 DDL 迁移下,租户数量超过几百个后便难以为继。
- 只从提供方一侧验证隔离,从未以消费者身份进行探测。
- RLS 特有的陷阱(递归、缺少
WITH CHECK、OR 组合、视图绕过):完整目录及修复方法见 references/rls-failure-mode-catalog.md。
衡量指标
- 隔离门槛:使用另一个租户执行探测查询(通过模拟消费者会话或第二个测试账户)时,在每个共享视图和策略中都不得返回任何其他租户的数据;这是二元门槛,必须在 GA 前以及每次策略或架构变更后通过。
- 架构门槛:任何破坏性变更都不得在未通过 CI 契约检查的情况下进入面向客户的架构;即使没有客户察觉,已发布的破坏性变更也算门槛未通过。
- 成本门槛:每个外部计算句柄在首位客户连接前,都必须设定语句超时、并发上限和支出上限——通过清单审核,并逐个句柄判定通过或失败。
- 从第一个月起建立趋势基线,而非设定阈值:每新增一个连接对应的连接支持工单数、每租户查询成本与实收费用的对比、数据新鲜度 SLA 的达标情况。面向客户的数据界面必须达到明显高于内部仪表板的可靠性标准——此处发生故障会导致支持工单,并带来客户流失风险,因此趋势恶化时应像产品事故一样分级处理。
持续迭代,直到三项门槛全部通过;它们是必须满足的门槛,而非目标。之后再根据这些趋势调整定价并重新审视架构。
调用示例
- “我们的客户总要求将 Tableau 接入我们产品中的数据——我们使用 Snowflake,请设计 SQL 访问界面。”
- “我们想将直接 SQL 访问作为企业版附加功能出售。考虑到大多数客户会在其上部署仪表板,架构和定价应该如何设计?”
- “请审查我们向客户开放生产环境 Postgres 只读副本的计划——会有哪些问题?”
参考资料
samber/developer-platform-skills@etl-connector-strategy- 作为其他人的 ETL 平台上的源连接器;本技能讨论的是客户直接查询你的数据。samber/developer-platform-skills@api-integration-surface-strategy- 在其他集成界面中,是否应该提供 SQL 访问的总体决策。samber/developer-platform-skills@api-auth-key-management- API 凭据的密钥格式、轮换体验和密钥卫生;第 7 步将同样的规范应用于数据库句柄。samber/developer-platform-skills@api-versioning-policy- 弃用机制和通知期限;第 4 步的契约定义架构中何为破坏性变更,该技能则规定如何宣布并淘汰破坏性变更。samber/developer-platform-skills@api-rate-limit-policy- API 网关的请求计数限额;第 6 步改为限制查询引擎的计算量和支出,两项已发布的策略不得互相矛盾。