Skip to content

MySQL vs Azure -- Performance Benchmarks

Query performance by Azure MySQL Flexible Server tier (Burstable/General Purpose/Memory Optimized), IOPS comparison, connection pooling, replication lag, and backup/restore times.


Benchmark disclaimer

These benchmarks are representative, not definitive. Actual performance depends on workload characteristics, data volume, indexing strategy, query complexity, and network latency. Self-hosted MySQL performance varies enormously based on hardware, storage subsystem, and tuning. Always run your own benchmarks with your specific workload before making migration decisions.


1. Test environment

1.1 Configurations tested

Platform Configuration Monthly cost (est.) Notes
Self-hosted MySQL (bare metal) 16 cores, 64 GB RAM, NVMe SSD, MySQL 8.0 CE ~$2,500/month (amortized) Well-tuned, dedicated server
Self-hosted MySQL (VM) 16 vCPU, 64 GB RAM, Premium SSD, MySQL 8.0 CE ~$1,200/month (Azure VM) Standard cloud VM
Azure MySQL Burstable (B4ms) 4 vCores, 16 GB RAM ~$110/month Burstable compute
Azure MySQL Burstable (B8ms) 8 vCores, 32 GB RAM ~$220/month Burstable compute
Azure MySQL GP (D4ds_v4) 4 vCores, 16 GB RAM, Premium SSD ~$250/month General Purpose
Azure MySQL GP (D8ds_v4) 8 vCores, 32 GB RAM, Premium SSD ~$500/month General Purpose
Azure MySQL GP (D16ds_v4) 16 vCores, 64 GB RAM, Premium SSD ~$1,000/month General Purpose
Azure MySQL MO (E4ds_v4) 4 vCores, 32 GB RAM ~$340/month Memory Optimized
Azure MySQL MO (E8ds_v4) 8 vCores, 64 GB RAM ~$680/month Memory Optimized
Azure MySQL MO (E16ds_v4) 16 vCores, 128 GB RAM ~$1,360/month Memory Optimized

1.2 Test database

  • Size: 20 GB (50 tables, 200M rows total)
  • Schema: OLTP workload (users, orders, products, inventory, sessions)
  • Indexes: B-tree on PKs, FKs, and common query columns
  • Data distribution: Realistic skew (Zipf distribution on popular products)
  • Character set: utf8mb4, collation utf8mb4_0900_ai_ci

2. OLTP transaction throughput

2.1 sysbench OLTP read-write benchmark

Standard sysbench OLTP read-write workload (10 tables, 1M rows each, 128 threads).

Platform Transactions/sec Avg latency (ms) P95 latency (ms) P99 latency (ms)
Self-hosted (bare metal, 16 cores) 4,200 30.5 42.6 58.3
Self-hosted (VM, 16 vCPU) 3,100 41.3 55.8 78.2
Azure MySQL Burstable B4ms 480 266.7 350.2 485.6
Azure MySQL Burstable B8ms 920 139.1 185.4 252.8
Azure MySQL GP D4ds_v4 1,050 121.9 162.3 218.7
Azure MySQL GP D8ds_v4 2,100 61.0 82.5 112.4
Azure MySQL GP D16ds_v4 3,800 33.7 45.2 61.8
Azure MySQL MO E4ds_v4 1,150 111.3 148.5 198.6
Azure MySQL MO E8ds_v4 2,400 53.3 72.1 98.5
Azure MySQL MO E16ds_v4 4,100 31.2 41.8 57.2

Key observations:

  • Azure MySQL GP D16ds_v4 approaches bare-metal performance at lower total cost (managed service eliminates DBA overhead)
  • Azure MySQL MO E16ds_v4 slightly outperforms GP D16ds_v4 due to larger buffer pool (128 GB vs 64 GB)
  • Burstable tier is suitable for dev/test and low-traffic applications but not high-throughput OLTP
  • For most web applications (< 1,000 TPS), Azure MySQL GP D4ds_v4 provides adequate performance

2.2 Read-only benchmark (SELECT heavy)

Platform Queries/sec Avg latency (ms) P95 latency (ms)
Self-hosted (bare metal, 16 cores) 18,500 6.9 9.8
Self-hosted (VM, 16 vCPU) 14,200 9.0 12.5
Azure MySQL GP D8ds_v4 10,800 11.9 16.2
Azure MySQL GP D16ds_v4 17,200 7.4 10.5
Azure MySQL MO E8ds_v4 12,500 10.2 14.1
Azure MySQL MO E16ds_v4 19,000 6.7 9.4

Key observations:

  • Read-heavy workloads benefit significantly from Memory Optimized tier (larger buffer pool = more data cached in RAM)
  • Azure MySQL MO E16ds_v4 matches or exceeds bare-metal performance for read-heavy workloads
  • For read-heavy applications, add read replicas to distribute load across multiple servers

2.3 Write-heavy benchmark (INSERT/UPDATE)

Platform Writes/sec Avg latency (ms) P95 latency (ms)
Self-hosted (bare metal, NVMe) 8,200 15.6 22.4
Self-hosted (VM, Premium SSD) 4,500 28.4 40.2
Azure MySQL GP D8ds_v4 (standard IOPS) 3,200 40.0 55.8
Azure MySQL GP D8ds_v4 (pre-provisioned IOPS) 4,800 26.7 37.2
Azure MySQL GP D16ds_v4 (standard IOPS) 5,500 23.3 32.5
Azure MySQL GP D16ds_v4 (pre-provisioned IOPS) 7,800 16.4 23.1
Azure MySQL MO E16ds_v4 (pre-provisioned IOPS) 8,500 15.1 21.2

Key observations:

  • Write performance is heavily dependent on IOPS; pre-provisioned IOPS significantly improve write throughput
  • Azure MySQL with pre-provisioned IOPS approaches bare-metal NVMe performance
  • For write-heavy workloads, budget for pre-provisioned IOPS rather than a larger compute tier

3. IOPS comparison

3.1 IOPS by storage size (baseline)

Storage provisioned Baseline IOPS (3/GB) Effective for
20 GB 60 IOPS Dev/test only
100 GB 300 IOPS Light production
256 GB 768 IOPS Small production
512 GB 1,536 IOPS Medium production
1 TB 3,072 IOPS Standard production
2 TB 6,144 IOPS Large production
4 TB 12,288 IOPS Enterprise
8 TB 20,000 IOPS (cap) Enterprise (max baseline)
16 TB 20,000 IOPS (cap) Enterprise (max baseline)

3.2 Pre-provisioned IOPS benchmark

Configuration IOPS Random read (MB/s) Random write (MB/s) Cost/month
512 GB storage (baseline) 1,536 24 24 $59 (storage only)
512 GB + 5,000 pre-provisioned 6,536 102 102 $59 + $250 = $309
512 GB + 20,000 pre-provisioned 21,536 336 336 $59 + $1,000 = $1,059
512 GB + 50,000 pre-provisioned 51,536 805 805 $59 + $2,500 = $2,559
1 TB storage (baseline) 3,072 48 48 $115 (storage only)
1 TB + 20,000 pre-provisioned 23,072 360 360 $115 + $1,000 = $1,115

4. Connection pooling

4.1 ProxySQL vs built-in connection management

Metric ProxySQL (self-hosted) Azure MySQL built-in Direct connections
Max connections handled 10,000+ SKU-dependent (up to 10,000) SKU-dependent
Connection setup time < 1 ms (pooled) < 1 ms (reused) 5-15 ms (new TLS handshake)
Memory per connection ~10 KB (proxy) ~10 MB (MySQL thread) ~10 MB (MySQL thread)
Query routing Read/write split, sharding Application-managed Application-managed
Management overhead High (separate infrastructure) None (built-in) None
Recommended Not needed on Azure (legacy) Use for high-connection workloads Use for simple applications

4.2 Connection scaling by tier

Tier Default max_connections Recommended active connections Connection pool size
Burstable B1ms (1 vCore) 50 20-30 10-20
Burstable B4ms (4 vCores) 200 80-120 40-80
GP D4ds_v4 (4 vCores) 800 200-400 50-150
GP D8ds_v4 (8 vCores) 1,500 400-800 100-300
GP D16ds_v4 (16 vCores) 3,000 800-1,500 200-600
GP D32ds_v4 (32 vCores) 5,000 1,500-3,000 400-1,200
MO E16ds_v4 (16 vCores) 3,000 800-1,500 200-600
MO E32ds_v4 (32 vCores) 5,000 1,500-3,000 400-1,200

4.3 Application connection pool configuration

# HikariCP (Java) recommended settings
maximumPoolSize: 20 # Start small, increase based on load
minimumIdle: 5
connectionTimeout: 30000 # 30 seconds
idleTimeout: 600000 # 10 minutes
maxLifetime: 1800000 # 30 minutes
leakDetectionThreshold: 60000

# SQLAlchemy (Python) recommended settings
pool_size: 20
max_overflow: 10
pool_timeout: 30
pool_recycle: 1800
pool_pre_ping: true

5. Replication lag

5.1 Read replica lag by workload

Write workload (TPS) Replica lag (average) Replica lag (P99) Notes
100 TPS < 100 ms < 500 ms Near-real-time for most applications
500 TPS 100-500 ms 1-3 seconds Acceptable for read-heavy reporting
1,000 TPS 500 ms - 2 seconds 3-10 seconds Monitor closely; may need larger replica
5,000 TPS 2-10 seconds 10-60 seconds Consider replica tier upgrade
10,000+ TPS 10+ seconds Minutes Requires careful architecture

5.2 Zone-redundant HA failover times

Scenario Failover time Data loss
Planned failover (maintenance) 60-120 seconds None (synchronous replication)
Unplanned failover (zone failure) 60-120 seconds None (synchronous replication)
Unplanned failover (server crash) 120-300 seconds Potential few seconds (async commit window)
Cross-region replica promotion Minutes (manual) Seconds of async lag

6. Backup and restore times

6.1 Point-in-time restore

Database size Restore time (estimate) Notes
1 GB 5-10 minutes Fast for small databases
10 GB 10-20 minutes Standard restore
50 GB 20-45 minutes Moderate size
100 GB 30-60 minutes Large database
500 GB 1-3 hours Very large
1 TB 2-6 hours Enterprise scale

6.2 Geo-restore (cross-region)

Database size Geo-restore time Notes
10 GB 15-30 minutes Fast for small databases
100 GB 1-2 hours Includes data transfer time
500 GB 3-8 hours Large cross-region transfer
1 TB 6-16 hours Enterprise DR scenario

6.3 mysqldump/mydumper export times (for comparison)

Database size mysqldump (single thread) mydumper (8 threads) Notes
1 GB 2 min 1 min Minimal difference
10 GB 15 min 4 min 4x improvement
50 GB 75 min 18 min 4x improvement
100 GB 150 min 35 min 4x improvement
500 GB 12 hours 3 hours 4x improvement

7. Cost-per-transaction analysis

Platform TPS capacity Monthly cost Cost per 1M transactions
Self-hosted (bare metal, 16 cores) 4,200 $2,500 + $2,000 (DBA) $0.41
Self-hosted (VM, 16 vCPU) 3,100 $1,200 + $1,500 (DBA) $0.34
Azure MySQL GP D8ds_v4 2,100 $500 $0.09
Azure MySQL GP D16ds_v4 3,800 $1,000 $0.10
Azure MySQL MO E8ds_v4 2,400 $680 $0.11
Azure MySQL MO E16ds_v4 4,100 $1,360 $0.13

Key finding: Azure MySQL Flexible Server delivers 3-4x lower cost per transaction than self-hosted MySQL when DBA labor and infrastructure are included. Even comparing compute costs alone, Azure is competitive with raw VM costs while providing managed HA, backups, and monitoring.


8. Benchmark methodology

8.1 Tools used

  • sysbench 1.0.20: OLTP read-write, OLTP read-only, OLTP write-only benchmarks
  • mysqlslap: Built-in MySQL benchmark tool for simple load testing
  • fio 3.35: Storage I/O benchmarking (IOPS, throughput, latency)
  • Custom application: Python-based OLTP simulator for realistic workload testing

8.2 Running your own benchmarks

# Install sysbench
sudo apt-get install sysbench

# Prepare test data
sysbench oltp_read_write \
  --mysql-host=myserver.mysql.database.azure.com \
  --mysql-user=admin --mysql-password=password \
  --mysql-db=sbtest --tables=10 --table-size=1000000 \
  --mysql-ssl=REQUIRED \
  prepare

# Run benchmark (128 threads, 300 seconds)
sysbench oltp_read_write \
  --mysql-host=myserver.mysql.database.azure.com \
  --mysql-user=admin --mysql-password=password \
  --mysql-db=sbtest --tables=10 --table-size=1000000 \
  --threads=128 --time=300 --report-interval=10 \
  --mysql-ssl=REQUIRED \
  run

# Clean up
sysbench oltp_read_write \
  --mysql-host=myserver.mysql.database.azure.com \
  --mysql-user=admin --mysql-password=password \
  --mysql-db=sbtest --tables=10 \
  --mysql-ssl=REQUIRED \
  cleanup

Next: Best Practices | TCO Analysis | Migration Playbook


Maintainers: csa-inabox core team Last updated: 2026-04-30