Causes and Countermeasures for Disk I/O Performance Degradation After Migrating an On-Premises SQL Server to AWS
Published 2026-09-07 · Updated 2026-09-07 · Last Verified 2026-09-07
Answer
On-premises physical servers use PCIe bus-attached local NVMe/SSD with extremely low latency and high I/O throughput, but Amazon EBS, the standard for AWS RDS/EC2, is a virtual block storage attached via network, with IOPS and throughput strictly throttled according to instance size and volume type. Without understanding this difference, batches and index rebuilds stall due to throttling even when CPU headroom exists, and oversizing Provisioned IOPS only increases costs without addressing the root cause. The basic approach is to reduce the volume of disk I/O itself, with buffer pool maximization, data compression, physical separation of data and log files, and addressing CDC cleanup delays causing log bloat as the practical center of these efforts.
Applicability of This Document
| Product | SQL Server (Amazon RDS for SQL Server / SQL Server on EC2) |
|---|---|
| Verified Versions | SQL Server 2012 and later (CDC-supported editions vary by version; verify) |
| Environment | AWS migration from on-premises physical servers (Amazon RDS for SQL Server, EC2) |
| Required Permissions | Read-only commands require VIEW DATABASE STATE or equivalent. db_owner (or sysadmin depending on environment) for manually running sys.sp_cdc_cleanup_job and executing DBCC SHRINKFILE |
| Execution Impact | Read-only commands only query. sp_cdc_cleanup_job deletes CDC change data; DBCC SHRINKFILE changes log file size |
| Restart | Not required |
| Last Verified | 2026-09-07 |
Ready-to-Run Commands
- 対象
- SQL Server 2008 or later / Amazon RDS for SQL Server
- 権限
- reference permission on the target table (execute permission for sp_estimate_data_compression_savings)
- 変更作業
- none (estimate only, does not perform actual compression)
- Production実行
- allowed
-- Target: SQL Server 2008 or later / Amazon RDS for SQL Server
-- Permission: reference permission on the target table (execute permission for sp_estimate_data_compression_savings)
-- Change: none (estimate only, does not perform actual compression)
-- Production execution: allowed
USE [SampleDB];
GO
EXEC sys.sp_estimate_data_compression_savings
@schema_name = 'dbo',
@object_name = 'Orders',
@index_id = NULL,
@partition_number = NULL,
@data_compression = 'PAGE';The difference between size_with_current_compression_setting and size_with_requested_compression_setting in the results indicates the estimated disk space reduction from compression. Compression reduces disk I/O at the cost of CPU overhead, so it is more effective in environments with CPU headroom.
- 対象
- SQL Server 2008 or later / Amazon RDS for SQL Server
- 権限
- metadata visibility on sys.databases
- 変更作業
- none (read-only)
- Production実行
- allowed
-- Target: SQL Server 2008 or later / Amazon RDS for SQL Server
-- Permission: metadata visibility on sys.databases
-- Change: none (read-only)
-- Production execution: allowed
SELECT name, log_reuse_wait_desc
FROM sys.databases
WHERE name = 'SampleDB';
-- If the result is 'REPLICATION', CDC or transactional replication is holding unread logsREPLICATION can apply to both CDC and transactional replication. Check whether CDC is enabled on the target database with sys.databases.is_cdc_enabled. For details on tracking capture delays, see "How to check whether the CDC log scan has stopped in SQL Server".
- 対象
- SQL Server 2008 or later (CDC-supported editions) / Amazon RDS for SQL Server
- 権限
- db_owner (execute permission for cdc.sp_cdc_cleanup_job)
- 変更作業
- yes (deletes data from CDC change tables beyond the retention period)
- Production実行
- allowed, but verify that downstream integrations are not still consuming the data to be deleted
-- Target: SQL Server 2008 or later (CDC-supported editions) / Amazon RDS for SQL Server
-- Permission: db_owner (execute permission for cdc.sp_cdc_cleanup_job)
-- Change: yes (deletes data from CDC change tables beyond the retention period)
-- Production execution: allowed, but verify that downstream integrations are not still consuming the data to be deleted
USE [SampleDB];
GO
EXEC sys.sp_cdc_cleanup_job;This is an immediate workaround when the scheduled cleanup job is delayed. However, this addresses the symptom, not why the cleanup job itself became delayed (job stoppage, insufficient runtime, etc.) — investigate separately. Before executing, always confirm that replication targets or downstream ETL processes have not yet read change data within the retention period.
- 対象
- SQL Server 2008 or later / Amazon RDS for SQL Server
- 権限
- sysadmin or db_owner
- 変更作業
- yes (truncates the end of the log file to reduce its size)
- Production実行
- generally allowed, but only after confirming log_reuse_wait_desc is NOTHING
-- Target: SQL Server 2008 or later / Amazon RDS for SQL Server
-- Permission: sysadmin or db_owner
-- Change: yes (truncates the end of the log file to reduce its size)
-- Production execution: generally allowed, but only after confirming log_reuse_wait_desc is NOTHING
USE [SampleDB];
GO
-- specify target size in MB to shrink to
DBCC SHRINKFILE (N'SampleDB_log', 1024);Shrinking before resolving the log_reuse_wait_desc cause will result in immediate re-expansion. In this knowledge base, all SHRINK operations are marked as "high" regardless of impact level. For the difference between data file and log file shrinking and how to use TRUNCATEONLY, see "The difference between DBCC SHRINKDATABASE and DBCC SHRINKFILE, and what to check before running them".
How to Read the Results
| Column | Meaning | What to Check |
|---|---|---|
| log_reuse_wait_desc | Reason why transaction log space is not being released | 'REPLICATION' indicates CDC or transactional replication is holding unread logs. 'NOTHING' means there is no factor preventing release |
| size_with_requested_compression_setting | Estimated size after applying the specified compression type | The larger the difference from the current size, the greater the potential for disk I/O reduction and storage cost savings |
When to Use This
- Nightly batches and index rebuilds take two or more times longer, even with an AWS instance of equivalent or higher spec than the on-premises server before migration
- The entire database becomes temporarily unresponsive during large-scale data loads or index rebuilds
- Batches never complete despite CPU having available capacity
- Storage capacity sharply depletes shortly after migration, triggering frequent RDS auto-storage scaling
- Increasing Provisioned IOPS (io2, etc.) only drives up storage costs without achieving fundamental improvement
Possible Causes (Most Likely First)
01
Latency inherent to network-attached storage (EBS)
On-premises physical NVMe/SSD is directly attached via the PCIe bus and operates with microsecond-level latency, but Amazon EBS, the standard for AWS RDS/EC2, is a virtual block storage connected to the instance via a dedicated internal network link. Physical latency (in the order of milliseconds) is structurally unavoidable.
02
IOPS and throughput throttling (bandwidth limiting)
IOPS and throughput (MB/s) limits per second are strictly defined according to instance size, volume type, and allocated capacity. General-purpose volumes such as gp2/gp3 consume burst credits when exceeding the baseline performance, and reaching limits during large-scale data loads or index rebuilds causes severe throttling that can make the entire DB unresponsive. Even raising volume-side IOPS with io2 or similar may be bottlenecked by the instance-side EBS dedicated bandwidth (EBS-Optimized).
03
Cost inflation from oversizing Provisioned IOPS
When attempting to easily resolve insufficient I/O performance by heavily provisioning high-cost IOPS types such as io2/io2 Block Express, storage costs alone can balloon to hundreds of thousands to millions of yen per month, eliminating the cost-reduction benefits of cloud migration.
04
Unreleased logs due to CDC cleanup job delay
When large batch updates occur and the CDC cleanup job cannot keep up, the transaction log is marked as "not yet read by CDC," preventing log space from being released even after CHECKPOINT or log backups. The log file continues auto-extending, depleting storage capacity and forcing RDS auto-storage scaling to trigger.
Diagnostic Steps
- 1
Estimate compression space reduction for existing tables
Read-onlyUse sys.sp_estimate_data_compression_savings to determine how much disk I/O reduction and storage cost savings potential exists.
- 2
Identify the cause of unreleased logs with log_reuse_wait_desc
Read-onlyCheck whether sys.databases.log_reuse_wait_desc is fixed at REPLICATION. If so, CDC or transactional replication delay is suspected.
- 3
Check whether CDC is enabled and if capture is delayed
Read-onlyVerify CDC status with sys.databases.is_cdc_enabled. If enabled, check capture delay with sys.dm_cdc_log_scan_sessions (see the related article for details).
- 4
Verify EBS volume and instance IOPS/throughput contracted values
Read-onlyCheck the provisioned IOPS and throughput for the volume type (gp3/io2, etc.) and the EBS bandwidth limit on the instance side via the AWS console or CLI.
- 5
Manually execute CDC cleanup
MediumAfter confirming that downstream integrations have consumed all data within the retention period, execute sys.sp_cdc_cleanup_job to restore the log to a releasable state.
- 6
Shrink the transaction log
HighAfter confirming log_reuse_wait_desc is NOTHING, shrink the log file to the target size using DBCC SHRINKFILE.
Solutions
Low-risk action that can be done immediately
Maximize the buffer pool to reduce disk reads
LowBefore paying to increase storage IOPS, scale up the instance size to increase RAM and maximize the SQL Server buffer pool size. Maintaining a state where frequently accessed active data remains cached in memory greatly alleviates the EBS I/O bottleneck.
Manually execute CDC cleanup to clear unreleased logs
MediumExecute sys.sp_cdc_cleanup_job without waiting for the scheduled job, restoring the log to a releasable state. Verify downstream integrations have consumed the data before executing.
Change requiring advance review
Separate data files and log files onto different EBS volumes
MediumMixing data files (random I/O dominant) and transaction logs (sequential writes dominant) on the same EBS volume causes I/O queue contention. Always separate them onto different EBS volumes.
Apply PAGE/ROW compression to reduce I/O volume
MediumWhen CPU resources are available, data compression reduces the block size read from disk and the size written to the log. Estimate the effect with sp_estimate_data_compression_savings before applying.
Offload read load with a read replica
MediumIf reporting queries and batch reads are straining the primary DB I/O bandwidth, build a read-only replica to distribute traffic.
Add CDC cleanup job lag to monitoring
LowMonitor the success/failure and duration of the cleanup job to detect before the delay becomes chronic.
Change involving a restart or service impact
Shrink the transaction log with DBCC SHRINKFILE
HighExecute after confirming log_reuse_wait_desc is NOTHING. Shrinking without resolving the cause will result in immediate re-expansion. The operation consumes I/O during execution, slowing writes to the target.
Work requiring expert review
Redesign the need for Provisioned IOPS (io2, etc.) across the entire workload
Expert Review RequiredConsider this only when IOPS remain insufficient even after buffer pool maximization, data compression, and file separation. Unplanned increases significantly raise monthly costs — have the I/O pattern of the entire workload analyzed and undergo professional review.
!Warnings
- Before running DBCC SHRINKFILE, confirm that log_reuse_wait_desc is NOTHING. Shrinking while the cause remains will result in immediate re-expansion.
- Before deleting CDC change data beyond the retention period with sys.sp_cdc_cleanup_job, verify that downstream integrations (replication targets or ETL) have not yet consumed data from that period.
- Before casually increasing Provisioned IOPS (io2, etc.), first evaluate whether reducing disk I/O itself through buffer pool maximization and data compression is feasible.
- The compression ratios and cost savings figures in this article depend on the workload and data content and are not guaranteed for all environments.
If This Doesn't Resolve It
Have you checked whether EBS volume IOPS/throughput burst credits are exhausted?
Verify that gp2/gp3 burst credit balance has not been depleted using CloudWatch metrics.
Have you checked whether the instance-side EBS bandwidth (EBS-Optimized) is the bottleneck?
Even raising volume-side IOPS will not produce expected performance if the instance EBS dedicated bandwidth limit has been reached.
Have you checked the CDC cleanup job execution history and errors?
Verify that the cleanup job regularly succeeds and that there are no consecutive failures or skips in the job history.
Have you measured network latency including whether AWS Direct Connect is needed?
Verify that communication latency between on-premises remaining applications and the DB on AWS is not another bottleneck.
Basis and Limitations of This Document
Content verified through real-world operations
This article generalizes Amazon EBS IOPS/throughput specifications, SQL Server CDC and DBCC SHRINKFILE public specifications, and trends commonly observed across multiple similar migration cases. Specific numerical values for compression ratios and cost savings are environment-dependent estimates and do not include specific customer examples or internal issue numbers.
FAQ
Is slow disk I/O after AWS migration always an instance sizing mistake?
Not necessarily. On-premises physical NVMe/SSD and AWS network-attached storage (EBS) have fundamentally different structures, and even selecting an equivalent-spec instance results in performance limits due to IOPS and throughput throttling. First consider designs that account for this structural difference: buffer pool maximization, file separation, and compression.
Does increasing Provisioned IOPS (io2, etc.) solve the problem?
It may solve the problem in some cases, but costs tend to escalate rapidly. We recommend first maximizing the buffer pool to reduce disk reads, then reducing I/O volume with data compression, and only considering IOPS increases if those measures are still insufficient.
Is the same response appropriate when log files are enlarging even though CDC is not in use?
log_reuse_wait_desc becoming REPLICATION can apply to both CDC and transactional replication. First check whether CDC is enabled with sys.databases.is_cdc_enabled. If CDC is not in use, refer to the transaction replication configuration or general log file usage verification procedures.
When is it safe to run DBCC SHRINKFILE?
When log_reuse_wait_desc has become NOTHING. If CDC cleanup delay is the cause, first execute sys.sp_cdc_cleanup_job to make the log releasable. Data file shrinking and log file shrinking have different impacts, so review the related articles before proceeding.
Questions This Document Covers
- I want to know the cause of slowdown after migrating on-premises SQL Server to AWS
- I want to know why storage costs surged after AWS migration
- I want to know how to handle log files that keep growing due to CDC
What the Risk Labels Mean
- Read-onlyDoes not change data or settings.
- LowImpact is limited, but permissions and load should be checked.
- MediumMay affect performance, locking, or cost.
- HighMay cause an outage, data loss, or require recovery work.
- Expert Review RequiredRequires a separate review before applying to production.
GIIP's Scope of Support
Alerts based solely on resource threshold values such as "Disk I/O exceeds 90%" cannot reveal which queries or processes are causing the I/O pressure or how much unnecessary cost is actually being incurred per month. GIIP retains storage I/O, IOPS consumption, CDC cleanup job execution status, and log file changes in the same time series, enabling root causes to be identified on a cost ($) basis. Decisions that are irreversible and high-cost, such as Provisioned IOPS increases, are operated on the basis of having a human confirm the SQL and rollback procedures to be executed beforehand.
Author & Technical Review
GIIP Production Operations Team
About 30 years of experience in designing, migrating, and operating large-scale web services, SQL Server, Oracle, AWS, and Azure. Experience includes 12 sets of x12large-class AWS RDS for SQL Server environments, an Oracle environment with roughly 120,000 tables, and migrating roughly 3TB from TiDB to Aurora MySQL. AI agents and human experts currently continue to monitor and operate multiple cloud databases and about 30 web services.
How to Check Whether CDC Log Scanning Has Stopped in SQL Server
A procedure for checking whether CDC's log scan is progressing, using sys.dm_cdc_log_scan_sessions, cdc.lsn_time_mapping, and the state of the capture job.
sql-serverThe Difference Between DBCC SHRINKDATABASE and DBCC SHRINKFILE, and What to Check Before Running Them
Organizes the difference between SHRINKDATABASE, which targets the whole database, and SHRINKFILE, which targets a single file; when to use TRUNCATEONLY; and what to check before running either.
sql-serverHow to Check Transaction Log Usage on RDS for SQL Server
A read-only procedure that checks log usage with DBCC SQLPERF(LOGSPACE) and sys.dm_db_log_space_usage, and isolates the reason it is not being released using log_reuse_wait_desc.
infrastructureWhat's the Difference Between EBS IOPS and Physical Disk IOPS
A document organizing why EBS IOPS cannot be compared directly to physical disk IOPS, based on where the ceiling applies (volume vs. instance) and how to measure it.
Related Services
Diagnose disk I/O performance after an AWS migration
If you need to repeat the same checks across multiple environments, we can discuss your entire operations setup.
Diagnose disk I/O performance after an AWS migration