海量数据卷(Large Data Volumes)

当 org 积累数百万条记录,性能问题随之而来。本文系统讲解 Salesforce 海量数据卷(LDV)管理:设计阶段规避账户/所有权/查找三类数据偏斜、用外部对象做数据分层;查询阶段利用查询优化器、Batch Apex、Bulk API 与 skinny 表;加载阶段精简加载、选 Bulk API、暂停事件;删除提取阶段用硬删除、PK chunking 与截断,构建可扩展到数百万记录的架构。...

📅 2026/10/7 ✍️ ponybai 🏷️ salesforce, headless

一、设计数据模型

slide_2
slide_3

本单元学习为海量数据卷(LDV)设计数据模型、三种数据偏斜(skew)及规避方法,以及用外部对象扩展数据模型。

学完本单元,你将能够:为海量数据卷设计数据模型、描述三种数据偏斜及规避方法、用外部对象扩展数据模型。

为规模而规划数据模型

slide_4

数据是任何应用的关键要素之一。用户整天、每天都在不断创建数据——突然之间,你的 org 已积累数百万条记录、数千个用户与数 GB 的存储。海量数据卷(Large Data Volumes,LDV)会导致性能迟缓:查询变慢、搜索与列表视图变慢、sandbox 刷新变慢。如果你从一开始就规划如何容纳 LDV、设计可扩展的数据模型,就能避免这种困境。关键洞察是:这些性能问题事后很难修复——在初始架构阶段就要考虑海量数据卷。

数据偏斜(Data Skew)概览

slide_5

管理海量数据卷的关键之一是精心架构记录所有权,避免数据偏斜。当 org 内超过 10,000 条子记录关联到同一条父记录时,就发生数据偏斜。要记住这个阈值:10,000 条记录。规避策略:规划足够的账户,让每个父记录下的子记录数低于此阈值;并在新子记录创建时把它们分布到这些账户上。如果不采用这些策略,可能发生三种数据偏斜:账户数据偏斜(account data skew)、所有权偏斜(ownership skew)、查找偏斜(lookup skew)。

账户数据偏斜——记录锁定

slide_6

某些对象(如账户与机会)在私有共享模型下维护父子记录访问的特殊数据关系。太多子记录关联到同一父对象会导致账户数据偏斜。例如把一批未分配的联系人塞到一个名为"Unassigned"的账户下,会引发记录锁定与共享性能问题。记录锁定场景:你在多个线程中更新同一账户下的大量联系人,每次更新系统都会锁定被更改的联系人及其父账户以维护数据库完整性。尽管每次锁定都很短暂,但由于所有更新都试图锁定同一账户,更新很可能因前一个更新仍持有账户锁而失败。

账户数据偏斜——共享问题

slide_7

共享方面也有类似的动态。取决于你的共享配置,当你做一件看似简单的事——比如更改账户的所有者——你可能需要检查该账户的每一条子记录并调整它们的共享,包括重新计算角色层级与共享规则。如果涉及几十万条子记录,这会耗费大量时间。根本原因相同:太多子记录集中在一个父记录下。解决方案是把子记录分布到多个账户,完全避免"Unassigned"这种反模式。

所有权偏斜(Ownership Skew)

slide_8

当大量同类型记录被单个用户拥有时,这种不平衡会导致所有权偏斜。既然每条记录都必须有所有者,看似自然的解决方案是把这些记录倾斜到一个通用所有者(如"Unassigned")。但偏斜所有者存在于角色层级中时,删除或所有权更新等操作必须从旧所有者、角色层级中的所有父用户,以及共享规则授予访问的所有用户移除共享——这就是为什么所有权变更往往是系统中最昂贵的交易变更之一。如果所有权偏斜确实无法避免,最好确保偏斜所有者没有角色,这样就把该用户及其记录从角色层级与相关共享规则中移除了。

查找偏斜(Lookup Skew)

slide_9

当大量记录关联到查找对象(你正在搜索的对象)中的同一条记录时,会发生查找偏斜。由于查找字段可以放在 Salesforce 的任何对象上,查找偏斜会给 org 内任何对象带来问题。查找字段本质上是对象间的外键关系:每次插入或更新记录,Salesforce 都必须锁定每个查找字段选中的目标记录,以保证数据提交时完整性得以维护。正常情况下保存操作快到不会遇到锁,但在自动化流程中同时叠加自定义代码与 LDV,就可能遇到导致插入/更新失败的锁异常。由于没有专门工具识别查找偏斜,建议评估记录量大且并发插入/更新活动频繁的对象。

用外部对象做数据分层

slide_10

另一种 LDV 策略是完全不必把数据带入 Salesforce——使用外部对象。外部对象与自定义对象类似,但映射到存储在 Salesforce org 外部的数据,让用户能搜索并交互外部数据。通过按需访问记录数据,外部对象始终反映外部数据的当前状态,无需在 Salesforce 中管理副本、浪费存储与同步资源。外部对象最适合:你有大量无法或不想存储在 org 中的数据,且任意时刻只需访问其中一小部分。Salesforce Connect 用外部数据源访问 org 外数据,Files Connect 用外部数据源访问第三方内容系统。

外部对象查找——三种关系类型

slide_11

外部对象支持标准查找关系(用 18 字符 Salesforce 记录 ID),但 org 外存储的数据往往不含这些 ID。因此外部对象有两种特殊查找关系:外部查找(external lookup)与间接查找(indirect lookup)。当父对象是外部对象时用外部查找,它把子对象(标准/自定义/外部)链接到父外部对象,用父对象上 External ID 标准字段的值与外部查找关系字段值匹配。当外部数据不含 Salesforce 记录 ID 时用间接查找,它把子外部对象链接到父标准/自定义对象,选择父对象上具有 External ID 与唯一属性的自定义字段与子的间接查找字段匹配。选择关系类型取决于父数据在哪、外部数据是否含 Salesforce 记录 ID。

二、进行数据查询与搜索

slide_12
slide_13

本单元学习在不降低性能的情况下查询海量数据、用批量查询高效查询大数据集,以及用 skinny 表容纳常用字段的好处。

学完本单元,你将能够:在不降低性能的情况下使用查询、用批量查询高效查询大数据集、描述 skinny 表的优势。

搜索架构与索引

slide_14

在 org 中存储海量数据会影响性能,搜索也不例外。Salesforce 搜索架构基于自己的数据存储,针对自由文本搜索做了优化。数据要能被搜索,必须先索引。索引搜索先搜索索引找到合适记录,再按访问权限、搜索限制与其他过滤器缩小结果,形成结果集(通常包含最相关结果)。当结果集达到预定大小后,其余记录被丢弃;结果集随后查询数据库检索用户看到的字段。当大量数据被添加或更改时,整个过程可能耗时很长。关键洞察:你没索引什么和你索引了什么同样重要——选择性查询更快。

SOQL 与 SOSL 的选择

slide_15

SOQL(Salesforce Object Query Language)用于查询子到父(常为多对一)与父到子(几乎总是一对多)关系,每事务最多返回 50,000 条记录。SOSL(Salesforce Object Search Language)能对字段内的多个词做分词,并据此构建搜索索引。事务限制:单个 SOSL 搜索可返回完整 40,000 条记录(同一事务中的 SOQL 还可返回其 50,000 条);若事务中有多个 SOSL 搜索(上限 20 个),每个最多返回 2,000 条(20×2,000=40,000 总量)。经验法则:对已知字段的结构化查询用 SOQL,跨多对象搜索自由文本用 SOSL。

Salesforce 查询优化器

slide_16

由于 Salesforce 采用多租户架构,数据库系统自带的优化器无法有效优化 Salesforce 查询。因此 Salesforce 平台内置了自己的查询优化器,利用索引字段为给定查询创建最有效的执行计划。它维护一张关于每个索引中数据分布的统计表,用这张表执行预查询来判断使用索引能否加速查询。它作用于所有自动生成的查询——报告、列表视图、SOQL 查询及依赖它们的其他查询。你的职责是编写能让优化器利用索引的 WHERE 子句(过滤索引字段);若 WHERE 过滤的是未索引字段,优化器只能全表扫描。

Batch Apex——处理多达 5000 万条记录

slide_17

查询与处理大数据集的最佳方式通常是异步分批进行。用 Batch Apex 最多可查询处理 5,000 万条记录。它把工作拆成可管理的块,每个块在自己的事务中以全新的限额运行,异步执行不阻塞用户,可安排在非高峰时段。局限:不适用于同步用例(例如需要查询超过 50,000 条的 Visualforce 页面);每个事务仍有 governor 限额。适合场景:夜间处理、数据清理、大规模更新、历史数据的复杂计算;不适合实时面向用户的查询。

批量查询——检索多达 15 GB 数据

slide_18

批量查询可检索最多 15 GB 数据,分成 15 个 1 GB 文件;结果超过 1 GB 时自动添加文件补全响应。Bulk API 查询支持 query 与 queryAll 两种操作:queryAll 会返回因合并或删除而删除的记录,以及归档的 Task 与 Event 信息。给批量查询任务添加批时,请求头 Content-Type 必须是 text/csv、application/xml 或 application/json(取决于创建任务时指定的类型),而实际的 SOQL 语句始终是纯文本格式。

批量查询的处理——超时与重试

slide_19

批量查询有两个超时阈值:查询执行有 2 分钟超时,若查询未完成则任务失败并返回 QUERY_TIMEOUT 错误——解决方法是重写更简单的查询并重新提交;结果检索有 5 分钟超时或 1 GB 文件大小限制,若超限则结果被缓存并再次尝试,30 次尝试后任务失败并返回"Retried more than thirty times"——此时应考虑用 PK Chunking 头把查询结果拆成更小的块。查询与检索都成功后,结果存储 7 天。要点:批量查询失败时,解决方案几乎总是让查询更简单或块更小。

Skinny 表——强大的性能工具

slide_20

即使遵循编码最佳实践并放置了自定义索引,性能仍可能在超大规模下恶化。这时有一个特殊而强大的解决方案:skinny 表。skinny 表是一张自定义表,包含标准或自定义基础对象字段的子集。由于行更窄、扫描的数据更少,每次数据库抓取能返回更多行,读取大对象时吞吐量更高。

skinny 表不包含软删除行(回收站中 isDeleted=true 的记录),常能显著减小表体积;基础表上的自定义索引也会复制,且因底层查询中表连接减少而表现更好。例如不再用昂贵的日期范围计算做年度报告,而是用 skinny 表加一个 Year 字段、过滤 Year='2016'。平台自动同步基础对象与 skinny 表之间的行,并在查询运行时决定何时使用 skinny 表,无需改报告或写 Apex/API 代码。

Skinny 表的考量与局限

slide_21

skinny 表很有用,但也有代价:skinny 表很"瘦"——只含满足特定用例所需的最小字段集,之后要给报告加字段必须联系 Salesforce Support 重建;sandbox 行为——Full sandbox 会复制 skinny 表,其他类型 sandbox 不复制(需 Support 激活);无动态元数据——改字段类型(如数字改文本)会使 skinny 表失效,需重建。最佳候选:含数百万条记录的表、自定义对象以及 Account/Contact/Opportunity/Lead/Case 对象、性能关键的报告/列表视图/SOQL。skinny 表增强性能,但不替代良好设计——基础对象上的高效索引在许多情况下已足够。

三、加载数据

slide_22
slide_23

本单元学习精简数据加载的好处、Bulk API 相对 SOAP API 的优势,以及通过暂停数据验证与丰富操作来加速大数据集加载。

学完本单元,你将能够:描述 Salesforce 中精简数据加载的好处、理解加载大量数据时 Bulk API 相对 SOAP API 的优势、通过暂停验证与丰富操作加速加载。

精简加载——提速策略

slide_24

无论是 LDV 迁移还是持续的大数据同步,最小化对业务关键操作的影响都是最佳实践。聪明策略是精简加载(loading lean)——只包含满足业务关键操作所需的数据与配置。四步:识别业务关键操作;识别实施这些操作所需的最小数据集与配置;定义数据与配置策略;尽快加载以减少同步范围。影响加载速度的配置选择:共享模型(Private 会在添加记录时计算共享,Public Read/Write 可推迟到切换后)、复杂对象关系(延后非关键查找)、共享规则(延后基于所有权/条件的规则)、工作流/验证规则/触发器(加载时禁用)。

加载时不可推迟的配置

slide_25

移除障碍能加速加载,但有些配置是必需(或至少强烈建议)的,不可省略:带主从子记录的父记录——父记录不存在就无法加载子记录,必须按顺序先加载父记录;记录所有者——记录需要有所有者,这些用户必须在加载前就存在于系统中;角色层级——你可能以为记录所有者不在角色层级中加载会更快,但几乎所有情况下性能相同(加载门户账户时反而明显更快),因此推迟这部分配置没有好处。这三项是前提条件,不是可选的优化。

Bulk API 与 SOAP API 的数据加载

slide_26

加载 LDV 时,所选 API 影响很大。SOAP API 针对实时客户端应用优化,一次更新少量记录;用它加载大量数据需要开发者实现复杂的分块上传、监控与重试逻辑,小数据量尚可接受,大数据集则笨重耗时。Bulk API 基于 REST 原则,专为简化从几千到数百万条记录的处理而设计,提供超快处理速度、更少的客户端代码、易监控的任务状态、失败记录自动重试、并行处理支持、最少的往返与 API 调用、易调的批大小。简言之,它是插入、查询、删除记录的最快方式。

Bulk API 的内部工作原理

slide_27

用 Bulk API 上传记录时,记录被流式传入创建新任务,数据先存入临时存储,再切分成用户定义的批(每批最多 10,000 条)。即使数据仍在发送到服务器,平台也在提交批进行处理。批可按需并行或串行处理,工作从客户端移到 Salesforce 服务器。API 记录每个任务的状态并自动重试失败记录;任务超时会被自动放回队列重试。每个批独立处理,批完成(无论成败)后任务更新结果,任何有权限的人都能从 Salesforce Admin UI 监控管理任务。API 承担了复杂性——你只需提交并监控。

通过暂停事件加速加载

slide_28

验证规则、工作流规则与触发器在日常操作中保证数据完整性,但在海量数据加载时会让插入慢到爬行。三阶段策略在牺牲质量的前提下加速加载:阶段一——分析与准备数据(加载前):检查通常由自动化满足的业务需求,在加载前清洗数据(查询数据集、修复不符合验证规则的记录),并对对象间关键依赖排序;阶段二——禁用事件(加载中):把验证规则、工作流、分配规则设为 inactive,用带复选框字段的自定义设置在触发器代码中加判断条件来禁用触发器;阶段三——后处理(加载后):重新启用所有规则与触发器,用 Batch Apex 或 Bulk API 做数据丰富(查找关系、累计汇总、外键)。

用自定义设置禁用触发器

slide_29

临时禁用验证规则与工作流很简单:编辑每条规则设为"inactive"即可。禁用触发器稍复杂,需要准备:先创建一个自定义设置与对应的复选框字段来控制触发器何时触发,然后在触发器代码顶部加入判断语句。

模式:1) 创建带复选框字段的自定义设置(如 Bypass__c);2) 每个触发器顶部加护栏子句 if (MySetting__c.getInstance().Bypass__c) return;;3) 加载前勾选 → 触发器禁用;4) 加载后取消勾选 → 触发器启用。这给了你单一控制点——翻转一个复选框就能禁用所有遵循此模式的触发器,无需部署代码或编辑触发器主体。

四、执行数据删除与提取

slide_30
slide_31

本单元学习用 Bulk API 硬删除永久移除记录、用 PK chunking 拆分超大提取,以及用 truncate 快速清除 sandbox 中自定义对象的测试数据。

学完本单元,你将能够:用 Bulk API 硬删除机制正向影响海量数据性能、用 PK chunking 应对提取海量数据的慢性能、理解截断自定义对象记录加速删除的好处。

软删除与硬删除

slide_32

Salesforce 对用户删除的数据使用回收站(Recycle Bin)——数据并非被移除,而是标记为已删除并可在回收站中看到,这个过程叫软删除。软删除的数据仍存在于 org 中,且每次查询都要排除已删除记录,因此在海量数据下仍影响数据库性能。数据在回收站保留 15 天,或直到回收站达到特定大小,之后才物理删除。Bulk API 支持硬删除(物理删除)选项,让记录绕过回收站、立即可被删除,是删除海量数据以更早释放空间、避免无关数据影响性能的推荐策略。注意硬删除默认禁用,需管理员启用。

PK Chunking——提取数亿条记录

slide_33

当按字段值过滤在极高数据量(数亿条记录)下不切实际时,用 PK Chunking。PK 代表主键——对象的记录 ID,它始终被索引。PK chunking 按被查询记录的记录 ID 范围把超大表上的批量查询拆成块,每块是独立查询、独立批,平台自动拆分并执行所有块。启用方式:在 Bulk API 任务请求上指定头 Sforce-Enable-PKChunking: chunkSize=50000。使用时机:表超过 1,000 万条记录,或批量查询持续超时。支持对象:Account、Campaign、CampaignMember、Case、Contact、Lead、LoginHistory、Opportunity、Task、User 及所有自定义对象。

PK Chunking 的配置与监控

slide_34

在头中指定块大小(如 chunkSize=50000),每个块作为独立批计入每日批限制,结果需分别下载。你可以结合 WHERE 子句在 PK chunking 时过滤,过滤后某块返回的记录可能少于 chunkSize。练习技巧:若查询的记录远少于 1,000 万,可设置较小的 chunkSize(如 chunkSize=1000)来观察 PK chunking 的实际效果。监控分块任务:分块成功时原始批状态显示 NOT_PROCESSED(别被这个状态吓到,真正的工作发生在分块后的批里);分块失败时原始批显示 FAILED,但已成功排队的块仍正常处理;随后监控各个批、逐个下载结果,全部完成后再安全关闭任务。

截断自定义对象——即时清理

slide_35

截断(truncate)是永久移除自定义对象所有记录、同时保留对象及其元数据以供将来使用的快速方法。它擦除对象当前所有记录、对象回收站中的记录、对象的历史记录,以及每条被删记录相关的事件、任务、备注与附件。启用:Setup → User Interface(列表最底部)→ 在 Setup 区勾选 Enable Custom Object Truncate → Save。警告:截断不可逆,记录无法恢复,被截断对象的副本会在 Deleted Objects 列表保留 15 天后永久删除。最佳用例:sandbox 中的自定义对象填满测试数据后,截断以清除测试记录、保留对象结构用于生产。限制:不能截断标准对象、被其他对象通过查找引用的对象、主从关系的主对象、有报表快照/自定义索引/External ID/已激活 skinny 表的对象。

海量数据卷——完整策略

slide_36

管理 Salesforce 中的 LDV 需要跨数据生命周期四个阶段的整体方法:设计——从第一天起为规模而构建,通过分布子记录避免数据偏斜、数据无需存于 Salesforce 时用外部对象;查询——写利用索引的选择性 WHERE 子句、用 Batch Apex 处理多达 5,000 万条、用 Bulk API 查询提取多达 15 GB、考虑为性能关键对象用 skinny 表;加载——精简加载、选 Bulk API 而非 SOAP API、加载时暂停事件加载后处理;删除与提取——用 Bulk API 硬删除绕过回收站、超 1,000 万条提取用 PK chunking、sandbox 即时清理用截断。每个阶段相互加强——提前规划、在规模下测试,你的 org 就能在数百万条记录下表现良好。


文章来源:Trailhead - Large Data Volumes