PostgreSQL 16–18
Available20/20 plans, 20/20 costs within 10%
Hypothetical indexes via HypoPG on the twin only. Nothing is installed on production.
The twin is an empty copy of your schema loaded with production's statistics. QueryInsights tries index changes on it and asks your database's own optimizer what every top query would cost.
Swipe to see the whole diagram →
QueryInsights reads the schema with constraints, table and index sizes, column statistics and planner settings from production. It never writes there and never copies a row.
An empty database on a server QueryInsights runs, at production's major version, receives those statistics. Table files are sized to production's page counts without using disk, so the planner sees a database as large as yours.
Your top patterns by total time and by calls are planned on the twin, weighted by how often each one actually runs. That is the baseline.
Candidate indexes come from what the planner does with each query: its filters, join keys, and sort and group keys. A search adds, merges and prunes them, and an AI agent can experiment through tools that only the twin answers.
A change set is proven only if total cost falls, no query's plan gets more expensive, and every new index is used. Anything else is discarded, whatever the AI suggested.
Each engine was tested against a seeded production database and its twin, query by query. The scripts that produce these numbers ship with QueryInsights.
20/20 plans, 20/20 costs within 10%
Hypothetical indexes via HypoPG on the twin only. Nothing is installed on production.
8/8 costs identical
Production statistics injected with INJECT STATISTICS into an empty twin cluster.
4/9 costs within 10%
Full scans match. Indexed lookups read the twin's empty index, so MySQL reports plan shape until a correction ships.
Average workload latency went from 176 ms to 1.01 ms under the same load.
| Query | Twin predicted (planner cost) | Production measured (mean time) | Faster | ||
|---|---|---|---|---|---|
| Events for a user, newest first | 36,416.8 | 4.5 | 118.3 ms | 0.073 ms | 1,621× |
| Orders for a user | 22,743.7 | 89.9 | 73.6 ms | 0.052 ms | 1,415× |
| Pending orders, newest first | 41,412 | 5,420.4 | 50.8 ms | 0.022 ms | 2,309× |
| Users by country and plan | 4,870 | 2,196.4 | 33.3 ms | 2.3 ms | 14× |
| Order by id (already indexed) | 8.2 | unchanged | 0.089 ms | 0.020 ms | no change predicted |
| Update order status | 8.2 | unchanged | 0.13 ms | 0.063 ms | no change predicted |
The optimizer chooses a plan from how big a table is and how its values are spread. A 1% sample looks like a small database, so the optimizer plans for a small database. The empty twin believes it is production-sized, because its statistics say so.
| PostgreSQL 16, same data | Empty twin + statistics | 1% data sample |
|---|---|---|
| Plan shape matches production | 20 of 20 queries | 1 of 7 queries |
| Costs comparable to production | Yes, within 10% | No, about 70× smaller |
| Copies production rows | No | Yes, real customer rows |
| Load on production | Catalog reads, milliseconds | Scans 1% of every table |
| Disk on the twin | About zero | 1% of production |
| Growth forecasts (2×, 10×) | Edit the statistics | Not possible |
Run the twin image at your production major version and point QueryInsights at it. Workload advice for that instance is then found and proven on the twin. Production keeps its existing read-only role.
pg_stat_statementspodman build --build-arg PG_MAJOR=16 \
-t queryinsights-twin:16 local/twin
QUERYINSIGHTS_POSTGRES_INSTANCES='[{
"name": "orders-db",
"dsn": "postgres://queryinsights:…@orders-db:5432/app",
"twin_dsn": "postgres://postgres:…@qi-twin:5432/postgres"
}]'QueryInsights is in early access. Tell us a little about your workload and we'll be in touch.
Or email us at bhargav.akhani@queryinsights.dev