Managing Large Data Volumes in Salesforce: Best Practices for Admins and Developers

As organizations grow, so does the volume of their Salesforce data. What starts as a manageable CRM setup can quickly become overwhelming: slower pages, higher storage costs, and more risk of hitting Salesforce’s governor limits.

For both admins and developers, handling large data volumes isn’t just about efficiency. It’s about keeping your org scalable, reliable, and responsive. This guide covers the practices that matter most, from indexes and selective queries to data skew, bulk loading and archiving.

What Counts as a Large Data Volume?

There is no fixed number. Salesforce’s own guidance talks about orgs with tens of millions of records, tens of thousands of users or hundreds of gigabytes of storage. In practice you will feel it earlier, usually once a single object passes a few million records or one parent record collects more than 10,000 children.

The usual warning signs:

  • Reports and list views time out or take a long time to load
  • Triggers fail with a non-selective query error
  • Data loads fail with UNABLE_TO_LOCK_ROW
  • Sharing recalculation after a role or owner change runs for hours
  • Batch jobs that used to finish overnight no longer do

Why Large Data Management Matters in Salesforce

Salesforce is built to scale, but even robust systems strain when data volume goes unchecked. A good data strategy improves speed and also gives you cleaner reporting, tighter automation, and better business decisions.

  • Optimizes performance: faster queries and a smoother user experience.
  • Reduces governor limit risk: less chance of hitting platform limits.
  • Improves reporting and analytics: it is easier to get answers from organized data.

Data Architecture and Planning

Everything starts with structure. Thoughtful data architecture avoids problems later.

  • Define essential fields: keep only what matters for business logic and reporting.
  • Choose relationships on purpose: master-detail gives you roll-up summaries and inherited sharing, but every child save locks the parent record. On very large child objects, prefer a lookup unless you need those features.
  • Plan the data lifecycle: decide up front when records get archived or deleted.

Example: if you’re managing hundreds of thousands of support cases, keep open and recent cases active and archive the resolved ones.

Indexing for Faster Retrieval

Indexes are like shortcuts for your queries. Without one, the database reads every row of the object to find the matches.

Salesforce indexes these fields for you:

  • Id, Name and OwnerId
  • CreatedDate and SystemModstamp
  • RecordTypeId
  • Lookup and master-detail fields
  • Custom fields marked as External ID or Unique

For other custom fields that you filter on often, you can ask Salesforce Support for a custom index.

Example: if your users frequently search by Region or Product Type, indexing those fields can cut query times significantly.

An Index Only Helps If the Filter Is Selective

Having an index is not enough. The query optimizer uses it only when the filter returns a small enough share of the object.

Index typeThe filter is selective when it returns
Standard indexLess than 30% of the first million records plus 15% of the rest, up to 1 million rows
Custom indexLess than 10% of the first million records plus 5% of the rest, up to 333,333 rows

Worked example: an object has 2 million records and a custom index on Region__c. The threshold is 10% of the first million (100,000) plus 5% of the second million (50,000), so 150,000 rows. A region with 120,000 records uses the index. A region with 400,000 does not, and the query scans the whole object.

What Stops an Index from Being Used

  • Negative filters such as !=, NOT IN and NOT LIKE
  • A leading wildcard, as in LIKE '%acme'
  • Comparing to null, unless Support has built the index to include nulls
  • Filtering on a formula field that can’t be indexed, such as one that points to another object or uses TODAY()
  • An OR where one side is not indexed

Check with the Query Plan Tool

In the Developer Console, open Help, then Preferences, and tick Enable Query Plan. The Query Editor now shows a Query Plan button. Run it on your query and read two columns: a Cost below 1 means the filter is selective, and Leading Operation Type tells you whether Salesforce will use an index or a full table scan.

Efficient Use of SOQL

Optimizing queries is key to performance when working with big data.

  • Specify fields: name the fields you use. SOQL has no SELECT *, and its nearest equivalent, FIELDS(ALL), is capped at 200 rows.
  • Use WHERE clauses: retrieve only the data you need, filtered on indexed fields.
  • Add LIMIT: cap large result sets unless a full export is required.
SELECT Name, Industry FROM Account WHERE Industry = 'Technology' LIMIT 100

This returns at most 100 records. Remember that LIMIT caps what comes back. It does not make a non-selective filter selective, so the WHERE clause still has to do the real work.

In a trigger, a non-selective query on an object with more than 200,000 records fails outright:

System.QueryException: Non-selective query against large object type (more than 200000 rows)

The fix is always the same: filter on an indexed field, or get one indexed. My SOQL and SOSL guide covers the query syntax in more depth.

Skinny Tables and Custom Indexes

Two tools are available through Salesforce Support when standard tuning isn’t enough.

  • Custom indexes on custom fields, including two-column indexes for queries that always filter on the same pair of fields.
  • Skinny tables, a narrow copy of an object holding only the fields you query most, kept in sync by Salesforce. Reports, list views and SOQL that use only those fields read the smaller table.

Skinny tables have limits worth knowing before you ask: up to 100 columns, no fields from other objects, and they are copied only to Full sandboxes.

Optimizing Data Load and Processing

For operations involving thousands of records, use tools like Batch Apex and Data Loader.

  • Batch Apex: processes data in smaller chunks (200 records at a time by default).
  • Data Loader: ideal for bulk updates or inserts without user interface delays. Switch on the Bulk API setting for large files.

Example: updating 15,000 records using Batch Apex keeps the system stable without hitting governor limits.

Loading Millions of Records

  • Use the Bulk API. It is built for this and processes batches in parallel.
  • Load parents before children, and use External ID fields with upsert to link them.
  • Sort child records by parent ID. Batches that touch the same parent at the same time cause UNABLE_TO_LOCK_ROW errors.
  • If lock errors continue, use smaller batches or serial mode for that object.
  • Bypass triggers, flows and validation rules that the load does not need, using a custom permission or custom setting for the integration user.
  • For very large loads or owner changes, ask Support about deferring sharing calculations until the load is done.
  • For extracting data, turn on PK Chunking so the Bulk API splits the query into ID ranges (100,000 records per chunk by default, 250,000 at most).

A Batch Apex Skeleton

public class ArchiveClosedCasesBatch implements Database.Batchable<SObject> {

    public Database.QueryLocator start(Database.BatchableContext context) {
        return Database.getQueryLocator(
            'SELECT Id FROM Case WHERE IsClosed = true AND ClosedDate < LAST_N_YEARS:2'
        );
    }

    public void execute(Database.BatchableContext context, List<Case> scope) {
        // copy the records to your archive store first, then remove them
        delete scope;
    }

    public void finish(Database.BatchableContext context) {}
}

// Run it 500 records at a time
Database.executeBatch(new ArchiveClosedCasesBatch(), 500);

A batch QueryLocator can cover up to 50 million records. Each chunk runs in its own transaction with fresh governor limits. The chunk size is 200 by default and 2,000 at most.

Archiving and Deletion Strategy

Outdated or unused records should be offloaded on a schedule. Use Big Objects for data you rarely access but can’t delete.

Example: archive closed cases older than 2 years to a Big Object while keeping recent cases active and searchable.

Three details matter here:

  • Big Objects can only be queried on their index fields, in the order the index defines them, and the index can’t be changed after you create it. Design it around the questions you will ask later.
  • The Recycle Bin still counts. Deleted records stay for 15 days and still slow queries down. For big clean-ups, use the hard delete option of the Bulk API, which needs the Bulk API Hard Delete permission.
  • Keep a way back. Before deleting, export the records or copy them to your archive, and test a restore.

Managing Data Skew

Skew occurs when a single parent or owner is linked to too many records. The working threshold is 10,000.

TypeWhat it isWhat goes wrongWhat to do
Account data skewOne account with more than 10,000 contacts, cases or opportunitiesLock errors when children update at once, slow sharing recalculationSplit the records across several accounts, for example by region
Ownership skewOne user or queue owns more than 10,000 records of an objectLong sharing recalculations when that user changes roleSpread ownership. If an integration user must own them, keep that user out of the role hierarchy
Lookup skewMore than 10,000 records point to the same lookup recordLock contention on that record during loads and updatesSpread records across more lookup values, or use a picklist if the value never needs its own record

Example: distribute ownership of 20,000 accounts across multiple users instead of assigning them all to one service rep.

Asynchronous Processing for Heavy Tasks

For resource-intensive logic, move the work out of the user’s transaction:

  • Future methods: good for lightweight, delayed tasks.
  • Queueable Apex: better for chaining jobs or passing complex data.
  • Scheduled jobs: useful for off-hours processing like nightly clean-ups.

Example: use Queueable Apex to send follow-up emails to 10,000 leads after a campaign instead of processing in real time. More on choosing between these in Apex best practices.

Streamlining Reports and Dashboards

  • Use filters: focus reports on relevant data, and filter on indexed fields such as dates, owner and record type.
  • Limit historical snapshots: keep only what’s useful.
  • Set refresh intervals: avoid constant dashboard refreshes on large datasets.

Example: filter opportunity reports by the last 3 months instead of pulling all historical data every time.

Externalizing Data

For massive historical datasets or infrequently used records:

  • Use External Objects: link external systems through Salesforce Connect.
  • Use external databases: store raw data outside Salesforce and reference it when needed.

Example: store 10+ years of archived sales data in AWS or Azure, and expose it in Salesforce using external objects.

Maintenance and Best Practices

  • Audit regularly: review and clean up unused fields, records, and automations.
  • Keep sharing simple: fewer sharing rules and a shallower role hierarchy mean faster access calculations.
  • Bulkify your code: always use bulk-safe logic in triggers and Apex.
  • Use health check tools: regularly scan for security and performance issues.
  • Stay updated: new Salesforce releases often bring performance and storage features.

Large Data Volume Checklist

  • Every query on a large object filters on an indexed, selective field
  • No single account, owner or lookup record has more than 10,000 children
  • Loads use the Bulk API, parents first, children sorted by parent ID
  • An archive and delete schedule exists and runs
  • Heavy processing runs in Batch or Queueable Apex, not in the user’s transaction
  • Reports filter on date ranges and indexed fields

FAQs

How many records is a large data volume in Salesforce?

There is no official number. Plan for it once an object is heading past a few million records, or sooner if you see slow reports, lock errors or non-selective query errors.

Does adding LIMIT make a slow query fast?

Not by itself. LIMIT reduces what is returned. If the filter is not selective, and especially if there is an ORDER BY, Salesforce still has to scan and sort the matching rows.

Are custom fields indexed?

Only lookup and master-detail fields, and fields marked External ID or Unique. Other custom fields need a custom index from Salesforce Support.

What is data skew?

Too many records tied to one parent, one owner or one lookup value, usually more than 10,000. It causes record locking and slow sharing calculations.

What is a skinny table?

A smaller copy of an object with only your most-used fields, created and kept in sync by Salesforce Support to speed up reports and queries.

Conclusion

Managing large data volumes in Salesforce isn’t about quick fixes. It’s about building a system that scales. From the way you query records to how you archive historical data, each choice affects the performance and reliability of your org. By combining good architecture, smart indexing, asynchronous processing, and regular housekeeping, you’ll be equipped to handle millions of records without slowing down.

Want to go further with this?

Learn it step by step in a course, or talk it through on a 1:1 call.

Shopping Cart