MySQL stress testing and load testing: query latency and the connection limit
The clearest way to see how many concurrent queries a MySQL or MariaDB server serves at what latency, and where it chokes, is to send the application's queries straight to the database at a growing load. A load test measures latency at the expected traffic; a stress test raises the load step by step to find the limit. The general method is in the database load testing guide; this page covers the MySQL-specific part.
The MySQL connection
| Default port | 3306 |
|---|---|
| Placeholder (parameter) | ? |
| Database / service | the database name |
| Driver parameter for TLS | — |
| Reads in a read-only transaction | Yes |
- Click Connections → Add connection, pick MySQL / MariaDB as the Type and give it a name (e.g.
orders-db). - Enter Host, Port (empty means 3306), Database / service, Username and Password. If you can, use a separate user with
SELECTonly on the tables you need; the password is stored encrypted and the test names only the connection. - Max open connections (per runner): 50 by default. Total connections ≈ number of runners × this value; set it so the database's connection limit is not exceeded.
- Save, then Test. If the database can only be reached through a bastion, add an SSH tunnel to the connection.
A SQL step for MySQL
Write the query in Query as fixed text; give the values with the ? placeholder under Parameters. Parameters can contain {{__VU}}, {{$randInt 1 100000}}, extracted variables or values from a CSV data file; templates cannot go inside the query text, so there is no SQL injection. In the Checks tab add e.g. Row count = 1, run one iteration with Try it to see the rows returned, then Save.
{"id": "my", "name": "MySQL", "protocol": "sql", "connection": "mysql-db",
"sql": {"query": "SELECT * FROM customers WHERE email = ?", "params": ["user{{__VU}}@example.com"]}}A read's duration covers the wait for a pooled connection, the query and fetching every row. Rows go to checks and extraction as an array of JSON objects (such as $[0].status). Errors are told apart by kind: timeout, refused connection, permission, missing table, constraint violation, syntax and read-only violation.
Protecting the data
Spitfire works out whether a query reads or writes and counts anything it cannot recognise as a read as a write. Writes cannot be saved without an explicit approval on the step, and once approved the test asks for confirmation again every time it starts, recorded in the audit log (security statement).
On MySQL and MariaDB every query counted as a read also runs inside a read-only transaction that is rolled back. The database itself refuses a query that looks like a read but changes data; the run shows Cannot execute statement in a READ ONLY transaction.
Which queries to load?
MySQL's performance schema tells which queries to load: it keeps query patterns (digests) with their call counts and total times. The few queries called most often and taking the most time in total are the test's skeleton.
You don't have to carry these statistics over by hand: the digest statistics are imported and the most-called queries become one SQL step weighted by their call counts; installation admins can also read the statistics straight from the connection (query mix).
Instead of writing the queries by hand, you can have them suggested from the database's own schema (installation admins; query suggestions from the schema). The privileges needed on MySQL: SELECT, SHOW VIEW on the database; optionally PROCESS and performance_schema access for statistics. Every suggestion is checked with EXPLAIN (never ANALYZE): only the plan is fetched, the query never runs; every suggestion has a row limit (LIMIT).
Then pick the load model: a steady load at the expected concurrency, or a stepped breaking point test to find the limit. Read the results with p95 and p99, not the average.
Common errors
Access denied for user …: the username or password is wrong, or the user has no access to that database. Type the password again and save; check Database / service.too many connections: the number of runners × Max open connections (per runner) exceeds the database's limit (max_connections); lower the value or raise the limit.connection refusedor a timeout on Test: wrong host/port, the database is only reachable from an internal network, or a firewall blocks it. Allow the controller and runner IPs; behind a bastion use an SSH tunnel.
Frequently asked questions
What is the difference between a MySQL stress test and a load test?Tests
Is MariaDB supported too?Tests
Does the load test change data on MySQL?Tests
Spitfire installs on Docker or Kubernetes with one command; every testing feature and protocol is open in the free edition.