This question comes up a lot
Especially during Office Hours, and the answer is usually… not great. You can spend a lot of money on people and complicated software to design, run, and monitor workloads against test environments, or you can throw together tests with some free tools like SQL Query Stress or Microsoft’s RML Utilities.
RML Utilities is pretty cool, and includes ostress.exe along with some other components. What I really like about ostress is that it’s all CLI, so you can call it from Agent Jobs, and feed it all sorts of cool options. When you create the Agent Job, you just use the Operating system (CmdExec) type of step instead of T-SQL, or whatever else.
For the command, you’ll want to use something like this:
"C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" --Path to ostress executable
-SNADAULTRA\SQL2016C --Server name (note that this is how you access a named instance)
-d"StackOverflow" --Database name
-n10 --How many simultaneous sessions you want to run your query
-r5 --How many iterations they should each perform
-q --Quiet mode; doesn't return rows
-Q"EXEC dbo.GenerateReads" --Query you want to run
-o"C:\temp\crap2" --Logging folder
There are a number of other flags available in the documentation, but these are the ones I usually run with. If I wanted to feed ostress a .sql file of commands to use instead of a stored procedure or ad hoc query, I could use
-i"C:\temp\BeatUpStack.sql" instead. It’s particularly important to specify different logging folders if you’re going to run ostress concurrently. Otherwise, every session will try to use the default, and you’ll get a bunch of access denied errors when it attempts to clean up logging folders at the start of each run.
For scheduling, I usually set the job(s) to run every 10 seconds, so there’s as little gap between runs as possible. It’s fine if they run over 10 seconds, I just don’t want the jobs sitting around for minutes doing nothing before they start up again.
The end result, depending on complexity, can look something like this. I have three jobs set up that do different things. One generates reads, one generates writes, and the other generates some TempDB load. The curious thing about TempDB load is that if you don’t explicitly drop temp tables after each run, you’ll get flooded with “object exists” type errors. Your command will have to look something like this:
Q"SELECT TOP (5000) * INTO #t FROM dbo.Orders; DROP TABLE #t;".
When jobs start running, you should see it immediately. Here’s what sp_BlitzWho returns with my workload in full swing. It’s ugly. There’s blocking, there’s heinously expensive queries, and this poor server silently weeping.
You can really do a lot to customize the simulation to suit your needs. It might not be perfect, but it’s a lot cheaper than perfect.
Thanks for reading!