Data and functionality
- Row counts and checksums on critical tables match (see the cutover runbook).
- Logins map to users (no orphaned users):
EXEC sp_change_users_login 'Report'(older) or compare SIDs insys.database_principalsandsys.server_principals. - Jobs, alerts and operators present and scheduled (SQL Agent, Elastic Jobs, or RDS-specific jobs).
- Linked servers, DB Mail, CLR and certificates work.
- App and integration tests passed; month-end and batch jobs tested once.
Keep plans stable first
- Leave the compatibility level as it was on the source at first.
- Turn on Query Store (on by default in Azure SQL) in read-write mode.
- Update statistics after the migration:
EXEC sp_updatestats; -- or Ola Hallengren IndexOptimize with @UpdateStatistics = 'ALL'
Raise the compatibility level safely
- Capture a baseline in Query Store under the old level for a few days.
- Change the level:
ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 160; - Watch Regressed Queries in Query Store; force the old plan where needed:
EXEC sp_query_store_force_plan @query_id = 4123, @plan_id = 88;
- Or use automatic plan correction (Azure SQL / SQL Server 2017+):
ALTER DATABASE CURRENT SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN = ON);
Common cloud performance gaps
| Symptom | Likely cause | Fix |
|---|---|---|
Slow writes, WRITELOG waits | Log rate limit of the tier / disk throughput | Bigger tier, Business Critical, faster disk (gp3/io2, Premium SSD v2) |
PAGEIOLATCH waits | IO latency higher than on-premises SAN | More memory (buffer pool), more IOPS, better indexes |
| Chatty apps slow | Network latency app ↔ DB (app still on-premises) | Move the app too, batch calls, or put them in the same region/zone |
| tempdb contention | Smaller tempdb on PaaS tier | Larger tier; reduce temp table use |
| CPU high after cutover | Plan changes, missing stats | Update stats, Query Store plan forcing |
SELECT TOP 10 wait_type, wait_time_ms / 1000 AS wait_s
FROM sys.dm_os_wait_stats
WHERE wait_type NOT LIKE 'SLEEP%' AND wait_type NOT IN ('BROKER_TASK_STOP','XE_TIMER_EVENT','SQLTRACE_INCREMENTAL_FLUSH_SLEEP')
ORDER BY wait_time_ms DESC;
Operations
- Monitoring: Azure SQL (database watcher, Azure Monitor) / RDS (CloudWatch Database Insights) with alerts.
- Backups: retention and PITR set; a restore tested.
- HA/DR as designed (zone redundancy, failover group, Multi-AZ) and tested.
- Cost: right-size after 2–4 weeks of real usage; reservations once stable.
Tip
Most "the cloud is slower" complaints in the first week are network latency between an app that stayed on-premises and the database that moved. Measure the round trip before tuning SQL.