How to Use MySQL with Azure Database for MySQL

How to Use MySQL with Azure Database for MySQL

When I migrated my first production workload off a self-managed MySQL server onto Azure Database for MySQL, the biggest shift wasn’t technical — it was mental. I stopped thinking about patching, backup scripts, and failover scripting, and started thinking purely about schema design, query performance, and application connection behavior. In this article, I’ll cover everything from MySQL fundamentals to provisioning, securing, tuning, and troubleshooting Azure Database for MySQL, so you have a complete picture whether you’re new to managed databases or migrating an existing system.

Table of Contents

  1. MySQL Architecture Fundamentals
  2. What Azure Database for MySQL Actually Manages For You
  3. Choosing a Deployment Option: Flexible Server vs Single Server
  4. Provisioning Azure Database for MySQL
  5. Connecting to Azure Database for MySQL
  6. Configuring Server Parameters
  7. High Availability and Read Replicas
  8. Backup and Restore
  9. Migrating an Existing MySQL Database to Azure
  10. Security Best Practices
  11. Performance Tuning and Optimization
  12. Monitoring with Azure Metrics and Query Insights
  13. Troubleshooting Common Issues
  14. Interview Questions
  15. FAQs
  16. Summary and Key Takeaways
  17. References

1. MySQL Architecture Fundamentals

Azure Database for MySQL runs standard MySQL server binaries underneath — usually MySQL 5.7 or 8.0 — so the same InnoDB storage engine fundamentals apply. The SQL layer parses and optimizes queries, and InnoDB handles the actual reading and writing of pages through its buffer pool and redo log, exactly as it would on a self-hosted server.

graph TD
    A[Application] --> B[Azure Database for MySQL Endpoint]
    B --> C[MySQL Server - SQL Layer]
    C --> D[InnoDB Storage Engine]
    D --> E[Buffer Pool]
    D --> F[Azure-Managed Storage]
    F --> G[Automated Backups]

The difference is everything below the SQL layer — storage, backups, patching, and failover — is managed by Azure rather than by me.

2. What Azure Database for MySQL Actually Manages For You

I’ve found it helpful to be explicit about what moves off my plate versus what stays my responsibility:

Managed by AzureStill My Responsibility
OS patching and MySQL version updatesSchema design and indexing
Automated backupsQuery optimization
Storage scaling and redundancyApplication-level connection pooling
High availability failoverUser privilege management
Infrastructure monitoringCost optimization (right-sizing tier)

3. Choosing a Deployment Option: Flexible Server vs Single Server

Azure has moved almost entirely to Flexible Server, and that’s what I recommend for any new deployment — Single Server is being retired and shouldn’t be used for new projects.

FeatureFlexible ServerSingle Server (legacy)
Zone redundant HAYesNo
Stop/Start to save costYesNo
Custom maintenance windowYesLimited
Burstable compute tierYesNo
Recommended for new projectsYesNo

4. Provisioning Azure Database for MySQL

I typically provision using the Azure CLI so the setup is scriptable and repeatable.

az login

az group create --name mysql-rg --location eastus

az mysql flexible-server create \
  --resource-group mysql-rg \
  --name my-app-mysql-server \
  --location eastus \
  --admin-user mysqladmin \
  --admin-password "StrongPass123!" \
  --sku-name Standard_B2s \
  --tier Burstable \
  --storage-size 32 \
  --version 8.0

Output (abridged):

{
  "fullyQualifiedDomainName": "my-app-mysql-server.mysql.database.azure.com",
  "administratorLogin": "mysqladmin",
  "state": "Ready",
  "version": "8.0"
}

Creating a database inside the server:

az mysql flexible-server db create \
  --resource-group mysql-rg \
  --server-name my-app-mysql-server \
  --database-name appdb

Allowing my application’s IP to connect:

az mysql flexible-server firewall-rule create \
  --resource-group mysql-rg \
  --name my-app-mysql-server \
  --rule-name AllowMyIP \
  --start-ip-address 203.0.113.10 \
  --end-ip-address 203.0.113.10

5. Connecting to Azure Database for MySQL

From the CLI:

mysql -h my-app-mysql-server.mysql.database.azure.com \
  -u mysqladmin -p --ssl-mode=REQUIRED

From a Python application using mysql-connector-python:

import mysql.connector

conn = mysql.connector.connect(
    host="my-app-mysql-server.mysql.database.azure.com",
    user="mysqladmin",
    password="StrongPass123!",
    database="appdb",
    ssl_disabled=False
)

cursor = conn.cursor()
cursor.execute("SELECT VERSION();")
print(cursor.fetchone())

I always keep ssl_disabled=False (or --ssl-mode=REQUIRED on the CLI), since Azure enforces TLS on the public endpoint by default, and connections without it will simply be rejected.

6. Configuring Server Parameters

Azure exposes most InnoDB and general MySQL parameters through Server Parameters, rather than letting me edit my.cnf directly.

az mysql flexible-server parameter set \
  --resource-group mysql-rg \
  --server-name my-app-mysql-server \
  --name innodb_buffer_pool_size \
  --value 1073741824
az mysql flexible-server parameter set \
  --resource-group mysql-rg \
  --server-name my-app-mysql-server \
  --name max_connections \
  --value 300

Some parameters require a server restart to take effect, which Azure will tell you explicitly in the CLI response.

7. High Availability and Read Replicas

For production workloads, I enable zone-redundant high availability, which keeps a synchronized standby in a different availability zone:

az mysql flexible-server update \
  --resource-group mysql-rg \
  --name my-app-mysql-server \
  --high-availability ZoneRedundant

For read scaling, I add read replicas:

az mysql flexible-server replica create \
  --replica-name my-app-mysql-replica1 \
  --resource-group mysql-rg \
  --source-server my-app-mysql-server
graph LR
    Primary[Primary - Zone 1] -- sync replication --> Standby[HA Standby - Zone 2]
    Primary -- async replication --> Replica1[Read Replica]
    App[Application] --> Primary
    Reports[Reporting Queries] --> Replica1

I route reporting or analytics traffic to the read replica so it doesn’t compete with transactional writes on the primary.

8. Backup and Restore

Azure takes automated backups on a schedule I define, with point-in-time restore capability.

az mysql flexible-server parameter set \
  --resource-group mysql-rg \
  --server-name my-app-mysql-server \
  --name backup_retention_days \
  --value 14

Restoring to a new server at a specific point in time:

az mysql flexible-server restore \
  --resource-group mysql-rg \
  --name my-app-mysql-server-restored \
  --source-server my-app-mysql-server \
  --restore-time "2026-07-15T03:00:00Z"

I still take my own logical backups with mysqldump for anything I might need to restore into a completely different environment, since Azure’s built-in restore only creates a new server within the same Azure ecosystem.

mysqldump -h my-app-mysql-server.mysql.database.azure.com \
  -u mysqladmin -p --ssl-mode=REQUIRED appdb > appdb-backup.sql

9. Migrating an Existing MySQL Database to Azure

For migrations, I use the Azure Database Migration Service (DMS) for minimal-downtime cutovers, or a manual dump/restore for smaller databases.

Manual approach for a small database:

mysqldump -h old-server -u root -p appdb > appdb.sql

mysql -h my-app-mysql-server.mysql.database.azure.com \
  -u mysqladmin -p --ssl-mode=REQUIRED appdb < appdb.sql

For larger, low-downtime migrations, I set up DMS through the Azure Portal, which handles initial full load plus continuous binlog-based replication until cutover — this is the option I use whenever downtime has to be measured in minutes rather than hours.

10. Security Best Practices

CREATE USER 'appuser'@'%' IDENTIFIED BY 'AppPass123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'%';
FLUSH PRIVILEGES;
az mysql flexible-server update \
  --resource-group mysql-rg \
  --name my-app-mysql-server \
  --public-network-access Disabled

11. Performance Tuning and Optimization

I approach tuning in three layers: compute tier sizing, InnoDB parameters, and query-level optimization.

Tuning AreaRecommendation
Compute tierMatch vCores/memory to peak connection and query load, not average
innodb_buffer_pool_sizeSet to roughly 70% of the server’s total memory
StorageUse Premium SSD tier for latency-sensitive workloads
ConnectionsUse a connection pooler (e.g., ProxySQL) if app opens many short-lived connections
Query tuningUse EXPLAIN and slow query log before scaling compute
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
EXPLAIN SELECT * FROM orders WHERE customer_id = 1042;

Scaling compute up is easy in Azure, but I always check for a missing index first — an extra vCore tier costs money every month, while an index is usually free performance.

12. Monitoring with Azure Metrics and Query Insights

Azure provides built-in metrics through Azure Monitor, and Flexible Server includes Query Performance Insight, which surfaces the top resource-consuming queries without needing an external APM tool.

az monitor metrics list \
  --resource /subscriptions/<sub-id>/resourceGroups/mysql-rg/providers/Microsoft.DBforMySQL/flexibleServers/my-app-mysql-server \
  --metric "cpu_percent" \
  --interval PT1H

I check cpu_percent, memory_percent, storage_percent, and active_connections daily on any production server, and set alert rules so I’m notified before a resource ceiling causes an outage.

12.5 Cost Optimization Strategies

Since Azure Database for MySQL bills continuously while the server is running, I pay close attention to right-sizing, especially for non-production environments.

For dev/test servers, I stop the server outside working hours using Flexible Server’s stop/start capability:

az mysql flexible-server stop \
  --resource-group mysql-rg \
  --name my-app-mysql-dev-server
az mysql flexible-server start \
  --resource-group mysql-rg \
  --name my-app-mysql-dev-server

I automate this with an Azure Automation runbook or a scheduled GitHub Actions workflow so dev servers stop every evening and start again each morning, which meaningfully cuts the monthly bill for environments that aren’t needed around the clock.

For production, I periodically review compute and storage metrics before renewing capacity commitments:

az mysql flexible-server show \
  --resource-group mysql-rg \
  --name my-app-mysql-server \
  --query "{sku:sku.name, storage:storage.storageSizeGb}"
Cost LeverWhen I Use It
Burstable tier (Standard_B)Dev/test or low, spiky traffic workloads
General Purpose tierSteady production workloads
Business Critical tierLatency-sensitive, high-throughput production systems
Reserved capacity pricingPredictable long-term production workloads
Stop/start automationNon-production environments only

I also review storage auto-grow settings, since storage in Azure Database for MySQL can only scale up, never down — so I start with a conservative size and let auto-grow handle real growth rather than over-provisioning up front.

az mysql flexible-server parameter set \
  --resource-group mysql-rg \
  --server-name my-app-mysql-server \
  --name storage_autogrow \
  --value ON

13. Troubleshooting Common Issues

SymptomLikely CauseFix
Access denied from a known-good IPMissing firewall ruleAdd the IP range via az mysql flexible-server firewall-rule create
SSL connection errorClient not configured for TLSAdd --ssl-mode=REQUIRED or configure the driver’s SSL options
Sudden latency spikeStorage or CPU throttling on a lower tierCheck Azure Monitor metrics, consider scaling up
Replica lag growingHeavy write load on primaryCheck replica IOPS, consider a larger tier for the replica
Restore fails to same server nameName collisionRestore always creates a new server; choose a new name

14. Interview Questions

  1. What’s the difference between Azure Database for MySQL Flexible Server and Single Server?
  2. How does zone-redundant high availability work in Azure Database for MySQL?
  3. How would you perform a low-downtime migration of an on-premises MySQL database to Azure?
  4. What Azure-native tool would you use to identify the top resource-consuming queries?
  5. Why would you disable public network access and use Private Link instead?
  6. How do you decide between scaling compute versus optimizing a query?

15. FAQs

Is Azure Database for MySQL based on the real MySQL engine? Yes, it runs standard MySQL Community Edition binaries under Azure’s managed infrastructure layer.

Can I access the underlying OS or file system? No — Azure Database for MySQL is a fully managed PaaS offering, so there’s no OS-level or file-system access.

How do I migrate with minimal downtime? I use Azure Database Migration Service, which performs an initial full load followed by continuous replication until cutover.

Does Azure Database for MySQL support read replicas? Yes, and I use them regularly to separate reporting and analytics traffic from transactional workloads on the primary.

16. Summary and Key Takeaways

Azure Database for MySQL removes the operational burden of patching, backups, and failover, letting me focus purely on schema design and query performance. My core habits are: always use Flexible Server for new projects, enforce TLS and Private Link for security, monitor built-in metrics and Query Performance Insight before scaling compute, and always fix a missing index before reaching for a bigger tier. The managed layer handles infrastructure — the database design decisions are still entirely mine to get right.

17. References

Exit mobile version