Azure SQL Managed Instance Next-Gen: Real IOPS Control at Last

Introduction

Azure SQL Managed Instance has always been positioned as the “lift-and-shift friendly” PaaS option for SQL Server. You get high compatibility, automatic patching, built-in HA, and reduced operational overhead. For many workloads, it is the easiest way to modernize without rewriting applications.

But for years, one thing held it back.

Storage performance.

If you needed more I/O, you had to scale the entire instance. More vCores, more memory, more cost, even when CPU was not the bottleneck. Disk latency issues meant bigger SKUs, longer scale times, and higher bills. It worked, but it was clumsy, slow, and inefficient.

The Next-Gen General Purpose Managed Instance finally fixes that.

This is not a minor enhancement. It is a fundamental architectural shift in how storage works in Azure SQL Managed Instance.


What Actually Changed in Next-Gen Managed Instance

The biggest change is invisible at first glance.

Microsoft replaced the legacy storage backend with Azure Elastic SAN.

This means your data and log files are no longer tied to the old page blob-based architecture. Instead, they run on a shared, high-performance SAN designed for consistent low latency and scalable throughput.

Elastic SAN was built specifically to decouple storage performance from compute. That design decision is what makes everything else possible.
This is the foundation for predictable I/O, better concurrency, and most importantly, direct control over throughput.


The IOPS Slider: The Feature DBAs Have Been Waiting For

Next-Gen General Purpose introduces a new capability that changes daily operations.

You can now directly provision IOPS.

In the Azure portal (and via ARM or REST), you can move a slider to add IOPS without touching vCores or memory. The change is applied online, with no failover and no downtime.

This is a massive shift from the old model.

Before:

  • Need more I/O? Scale compute
  • Scaling takes time
  • Costs increase across the board
  • Performance tuning is guesswork

Now:

  • Identify I/O bottleneck
  • Increase IOPS
  • Measure results
  • Adjust again if needed

This is how performance tuning should work.


How the IOPS Model Works

Microsoft implemented a simple and transparent model:

• You get 3 IOPS per GB of reserved storage for free
• 1 TB storage includes ~3,000 baseline IOPS
• You can provision additional IOPS at low cost
• Changes are applied online
• No instance restart required

Additional IOPS are billed based on regional storage pricing, roughly storage price divided by three. In many regions, 1,000 extra IOPS costs less than $40 per month.

That is cheaper than a single vCore upgrade and far more targeted.


The One Limitation You Need to Know

IOPS are still capped by vCore count.

The current limit is 1,600 IOPS per vCore.
That means:

  • 8 vCores = 12,800 max IOPS
  • 16 vCores = 25,600 max IOPS
  • 32 vCores = 51,200 max IOPS

This is likely a safety guardrail to keep storage and compute balanced, but it does limit extreme I/O-heavy designs. Even so, it is a massive improvement over the old “scale everything” model.


Why This Matters to DBAs

This upgrade gives DBAs something rare in the cloud.

Operational control.

You can now tune storage the same way you did with on-prem SANs:

  • Detect bottleneck
  • Add throughput
  • Validate improvement
  • Roll back if needed

All inside a managed service.

This is the closest Managed Instance has ever felt to running real infrastructure, without the operational burden.


Hands-On Workshop: Measuring and Tuning IOPS

This workshop lets you prove the impact yourself.


Step 1: Baseline Your I/O

Run these before changing anything.

Top waits




SELECT *
FROM sys.dm_os_wait_stats
WHERE wait_type LIKE 'PAGEIOLATCH%' OR wait_type = 'WRITELOG'
ORDER BY wait_time_ms DESC;

File latency

SELECT
DB_NAME(vfs.database_id) AS database_name,
mf.name,
mf.type_desc,
vfs.num_of_reads,
vfs.io_stall_read_ms / NULLIF(vfs.num_of_reads,0) AS avg_read_ms,
vfs.num_of_writes,
vfs.io_stall_write_ms / NULLIF(vfs.num_of_writes,0) AS avg_write_ms
FROM sys.dm_io_virtual_file_stats(NULL, NULL) vfs
JOIN sys.master_files mf
ON vfs.database_id = mf.database_id
AND vfs.file_id = mf.file_id
ORDER BY avg_read_ms DESC;

Step 2: Increase IOPS

Portal

  1. Open your Managed Instance
  2. Go to Compute + Storage
  3. Enable Next-Gen General Purpose
  4. Move the IOPS slider
  5. Apply changes

No downtime. No reconnects.


Step 3: Measure Again

Run the same scripts and compare:

  • Read latency
  • Write latency
  • Wait stats
  • Concurrency stability

You should see lower latency and fewer I/O waits almost immediately.


Step 4: Find the Cost Sweet Spot

Reduce IOPS gradually until performance just meets your SLA.
This gives you maximum efficiency for minimum cost.

This is now possible in Managed Instance for the first time.


Automation: Adjust IOPS with Azure CLI

You can also automate this using ARM REST.

az rest \
--method patch \
--url "https://management.azure.com/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Sql/managedInstances/<mi>?api-version=2024-11-01-preview" \
--body "{
\"properties\": {
\"storageIOps\": 8000
}
}"

This allows you to script performance changes during load tests or scheduled scaling windows.


ARM Template Snippet (Next-Gen Enabled)

{
"type": "Microsoft.Sql/managedInstances",
"apiVersion": "2024-11-01-preview",
"name": "mi-nextgen",
"location": "eastus",
"sku": {
"name": "GP_Gen5",
"tier": "GeneralPurpose",
"capacity": 8
},
"properties": {
"storageSizeInGB": 1024,
"isGeneralPurposeV2": true,
"storageIOps": 8000
}
}

Summary

Azure SQL Managed Instance Next-Gen is one of the most important platform upgrades Microsoft has released for SQL Server in the cloud.

• Storage runs on Elastic SAN
• IOPS are decoupled from compute
• Baseline IOPS are free
• Performance tuning is simple and fast
• Costs are predictable
• Changes happen online

For years, DBAs have been forced to overprovision compute to fix storage problems. That era is over.

If you run Managed Instance today and care about performance, Next-Gen General Purpose is not optional. It is the new baseline.


References

Microsoft Learn – Next-Gen General Purpose Managed Instance
https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/service-tiers-next-gen-general-purpose-use

Azure SQL Team Blog – Next-Gen General Purpose GA
https://techcommunity.microsoft.com/blog/azuresqlblog/generally-available-azure-sql-managed-instance-next-gen-general-purpose

Elastic SAN Performance Overview
https://learn.microsoft.com/en-us/azure/storage/elastic-san/elastic-san-performance

Azure SQL Managed Instance Resource Limits
https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/resource-limits

Azure SQL Managed Instance Overview
https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/sql-managed-instance-paas-overview

DECLARE @DoDelete bit = 0; -- 0 = TEST (SELECT), 1 = DELETE
IF @DoDelete = 0
BEGIN
;WITH d AS (
SELECT
fss.function_schedule_status_id,
fss.function_id,
fss.schedule_status,
fss.status_date,
fss.status_description,
fss.created_on,
fss.created_by,
fss.last_modified_on,
fss.last_modified_by,
fss.status_reference_id,
ROW_NUMBER() OVER (
PARTITION BY fss.function_id
ORDER BY fss.created_on DESC, fss.function_schedule_status_id DESC
) AS rn
FROM dbo.function_schedule_status AS fss
)
SELECT
function_schedule_status_id,
function_id,
schedule_status,
status_date,
status_description,
created_on,
created_by,
last_modified_on,
last_modified_by,
status_reference_id,
rn
FROM d
WHERE rn > 1000
OR created_on < DATEADD(MONTH, -3, GETDATE());
END
ELSE
BEGIN
;WITH d AS (
SELECT
fss.function_schedule_status_id,
fss.function_id,
fss.created_on,
ROW_NUMBER() OVER (
PARTITION BY fss.function_id
ORDER BY fss.created_on DESC, fss.function_schedule_status_id DESC
) AS rn
FROM dbo.function_schedule_status AS fss
)
DELETE fss
FROM dbo.function_schedule_status AS fss
JOIN d
ON d.function_schedule_status_id = fss.function_schedule_status_id
WHERE d.rn > 1000
OR d.created_on < DATEADD(MONTH, -3, GETDATE());
END


Discover more from SQLYARD

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from SQLYARD

Subscribe now to keep reading and get access to the full archive.

Continue reading