所有权、父记录和查找倾斜
另有 19 种语言版本
English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · ไทย · Українська · Español (México) · Français (Canada) · Deutsch (Schweiz)

第 2 个模块,共 4 个,属于 Data Architect 课程第 7 章为规模而设计。每个模块都可以单独阅读。 上一篇:当数据量改变一切 · 下一篇:当数据量遇上共享模型
**本模块回答的问题:**当太多记录指向同一个东西时,会发生什么?
大数据量问题并不总是记录太多。有时是太多记录连到了同一个东西上:一个所有人、一个客户、一个父记录、一个查找值。想想交通。一座城市可以有几百万辆车,照样运转。麻烦始于每个人都在下午 5 点走同一个高速出口。总量重要,集中程度也重要。我把这种集中叫作热点;Salesforce 叫它数据倾斜。倾斜有好几种,背后是同一个想法:太多活动汇聚到模型中的一个点上。
The easiest to picture. One account with 100 contacts: nothing unusual. A thousand: probably fine. Ten thousand: now I’m paying attention. My design rule is no more than about 10,000 children under one parent, and Salesforce’s guidance draws the same line: “a large number of child records (10,000 or more) with a single parent account” is a common cause of poor performance (Parent-Child Data Skew (在新标签页中打开)). Salesforce’s own example shows why. Put 300,000 contacts under one account. A user who can see one of those contacts gets implicit access to the account. When that user loses access to the contact, Salesforce has to check the other 299,999 contacts before it can decide whether to remove the user’s access to the account. Do that for many users at once, and it gets expensive. Salesforce also names one way to take part of that cost away: where your security requirements allow, set the child object to Controlled by Parent, so there are no parent implicit shares to maintain (same source). In practice, it’s the most common skew I see, and I’ve seen users notice visible lag at around 20,000 children under one parent. Where somebody feels it depends on what the system is doing. That’s why 10,000 is a design threshold, not a promise that record 10,001 breaks anything. The real question is: why are all these records pointing at the same parent? ⟲
Step 1 of 2: every line points at one account. That’s the hot spot. ⟲
“未分配”客户与客户池
Section titled ““未分配”客户与客户池 ”The classic case is contacts with no real account. Somebody decides every contact needs an account, so they create Unassigned Contacts and put every contact without an account there. Problem solved, until there are 300,000 contacts under it. We created an artificial hot spot because the model required us to put everything somewhere, and the relationship doesn’t even carry business meaning. It worked beautifully at 500 records. Then the Number of Zeros caught up with it. ⟲
What do you do when you’ve got a million contacts under it? I’ve seen child accounts by state, one unhandled-contacts account per state. I’ve seen an automatic version: whenever an account reaches 10,000, create another one. It can be a little awkward, but it spreads the load. ⟲
One answer is an account pool: Unassigned Contacts 001, 002, 003 and so on, with the children spread across them, each kept under the threshold. It works, and it’s Salesforce’s own advice: “Distribute child records so that no parent has more than 10,000 child records,” and for contacts without real accounts, “set up several dummy accounts and distribute the contacts among them” (Best Practices for Deployments with Large Data Volumes: General (在新标签页中打开), Winter ‘27). But those aren’t real accounts; they’re architecture artifacts. Users will ask who “Unassigned Contacts 017” is and why it’s in their search results. So when I use a pool, I hide the placeholder parents from normal business experiences as much as I can. It’s a recurring trade-off: sometimes scale forces a structure that exists for the platform, not the business. When it does, don’t pretend it’s business data. Name it, document it, hide it where appropriate. ⟲
Step 2 of 2: the same contacts, spread across a pool. Nothing moved but where they point. ⟲
Master-Detail at Volume
Section titled “Master-Detail at Volume”Contacts under an account are a lookup. Master-detail is a different decision, and it’s one you make in the data model, long before the records arrive. Here’s the question I ask in class: would an invoice ever have more than 10,000 line items? No, it never would. So an invoice and its lines can be master-detail all day long. Now ask the same question of every master-detail relationship you’re about to build: at the volume we expect, will one parent ever pass 10,000 children? ⟲
Salesforce draws the same line: “As a best practice, don’t exceed 10,000 child records for a master-detail relationship” (对象关系的注意事项 (在新标签页中打开)). Master-detail is the strongest relationship Salesforce gives you, and that’s exactly why the number matters. The detail record has no owner of its own; its access is controlled by the parent. Delete the parent, and every child goes with it. A child can’t move to another parent unless you allowed reparenting. Roll-up summaries on the parent have to keep up as the children change. And saving a child can lock the parent, which is why Salesforce’s loading advice is to group child records by their parent “to minimize locking conflicts” (Loading Data from the API (在新标签页中打开)). With ten children, none of that is noticeable. With a hundred thousand, every one of them is. ⟲
It’s also hard to undo. A master-detail relationship can only be converted to a lookup when the parent has no roll-up summary fields, and converting it changes the child’s sharing to Public Read/Write (same source). By the time volume tells you it was the wrong choice, the relationship may be holding up reports, sharing and automation. Count the children before you choose the relationship. ⟲
Steve Trucking has one of these decisions in its model. Each Retail Partner sends a Manifest (Manifest__c, a custom object) for each distribution center, for each working day. Manifest is custom, so master-detail to the partner’s account is allowed, and it’s tempting: the manifest would inherit the partner’s sharing, which is exactly the isolation we want. Now count. Our largest partner has nine or more DCs: nine manifests a working day, about 2,700 a year. Past 10,000 in under four years, under one parent, and every manifest revised on the day, so every revision would touch the partner’s account. That’s why the model makes Manifest a lookup and handles partner access through sharing. The invoice stays small; the manifests keep coming. ⟲
Step 1 of 2: master-detail, where the parent stays small. ⟲
Step 2 of 2: a lookup, where the children keep coming. ⟲
Watch Account with its contacts and opportunities too. It isn’t master-detail, but it behaves like a pseudo master-detail, with implicit sharing to the parent, which is why the 10,000 guidance shows up there as well. None of this means never use master-detail at scale. It means don’t choose a strongly coupled relationship and then act surprised that the records are strongly coupled. ⟲
Test knowledge. Know the 10,000-children guidance for master-detail, and what master-detail brings with it at volume: no owner on the detail (access controlled by the parent), cascade delete, no reparenting unless allowed, roll-up summaries, and parent locking when children are saved. Converting to a lookup needs no roll-up summaries on the master. ⟲
所有权倾斜
Section titled “所有权倾斜 ”Now the owner. I ask the room: who owns a million records? The admin? Is the admin sitting there creating a million records? Usually it’s the integration user or the data load user. The records come in, and nobody knows who to give them to. We create an integration user, it creates records, nobody thinks about ownership (“it’s just the integration user”), and a year later that user owns 2 million records. Salesforce calls it ownership data skew when one user owns more than 10,000 records of an object, and the classic example is all the unassigned leads parked on a dummy user (Ownership Data Skew (在新标签页中打开)). That doesn’t mean a user can never own more. It means understanding the consequences: ownership isn’t a Created By stamp. It drives the role hierarchy, sharing rules and recalculation: behind the scenes, Salesforce keeps share records for the owner’s records above them in the role hierarchy. Attach millions of records to one owner who sits deep in the sharing model, and changing anything about that owner gets expensive. ⟲
The guidance matches what I teach in class. If a few users must own a very large number of records, give them no role; if they need one, put them in a separate role at the top of the hierarchy and don’t move them, and keep them out of public groups used as sources for sharing rules (same source). One catch with the top role: that user inherits access to all the data owned by or shared with the users below them. You could spread the records across a bunch of users instead, but for an integration user or a data load user, that typically isn’t worth the trouble. ⟲
We’re isolating the hot spot from the structures that trigger recalculation. Imagine a user who owns 5 million records being moved in a reorganization. That isn’t moving a box on the org chart; Salesforce may have to re-evaluate access across 5 million records. At this scale, organizational changes become data operations. ⟲
Test knowledge. Know the skew thresholds and the fixes: ownership skew when one user owns more than 10,000 records of an object (no role, or a separate role at the top that doesn’t move, and out of public groups used by sharing rules); parent-child skew at about 10,000 children under one parent. ⟲
机器创建的记录应该归谁所有?
Section titled “机器创建的记录应该归谁所有? ”谁创建了一条记录和谁拥有它,不是同一个问题。Redrock 通过 API 发来 500,000 次配送,由集成用户创建。它应该永远拥有这些记录吗?也许,但不只是因为是它创建的。现在智能体也会创建记录。
我的立场:取决于所有权对共享是否重要。集成用户或智能体用户是合法用户,可以拥有记录。如果在这个对象里所有权驱动访问(通过角色层级或共享规则),就把记录分配给业务要求的所有人:一个真实用户、一个队列(在对象支持队列的情况下),或一个专门的所有人用户。如果所有权不驱动访问,让智能体或集成用户继续当所有人可能是更好的选择,因为审计线索会直接指回创建记录的东西。无论哪种,都要决定对这个对象而言所有人意味着什么,而不是因为人通常拥有记录,就机械地重新分配每一条机器创建的记录。还要记住“创建人”字段一直都在:重新分配所有权不会丢失“是集成创建的”这一事实。创建人和所有人回答的是不同的问题。让一个字段同时回答两个问题,就会得到一个糟糕的模型。
A lookup can be a hot spot too: millions of records pointing at the same location, category, reference record or configuration record. Salesforce’s own training defines it: “Lookup skew happens when a very large number of records are associated with a single record in the lookup object” (Large Data Volumes: Design Your Data Model (在新标签页中打开)). The danger isn’t “lots of lookups.” It’s concentration combined with activity: if millions of active records reference one record, and transactions keep touching that relationship, operations start colliding over it. That’s record locking, which chapter 11 covers. For now, recognize the shape: many active records, one heavily referenced record, worth inspecting. ⟲
并非每种倾斜都同样危险
Section titled “并非每种倾斜都同样危险 ”不要死记“10,000 = 坏”。五万条历史记录指向一个永不改变的参考值,不承担共享角色,几乎没有并发活动,可能完全没问题。两万条活动记录挂在一个经常变化、驱动共享、给汇总字段提供数据、同时被几个集成访问的父记录下,就是完全不同的故事了。数据量有形状,倾斜也有。有多少记录汇聚在那里?它们多久变一次?父记录会变吗?这个关系驱动共享或锁定吗?集成会并发更新子记录吗?移动所有人或父记录会触发重新计算吗?数字告诉我该看哪里;行为告诉我该有多担心。
Steve Trucking:热点在哪里?
Section titled “Steve Trucking:热点在哪里? ”按五年算,我们预计大约有 **900 万个工作订单。**假设有人说:“让集成用户拥有每一次配送吧。”900 万条记录,一个所有人。所有权对配送的共享重要吗?如果不重要,一个专门的大数据量所有人可以行得通,像上面说的那样与角色层级和共享规则组隔离。如果重要,一个集成所有人就是错误的业务模型:集成创建了记录,不等于它应该拥有记录。
Then the Retail Partner. Every work order carries its partner, deliberately, because partner isolation matters. But if one partner is 40% of the business, five years of deliveries is about 3.6 million work orders under one partner account. Does that make the relationship wrong? No. The business relationship is real, and I’m not going to invent 400 fake Redrock accounts because I don’t like the count. But I am going to look for a split the business would recognize. Each partner has real distribution centers, so a child account per DC under the partner spreads the work orders nine or more ways while every account still means something. Add archiving completed deliveries after the claims window, and the active children per parent stay low. ⟲
这就是两种修复方式的区别。“Unassigned Contacts”本来就是人为的,所以把它拆成一个池子不会丢失任何含义。Redrock 是一个真实的合作伙伴;把它拆成 Redrock 001、002 和 003 会破坏模型。按它真实的配送中心拆分则不会。**规模上的修复仍然必须保留含义。**有时答案是换一种关系、共享策略、查询模式、存储模型或归档策略,但绝不是给一个真实的业务实体编造一个假的身份。
Inspect, not bad. Concentration matters, not the total. ⟲
在图上找出热点
Section titled “在图上找出热点 ”这是我喜欢的一个练习。拿出数据模型,看箭头,而不只是看对象。对每个关系问:**箭头这一侧能堆积多少条记录?**然后对所有人问同样的问题。再想象三年、五年、十年以后。记录在哪里集中?明天的问题往往就藏在那里。
**考试之外:AI 改变了什么。**一个人一天也许创建 20 条记录;一个智能体可能创建几千条。给智能体一个自己的用户,让每条记录都归它所有,所有权倾斜出现的速度会比以往人工操作时快得多。智能体拥有记录并没有错,但要在数据量让默认做法变成架构的一部分之前,决定智能体创建的记录的所有人意味着什么。
架构师在这里决定什么
Section titled “架构师在这里决定什么 ”- 父子、所有权或查找关系在哪里让记录集中,以及它们是否活跃到足以成为热点。
- 机器创建的记录是继续归集成用户或智能体用户所有,还是因为所有权驱动访问而重新分配。
- 拥有大量记录的所有人如何与角色层级和共享规则组隔离。
- 什么时候可以把人为的父记录做成池子,什么时候真实的关系需要一个有含义的拆分。
- Whether each master-detail relationship stays under about 10,000 children per parent at projected volume, or should be a lookup. ⟲
分散在几千个所有人和父记录下的一百万条记录,和指向同一个地方的一百万条记录,表现截然不同。尽早寻找热点:所有人、父记录、查找关系,任何“零的个数”汇聚的地方。目标不是消灭每一个大的关系;有些关系正确地代表了业务。而是在开通高速公路之前知道车流去哪里,因为一旦几百万条记录都奔向同一个出口,改路就难得多了。
- Salesforce Developers, Best Practices for Deployments with Large Data Volumes, Loading Data from the API (在新标签页中打开) (Winter ‘27): group child records by parent to minimize locking conflicts, checked 2026-11-01. ⟲
- Salesforce Help, 对象关系的注意事项 (在新标签页中打开): no more than 10,000 child records for a master-detail relationship; converting master-detail to lookup, checked 2026-11-01. ⟲
- Salesforce Developers, Designing Record Access for Enterprise Scale (Winter ‘27): Ownership Data Skew (在新标签页中打开) and Parent-Child Data Skew (在新标签页中打开), checked 2026-11-01. ⟲
- Salesforce Developers, Best Practices for Deployments with Large Data Volumes: General (在新标签页中打开) (Winter ‘27), the 10,000-record owner and parent guidance, checked 2026-11-01. ⟲
- Trailhead, Large Data Volumes: Design Your Data Model (在新标签页中打开): parent-child, ownership and lookup skew, checked 2026-11-01. ⟲
- Salesforce engineering blog, Managing Lookup Skew to Avoid Record Lock Exceptions (在新标签页中打开) (2013; older, but still the clearest explanation; checked 2026-11-01). ⟲
- SteveTechArc, Salesforce Documents by Release: these guides, once a year from Winter ‘16, each with an archived copy. ⟲
