Usually you do not need to. A load test needs realistic volume, shape and skew, not real customers. Generate synthetic records at the target size, copy only statistical properties such as row counts and value distributions, and keep real personal data out of the load test environment entirely.
What a load test really needs
Performance depends on a few data properties: how many rows each table holds, how large the values are, how evenly values spread, and how related rows cluster. Query plans change with those properties. None of that requires a real name or email.
The shape questions to answer:
- How many rows per table at your target scale?
- How skewed are the keys? One customer with half the orders behaves differently from an even spread.
- How large are text and file fields?
- How many related rows hang off a typical parent?
Why not just copy production
A production copy carries every risk discussed in the related posts on staging data: more people with access, weaker monitoring, forgotten snapshots, and test output that captures real values. Load tests make this worse. They hammer systems, produce large logs and traces, and often run on shared tooling with broad access.
Data protection principles ask for data limited to what is necessary for the purpose, and that is hard to argue for a test that only needs row counts. This is general information, not legal advice.
Steps to build a realistic synthetic dataset
- Measure production, safely. Run aggregate queries that return counts, percentiles and distinct counts. Take only the numbers, never the rows. Have the query reviewed so it cannot return values.
- Write the profile. Record the measurements in a small configuration file: rows per table, ratio of children to parents, key skew, average and maximum field lengths.
- Generate at scale. Use a seeded generator to build the dataset from the profile. Generate in batches and load with bulk methods, because row by row inserts at millions of rows are slow.
- Create skew deliberately. Make a few hot accounts with very large histories, and a long tail of small ones, matching what you measured.
- Refresh indexes and statistics. Run the database's statistics update after loading, so query plans reflect the new data.
- Check plan similarity. Compare execution plans of your ten slowest production queries against the synthetic environment. If they differ, adjust the skew.
- Delete afterwards. Tear the environment down on a schedule, with the dataset kept only as a generator configuration.
When masked data is considered
Sometimes plans depend on subtle properties that synthetic data misses. In that case a masked copy may be justified, with approval, in a locked down environment, with a stated end date and deletion evidence. Make it the exception, and write the reason down. See the related post on keeping joins working when masking test data.
Mistakes to avoid
- Testing with a small dataset and being surprised by production behaviour.
- Generating uniform data, which hides hot spot problems.
- Capturing production traffic and replaying it, which replays real identifiers and tokens. If you do replay traffic, scrub it first.
- Leaving a large load test database running over a weekend.
Quick check
Is replaying production traffic safe?
Only after scrubbing identifiers, tokens and payloads. Raw captured traffic is real personal data plus live credentials.
How do I make synthetic data realistic enough?
Match the measured distributions, not the values. Compare query plans, and tune until the plans agree.
Sources
Last reviewed: 19 October 2026.