Salesforce Big Objects:处理海量数据的可扩展架构指南
背景与应用场景
作为一名 Salesforce 架构师,我在设计企业级解决方案时,最常遇到的挑战之一就是如何处理和管理海量数据。在 Salesforce 平台中,标准的 Custom Objects(自定义对象)和 Standard Objects(标准对象)在处理数千万甚至数亿条记录时,会面临性能瓶颈、存储成本高昂以及查询超时的风险。这些对象是为事务性数据(Transactional Data)设计的,强调的是数据的实时交互、复杂的业务逻辑和丰富的用户界面集成,而非大规模数据存储和分析。
当企业需要追踪用户在网站上的每一次点击、存储来自 IoT (Internet of Things,物联网) 设备的传感器读数、或归档长达十年的客户服务历史记录时,传统的数据模型便显得力不从心。这不仅会拖慢核心 CRM 应用的性能,还会快速消耗昂贵的数据存储空间。
为了解决这一挑战,Salesforce 推出了 Big Objects(大对象)。Big Objects 是一种专为在 Salesforce 平台上存储和管理海量数据而设计的解决方案。它们构建于一个可扩展的大数据后端(如 Apache HBase),旨在提供一致的性能,即使在处理数十亿条记录时也是如此。从架构师的角度来看,Big Objects 并非要取代标准对象,而是作为整体数据策略中一个至关重要的补充,用于特定的大数据场景。
典型的应用场景包括:
- 客户 360 度视图归档: 存储客户的长期行为数据,例如网站点击流、应用使用日志、历史购买记录等。这些数据量巨大,但查询模式相对固定(例如,按客户 ID 和时间范围查询),非常适合 Big Objects。
- 审计与合规性: 记录详细的字段变更历史、系统访问日志或交易审计追踪。对于金融、医疗等受严格监管的行业,需要长期保留这些不可变的记录,Big Objects 提供了理想的存储方案。
- 物联网(IoT)数据集成: 捕获和存储来自成千上万个连接设备的时间序列数据,如传感器读数、设备状态更新等。这些数据写入频繁,且通常按设备 ID 和时间戳进行查询。
- 数据归档策略: 将标准对象(如 Case、Order)中不再活跃的历史数据迁移到 Big Objects 中。这可以显著提升核心对象的性能,减少存储成本,同时保留对历史数据的查询能力,实现“冷热数据分离”的架构。
原理说明
要理解 Big Objects 的强大之处,首先必须明白其底层架构与标准对象有着根本性的区别。标准对象运行在 Salesforce 的多租户核心数据库上,这是一个高度优化的关系型数据库。而 Big Objects 则构建于大数据技术之上,这赋予了它们处理海量数据的能力,但也带来了一些独特的设计约束。
1. 核心构成:索引(Index)
Big Objects 的核心设计理念是索引驱动。在定义一个 Custom Big Object(自定义大对象)时,你必须定义一个索引,它由一个或多个字段组成,作为复合主键。这个索引决定了数据在底层如何被物理存储、分区和排序。所有对 Big Objects 的查询必须利用这个索引,否则将无法执行或效率极低。索引字段的顺序至关重要,查询条件必须从索引的第一个字段开始,按顺序提供,不能跳过。
例如,如果你定义了一个索引 `(AccountId__c, EventTimestamp__c)`,那么高效的查询必须首先过滤 `AccountId__c`,然后才能过滤 `EventTimestamp__c`。你不能只过滤 `EventTimestamp__c`。
2. 数据操作的限制
与标准对象支持完整的 CRUD (Create, Read, Update, Delete) 操作不同,Big Objects 的设计是写一次,读多次(Write-Once, Read-Many)。它们仅支持 DML (Data Manipulation Language,数据操作语言) 的 `insert` 操作。不支持 `update`、`upsert` 或 `delete` 单条记录。这种不可变性(Immutability)是其高性能和可扩展性的关键,因为它简化了底层数据管理。如果需要删除数据,唯一的方法是截断(Truncate)整个 Big Object(需要 Salesforce 开启相应功能)。
3. 查询机制
查询 Big Objects 主要通过以下两种方式:
- 标准 SOQL (Salesforce Object Query Language,Salesforce 对象查询语言): 可以在 Apex(一种 Salesforce 的专有编程语言)中像查询普通对象一样使用 SOQL。但是,这种查询是同步的,并且必须严格遵循索引规则,适用于小范围、目标明确的数据检索。
- Async SOQL (Asynchronous SOQL,异步 SOQL): 这是查询海量数据的推荐方式。Async SOQL 会在后台处理查询作业,并将结果集存储在一个目标对象(标准、自定义或另一个 Big Object)中。它专为处理数百万甚至数十亿条记录而设计,可以避免同步查询的超时限制。
4. 用户界面与自动化
Big Objects 的数据不会出现在标准的 Salesforce 报表、仪表盘或列表视图中。要将 Big Objects 的数据显示给用户,必须构建自定义的用户界面,例如使用 Lightning Web Components (LWC) 或 Visualforce。此外,Big Objects 不支持标准自动化工具,如 Triggers、Flows 或 Process Builder。
示例代码
以下示例代码均来自 Salesforce 官方文档,旨在说明如何定义、插入和查询 Big Objects。
1. 定义一个自定义 Big Object
Big Objects 通常通过 Metadata API(元数据 API)进行定义。下面是一个名为 `Customer_Interaction__b` 的 Big Object 定义文件(`Customer_Interaction__b.object`),用于存储客户交互日志。
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<deploymentStatus>Deployed</deploymentStatus>
<fields>
<fullName>Account__c</fullName>
<label>Account</label>
<length>18</length>
<required>true</required>
<type>Text</type>
<unique>false</unique>
</fields>
<fields>
<fullName>Interaction_Type__c</fullName>
<label>Interaction Type</label>
<length>255</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</fields>
<fields>
<fullName>Timestamp__c</fullName>
<label>Timestamp</label>
<required>true</required>
<type>DateTime</type>
</fields>
<indexes>
<!-- 定义一个复合索引,这是查询性能的关键 -->
<fullName>CustomerInteractionIndex</fullName>
<fields>
<name>Account__c</name>
<sortDirection>ASC</sortDirection>
</fields>
<fields>
<name>Timestamp__c</name>
<sortDirection>DESC</sortDirection>
</fields>
<label>Customer Interaction Index</label>
</indexes>
<label>Customer Interaction</label>
<pluralLabel>Customer Interactions</pluralLabel>
</CustomObject>
注释: 在这个定义中,`CustomerInteractionIndex` 是索引,它包含了 `Account__c` 和 `Timestamp__c` 两个字段。这意味着所有的高效查询都必须先提供 `Account__c` 的值。
2. 使用 Apex 插入数据
可以使用 Apex 的 `Database.insertImmediate()` 方法向 Big Objects 中同步插入记录。
// 准备要插入的 Big Object 记录列表
List<Customer_Interaction__b> interactions = new List<Customer_Interaction__b>();
Customer_Interaction__b interaction1 = new Customer_Interaction__b();
interaction1.Account__c = '001xx000003DHPxAAO'; // 必须提供索引字段的值
interaction1.Timestamp__c = Datetime.now(); // 必须提供索引字段的值
interaction1.Interaction_Type__c = 'Website Login';
interactions.add(interaction1);
Customer_Interaction__b interaction2 = new Customer_Interaction__b();
interaction2.Account__c = '001xx000003DHPxAAO';
interaction2.Timestamp__c = Datetime.now().addMinutes(-10);
interaction2.Interaction_Type__c = 'Viewed Product Page';
interactions.add(interaction2);
// 使用 Database.insertImmediate() 插入记录
// 对于 Big Objects,必须使用这个方法,而不是标准的 insert DML 语句
Database.SaveResult[] results = Database.insertImmediate(interactions);
// 检查插入结果
for (Database.SaveResult res : results) {
if (res.isSuccess()) {
System.debug('Successfully inserted interaction with ID: ' + res.getId());
} else {
for (Database.Error err : res.getErrors()) {
System.debug('Error inserting interaction: ' + err.getStatusCode() + ': ' + err.getMessage());
}
}
}
注释: 对于大批量数据加载,强烈推荐使用 Bulk API 2.0,因为它更高效,并且能更好地处理大型数据集。
3. 使用 Async SOQL 查询数据
当需要从 Big Object 中查询大量数据进行分析时,Async SOQL 是最佳选择。以下示例查询特定客户在过去一年的所有交互,并将结果存入一个名为 `Interaction_Archive__c` 的自定义对象中。
// 定义目标对象和查询语句
AsyncSOQLOptions options = new AsyncSOQLOptions();
options.targetObject = 'Interaction_Archive__c'; // 结果将存储在这个对象中
options.targetFieldMapping = new Map<String, String>{
'Source_Account_Id__c' => 'Account__c',
'Interaction_Date__c' => 'Timestamp__c',
'Interaction_Details__c' => 'Interaction_Type__c'
};
// 构建查询语句,注意 WHERE 子句遵循索引顺序
String query = 'SELECT Account__c, Timestamp__c, Interaction_Type__c ' +
'FROM Customer_Interaction__b ' +
'WHERE Account__c = \'001xx000003DHPxAAO\' ' +
'AND Timestamp__c >= LAST_N_YEARS:1';
// 提交异步查询作业
AsyncApexJob job = System.enqueueAsyncSOQL(query, options);
// 可以通过 job.Id 跟踪作业状态
System.debug('Async SOQL job submitted with ID: ' + job.Id);
注释: Async SOQL 作业是异步执行的,不会立即返回结果。你需要通过 `AsyncApexJob` 对象来监控其进度(例如,`Status` 字段为 `Completed`, `Failed` 等)。
注意事项
作为架构师,在方案中引入 Big Objects 前,必须仔细评估以下限制和注意事项:
- 索引设计是关键且不可变: Big Object 的索引一旦创建就无法修改。在设计阶段必须深入理解未来的数据查询模式,因为一个糟糕的索引设计会使整个 Big Object 变得毫无用处。
- 查询的局限性: SOQL for Big Objects 的功能集比标准 SOQL 要小。例如,它不支持 `OR`、`NOT`、`LIKE` 等操作符,并且对聚合函数的支持也有限。所有查询都必须高度依赖于索引。
- API 和限制: Async SOQL 和 Bulk API 调用会消耗 API 限额。在设计高频率读写的集成时,需要仔细规划 API 的使用。
- 权限和可见性: Big Objects 的访问权限通过 Profile(简档)和 Permission Set(权限集)进行控制,与标准对象类似。需要确保用户拥有正确的读写权限。 -
- 关系字段: Big Objects 只能与标准或自定义对象建立 Lookup(查找)关系,不支持 Master-Detail(主从)关系。这意味着它们不能参与汇总字段(Roll-up Summary Fields)的计算。
- 没有开箱即用的 UI: 业务用户无法直接查看或报告 Big Object 中的数据。任何可视化需求都意味着额外的开发工作,需要创建 LWC 或其他自定义组件。对于分析需求,推荐的路径是集成 Tableau CRM (原 Einstein Analytics)。
总结与最佳实践
Big Objects 是 Salesforce 平台上一把解决海量数据存储和查询问题的“利器”,但它并非万能。作为架构师,我们的职责是判断何时以及如何正确地使用它。
何时使用 Big Objects?
- 当数据量达到亿级或以上时。
- 当数据是不可变的,或主要是追加写入时(例如,日志、事件、历史快照)。
- 当查询模式是预定义的、简单的,并且可以完全基于索引执行时。
- 当主要目标是数据归档,以提升核心 CRM 对象的性能时。
何时应考虑其他方案?
- 标准/自定义对象: 适用于需要复杂业务逻辑、实时 UI 交互、标准报表和自动化的事务性数据。
- Salesforce Connect: 当数据需要保留在外部系统(如 ERP、数据仓库)中,但又需要在 Salesforce UI 中实时查看时。
- Heroku / 外部数据库: 当需要复杂的数据转换、ETL 过程、灵活的关系模型或不受 Salesforce Governor Limits 限制的计算能力时。
最佳实践:
- 设计先行: 在编写任何代码或部署任何元数据之前,请花费充足的时间设计你的 Big Object 索引。这是通往成功的首要且最关键的一步。
- 数据加载策略: 对于大规模数据导入,始终优先选择 Bulk API 2.0。它为处理大型数据集进行了优化,并且具有更高的容错性。
- 异步处理为王: 对于任何可能返回大量结果的查询,始终使用 Async SOQL。不要试图在同步 Apex 中挑战大数据量,这会直接导致失败。
- 规划数据可视化: 如果业务用户需要访问 Big Object 数据,请提前规划好自定义 UI 的开发,并评估使用 Tableau CRM 等分析工具的可行性。
- 将其视为归档层: 将 Big Objects 视为数据架构中的归档层或分析层,而不是核心事务层。它与核心 CRM 对象协同工作,而不是取而代之。
通过遵循这些原则,Salesforce 架构师可以有效地利用 Big Objects 构建出既可扩展又高性能的解决方案,从容应对企业日益增长的数据挑战。
评论
发表评论