ProxySQL MySQL MySQL · DBA · Advanced

ProxySQLTraffic Mirroring

Mirror production MySQL traffic to a test server using ProxySQL mirror_hostgroup. Validate new servers, warm up replicas and A/B test with zero application impact.

ProxySQL can mirror (shadow) queries to a secondary hostgroup while still serving the original query normally. The application gets its response from the primary backend — the mirrored query runs asynchronously on the mirror backend without affecting response time.

Traffic mirroring is perfect for:

  • Testing a new MySQL version with production traffic
  • Validating a new schema or index before cutover
  • Load testing a new server with real query patterns
  • A/B testing query optimizations
  • Warming up a new replica before adding it to the read pool
Application sends: SELECT * FROM products WHERE id=1

ProxySQL:
  ├── Primary: Sends to HG20 (Replica) → returns result to app ✓
  └── Mirror:  Sends copy to HG30 (Test server) async → result discarded

App only sees result from HG20. HG30 gets the query load.
SQL — Add Mirror Server
-- Add new/test MySQL server in its own hostgroup (HG30)
INSERT INTO mysql_servers (hostgroup_id, hostname, port, status, comment)
VALUES (30, '192.168.1.200', 3306, 'ONLINE', 'Mirror/Test MySQL Server');

LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;

-- Make sure app user can connect to HG30 as well
-- (ProxySQL uses same credentials for mirror connections)
SQL — Basic Mirroring Rule
-- Mirror ALL SELECT queries to HG30 while serving from HG20
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, destination_hostgroup,
   mirror_hostgroup, apply, comment)
VALUES
  (500, 1, '^SELECT', 20, 30, 1,
   'Serve reads from HG20, mirror to HG30');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
SQL — Selective Mirroring
-- Mirror only specific table queries
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, destination_hostgroup,
   mirror_hostgroup, apply, comment)
VALUES
  (501, 1, 'SELECT .* FROM orders', 20, 30, 1,
   'Mirror orders queries to test server');

-- Mirror queries from specific user only
INSERT INTO mysql_query_rules
  (rule_id, active, username, match_pattern, destination_hostgroup,
   mirror_hostgroup, apply, comment)
VALUES
  (502, 1, 'testuser', '^SELECT', 20, 30, 1,
   'Mirror testuser queries to HG30');

-- Mirror only 10% of traffic using flagIN/flagOUT chaining
-- (ProxySQL does not natively support % mirroring but you can use
--  application-side sampling or route specific users)

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
SQL — Monitor Mirroring
-- Check if mirror hostgroup is receiving queries
SELECT hostgroup, srv_host, Queries, Bytes_data_sent
FROM stats.stats_mysql_connection_pool
WHERE hostgroup=30;

-- Check query digest on mirror hostgroup
SELECT hostgroup, digest_text, count_star
FROM stats.stats_mysql_query_digest
WHERE hostgroup=30
ORDER BY count_star DESC LIMIT 20;

-- Check rule hits for mirror rule
SELECT rule_id, hits, mirror_hostgroup, comment
FROM stats.stats_mysql_query_rules
WHERE mirror_hostgroup IS NOT NULL;
SQL — Disable Mirroring
-- Disable the mirror rule without deleting it
UPDATE mysql_query_rules SET active=0 WHERE rule_id=500;

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

-- Or remove mirror_hostgroup to stop mirroring but keep routing
UPDATE mysql_query_rules SET mirror_hostgroup=NULL WHERE rule_id=500;

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
💡 Note: Mirrored queries are fire-and-forget — ProxySQL does not wait for mirror results. If the mirror server is slow or down, it does not affect your application response time.
⚠ Warning: Mirrored write queries (INSERT/UPDATE/DELETE) will actually EXECUTE on the mirror server. Use mirroring with SELECT-only rules unless you want data changes on the mirror.