Salesforce 共享规则:架构师指南之可扩展的数据可见性与性能优化
背景与应用场景
在 Salesforce 平台的宏伟蓝图中,数据安全模型是其基石。作为一名 Salesforce 架构师,我的职责是设计一个既能保护敏感数据,又能促进团队协作,同时还能保证系统高性能和可扩展性的安全架构。在这个多层次的安全模型中,Organization-Wide Defaults (OWD, 组织范围默认设置) 设定了最严格的基线访问级别。然而,在现实的商业场景中,严格的基线往往无法满足复杂的跨部门、跨区域协作需求。这时,Sharing Rules (共享规则) 就作为一座桥梁,优雅地在严格的 OWD 之上,为特定用户群体打开了访问特定记录集的通道。
Sharing Rules 的核心价值在于,它允许我们以一种声明式、可预测的方式,横向扩展数据可见性,而无需修改 OWD 或复杂的角色层级。它解决了当 OWD 设置为“私有 (Private)”或“公共只读 (Public Read-Only)”时,特定用户组需要访问不属于他们的记录这一常见问题。
典型的应用场景包括:
- 跨职能协作: 销售团队(位于一个角色层级分支)需要将价值超过一百万美金的商机记录共享给法务团队(位于另一个完全独立的角色层级分支)进行审核。通过创建一个基于条件的共享规则(例如 `Amount > 1,000,000`),我们可以精确地将这些商机记录的读写权限授予法务团队所在的公共组。
- 区域团队支持: 一个全球化的支持团队被划分为美洲、欧洲和亚太三个区域。亚太区的支持代理需要查看所有来自亚太区客户的高优先级个案,无论这些个案的所有者是谁。一个基于条件的共享规则(例如 `Customer_Region__c = 'APAC' AND Priority = 'High'`)可以将这些个案共享给“亚太支持团队”这个公共组。
- 矩阵式组织结构: 在现代企业中,员工可能同时向多个项目经理或职能经理汇报,这种矩阵结构无法用单一的 Role Hierarchy (角色层级) 来完美体现。共享规则可以弥补这一不足,例如,将特定项目相关的所有记录共享给该项目团队成员组成的公共组。
- Experience Cloud 站点访问: 通过特殊的 Guest User Sharing Rule (访客用户共享规则),我们可以控制未经身份验证的外部用户(访客)能够访问哪些记录,这对于构建公共知识库或产品目录等场景至关重要。
从架构师的角度看,共享规则不仅是一个功能,更是一个需要精心设计的架构组件。它的设计直接影响到系统的性能、可维护性和未来的可扩展性。
原理说明
要真正掌握共享规则,我们必须深入其底层工作原理。当您创建一个共享规则时,Salesforce 并不是在每次用户访问记录时动态计算权限。相反,它会在一个被称为 Share Table (共享表) 的特殊数据库表中创建具体的共享记录。每个支持共享的标准对象和自定义对象都有一个对应的共享表,例如 `AccountShare`、`OpportunityShare`、`MyCustomObject__Share`。
一个共享表记录通常包含以下关键字段:
- ParentId: 指向被共享的记录 ID(例如,特定客户的 ID)。
- UserOrGroupId: 指向被授予访问权限的用户或组(Public Group、Role、Role and Subordinates 等)的 ID。
- AccessLevel: 定义授予的访问级别(例如,`Read` 或 `Edit`)。
- RowCause: 这是一个至关重要的字段,它解释了这条共享记录存在的原因。对于由共享规则创建的记录,其 `RowCause` 的值为 `Rule`。其他可能的值包括 `Owner` (记录所有者)、`Manual` (手动共享)、`ImplicitChild` (父子级联共享)等。理解 `RowCause` 对于调试“为什么这个用户能看到这条记录?”这类问题非常有帮助。
当共享规则被创建、修改或相关数据发生变化时,Salesforce 会启动一个异步的共享计算 (Sharing Recalculation) 过程。这个过程会评估所有符合条件的记录,并在共享表中创建或删除相应的行。触发重新计算的事件包括:
- 创建、编辑或删除共享规则。
- 更改记录的所有者。
- 用户的角色、区域或在公共组中的成员资格发生变化。
- 对于基于条件的共享规则,当记录中被用作条件的字段值发生变化时。
从架构层面来看,这个重新计算的过程是性能考量的核心。在一个拥有数千万条记录和数千名用户的组织中,一次大规模的共享计算可能会消耗大量系统资源,甚至导致记录锁定,影响用户操作。因此,设计高效、简洁的共享规则至关重要。
共享规则的类型
Salesforce 提供两种主要的共享规则类型:
- 基于所有者的共享规则 (Owner-based Sharing Rules): 这种规则基于记录的所有权。您可以将由特定公共组、角色或角色及下属的成员所拥有的记录,共享给另一个公共组、角色或角色及下属。这种方式简单直接,适用于基于团队或部门所有权进行共享的场景。
- 基于条件的共享规则 (Criteria-based Sharing Rules): 这是功能更强大、更灵活的一种。它不关心记录的所有者是谁,而是根据记录自身的字段值来决定是否共享。例如,共享所有“行业”为“金融”的客户记录。这种规则是实现复杂业务逻辑的关键。
作为架构师,我通常更倾向于使用基于条件的共享规则,因为它们与具体的用户或角色解耦,使得共享逻辑更加稳定和易于维护。当组织结构调整时,基于条件的规则通常不需要修改。
示例代码
虽然共享规则本身是声明式配置,但理解其背后的编程模型——即直接操作共享表——对于架构师和开发人员来说非常有价值。当标准共享规则无法满足极其复杂的动态共享需求时,我们就需要使用 Apex Managed Sharing (Apex 管理的共享)。Apex 代码可以直接在共享表中插入记录,`RowCause` 通常会指定为一个自定义的 `ApexSharingReason`。
以下示例代码来自 Salesforce 官方文档,展示了如何使用 Apex 编程方式共享一个 `Case` (个案) 记录。这与共享规则在后台所做的事情本质上是相同的,只是 `RowCause` 不同。
使用 Apex 共享个案记录
假设我们有一个自定义的共享逻辑:当个案升级时,需要将其共享给一个特定的专家用户 `expertUserId`。标准共享规则可能无法动态地找到这位专家,但 Apex 可以。
// 假设我们已经获取了需要共享的个案和目标专家用户的 ID
Id caseId = '500xx000000_some_case_id';
Id expertUserId = '005xx000000_some_user_id';
// 1. 创建 CaseShare 对象的一个实例
// CaseShare 是 Case 对象的共享表对应的 sObject
CaseShare caseShareRecord = new CaseShare();
// 2. 设置 ParentId,即需要被共享的记录的 ID
caseShareRecord.CaseId = caseId;
// 3. 设置 UserOrGroupId,即被授予访问权限的用户或组的 ID
caseShareRecord.UserOrGroupId = expertUserId;
// 4. 设置访问级别。可以是 'Read' 或 'Edit'
caseShareRecord.CaseAccessLevel = 'Edit';
// 5. 设置 RowCause。对于 Apex Managed Sharing,我们通常会创建一个自定义的 ApexSharingReason
// 这里为简单起见,我们使用 'Manual',表示这是一条手动共享记录。
// 一个真正的共享规则会在这里创建一个 'Rule' 类型的 RowCause。
caseShareRecord.RowCause = Schema.CaseShare.RowCause.Manual;
// 6. 插入共享记录
// 在 try-catch 块中执行 DML 操作是一个好习惯,以处理可能的错误
try {
Database.SaveResult saveResult = Database.insert(caseShareRecord, false); // 第二个参数为 false 表示允许部分成功
if (saveResult.isSuccess()) {
System.debug('成功为个案 ' + caseId + ' 添加了共享记录。');
} else {
// 处理插入失败的情况
for (Database.Error err : saveResult.getErrors()) {
System.debug('无法共享个案。错误: ' + err.getStatusCode() + ': ' + err.getMessage());
}
}
} catch (DmlException e) {
System.debug('插入共享记录时发生 DML 异常: ' + e.getMessage());
}
通过这个例子,我们可以清晰地看到共享规则在数据库层面的本质:它就是在 `CaseShare` 表中创建了一条记录,明确指定了“哪个记录”被以“何种权限”共享给了“哪个用户/组”,以及“为什么”(`RowCause`)。
注意事项
设计共享规则时,架构师必须仔细权衡功能需求与系统性能、可维护性之间的关系。以下是一些关键的注意事项:
- 性能与可扩展性:
- 共享计算风暴: 避免在业务高峰期进行大规模的用户、角色或共享规则变更,因为这会触发长时间的共享计算,可能影响整个组织的性能。对于大规模数据加载或所有者变更,建议暂时禁用相关的共享规则,在操作完成后再重新启用并计算。 - Data Skew (数据倾斜): 这是 Salesforce 性能的大敌。Ownership Skew (所有权倾斜) 指单个用户拥有超过 10,000 条记录;Account Data Skew (客户数据倾斜) 指单个客户下有超过 10,000 条关联记录(如联系人、商机、个案)。当这些倾斜的用户或记录参与共享计算时,会导致严重的性能瓶颈和记录锁定。在设计阶段就应识别并规避可能产生数据倾斜的场景。
- 规则的复杂性: 基于条件的共享规则中使用的字段应尽量简单。使用索引字段可以提高查询效率。避免使用复杂的公式字段或引用过多层级关系的字段,这些都会降低计算性能。
- 限制与治理:
- 数量限制: 每个对象最多可以有 300 个共享规则(包括基于条件和基于所有者等),其中最多 50 个可以是基于条件的。虽然这个限制很高,但接近这个限制通常意味着共享模型设计过于复杂,应该考虑重构或使用其他工具。
- 不可限制访问: 共享规则只能扩展访问权限,永远不能用于限制访问。它是在 OWD 和角色层级授予的权限之上“增加”权限。如果需要限制可见性,应考虑使用 Restriction Rules (限制规则)。
- 设计替代方案:
- 角色层级优先: 对于垂直的、基于汇报关系的共享需求,始终优先使用角色层级。它的性能是所有共享机制中最好的。
- Apex Managed Sharing: 当共享逻辑是动态的、基于算法的,或依赖于外部系统状态时,标准共享规则无能为力。此时应使用 Apex Managed Sharing,它提供了无与伦- 比的灵活性。
- Territory Management (区域管理): 对于复杂的、基于地理位置或其他维度的客户分配和共享模型,尤其是围绕客户和商机,企业版区域管理功能提供了比共享规则更强大、更结构化的解决方案。
- Restriction Rules & Scoping Rules: 这些是较新的安全工具。限制规则可以对用户已有的访问权限进行过滤,创建真正的“记录不可见”场景。范围规则可以根据用户属性和记录属性筛选默认可见的记录列表,提升用户体验。在某些场景下,它们可以替代或补充共享规则。
总结与最佳实践
共享规则是 Salesforce 数据安全模型中一把强大而精密的瑞士军刀。作为架构师,我们的目标是精准地使用它,而不是滥用它。一个设计良好的共享策略可以极大地促进业务协作,而一个糟糕的设计则会成为性能的噩梦。
以下是我的核心最佳实践建议:
- 坚持最小权限原则: 将 OWD 设置为业务允许的最严格级别(通常是“私有”),然后仅在必要时通过共享规则、角色层级等方式逐层放开权限。
- 保持简洁: 共享规则的数量越少越好。优先使用公共组来整合相似的访问需求,而不是为每个角色创建单独的规则。定期审查和清理不再需要的规则。
- 拥抱基于条件: 尽可能使用基于条件的共享规则,因为它们更具韧性,不受组织结构频繁变动的影响。
- 前瞻性设计: 在项目初期就与业务方深入沟通,理解数据量、用户增长和未来的协作模式。主动识别潜在的数据倾斜风险,并设计相应的解决方案(例如,使用队列或通用的集成用户作为记录所有者)。
- 监控与审计: 定期使用“设置审计跟踪”来监控对共享规则的变更。对于关键对象,关注共享表的增长情况,并留意是否有长时间运行的共享计算任务。
- 工具箱思维: 不要只依赖共享规则。熟练掌握角色层级、Apex 共享、区域管理、限制规则等所有可用的工具,并根据具体场景选择最合适、性能最优的组合。
最终,一个优秀的 Salesforce 架构师所设计的共享模型,应该像一座精心规划的城市交通系统:既能保证主干道(角色层级)的畅通无阻,又能通过立交桥和匝道(共享规则)高效地疏导特定车流,最终实现整个系统的安全、高效和有序运行。
评论
发表评论