{"id":1041,"date":"2025-06-06T08:00:00","date_gmt":"2025-06-06T13:00:00","guid":{"rendered":"https:\/\/www.sqltabletalk.com\/?p=1041"},"modified":"2025-06-10T09:58:35","modified_gmt":"2025-06-10T14:58:35","slug":"sql-server-indirect-checkpoints-target-recovery-time","status":"publish","type":"post","link":"https:\/\/www.sqltabletalk.com\/?p=1041","title":{"rendered":"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time"},"content":{"rendered":"<h2>Introduction<\/h2>\n<p>A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.<\/p>\n<p>With SQL Server 2012, <strong>indirect checkpoints<\/strong> were introduced. In this case, the dirty page manager manages the dirty page list and keeps track of all the dirty page modifiers of the database. By default, it runs every 60 seconds and tracks the dirty pages that need to be flushed.<\/p>\n<p>For example, if an indirect checkpoint is set to 120 seconds, then individual pages would be flushed around 120 seconds after they were made dirty. This will impact I\/O and may lead to I\/O spikes. I\/O also depends on the number of dirty modifiers lists of the database. We may even see a significant spike in the set target interval and see small hits as the pages change. This eliminates overhead from tracing and flushing the dirty pages, which results in a significant improvement in backup performance.<\/p>\n<p>To view your current target recovery time, you can query <code>sys.databases<\/code>.<\/p>\n<pre><code>SELECT name, target_recovery_time_in_seconds\nFROM sys.databases;<\/code><\/pre>\n<p>On a database where an automatic checkpoint mode is set, tracing the entire buffer pool is necessary to evaluate for possible dirty pages. On the other hand, with an indirect checkpoint, only those pages of the buffer pool that are dirty must be dealt with.<\/p>\n<p>For example, let\u2019s consider a system with 2 TB of memory that contains approximately 250 million buffer units and has 100 dirty pages. Assume that it requires 5 CPU cycles to trace and identify the status of each buffer unit. To traverse 250 million buffer units, it would require 1.25 trillion CPU cycles\u2014and to what? Simply to find 100 positive dirty pages in the automatic checkpoint mode! However, 100 dirty pages are hardly anything with the indirect checkpoint mode. The entire buffer pool scanning time is eliminated.<\/p>\n<h2>How it Works<\/h2>\n<p>When <code>target_recovery_time<\/code> is set at the database level, indirect checkpoint is activated, and a dirty page manager is created for the database. The dirty page manager maintains the dirty page lists (DPList) for the database. Partitioned DPLists help improve concurrency and avoid any contention, with each worker thread running on a scheduler writing to its corresponding dirty page list. Because of this, when the transactional activity on the database is low (&lt;3 k\u20135 k transactions per second), the overhead of tracking dirty pages in DPList is minimal and doesn&#8217;t impact the overall throughput of the workload running on the database with indirect checkpoint.<\/p>\n<p>When the total number of dirty pages exceeds the dirty page threshold (calculated internally from <code>target_recovery_time<\/code>), the recovery writer wakes up, swaps the list, and starts iterating through the DPList in a loop\u2014starting with the longest DPList. It collects the page IDs, sorts the pages in the list to optimize and bundle writes together, and finally posts asynchronous write requests to write the pages.<\/p>\n<p>These are good improvements for the most part; however, in some situations on a busy system, you can still see contention in this process. The recovery writer handles dirty pages by organizing, sorting, and posting writes to optimize database performance. When transactional activity is high, contention can occur between the recovery writer and I\/O threads, leading to slower processing. To maintain recovery time targets, worker threads may step in to assist the recovery writer, ensuring efficient handling of dirty pages despite increased workload. While this strategy helps meet recovery goals, it can temporarily impact overall system performance.<\/p>\n<p>This performance impact may occur because, in some situations, indirect checkpoint can cause uneven distribution of dirty pages\u2014especially in databases like <code>tempdb<\/code>\u2014leading to long dirty page lists. When these lists grow too large, the recovery writer struggles to handle them efficiently, causing contention with I\/O threads and consuming additional CPU resources. In severe cases, this can lead to non-yielding scheduler errors, where the system encounters performance issues due to excessive spinlock contention and delays in processing.<\/p>\n<p>If you see non-yielding schedulers in your environment due to this, you will see the following symptoms in the error log.<\/p>\n<pre><code>Process 0:0:0 (0x34c0) Worker 0x000000FCD89BC160 appears to be non-yielding on Scheduler 9. Thread creation time: 13140343858116. Approx Thread CPU Used: kernel 0 ms, user 60968 ms. Process Utilization 13%. System Idle 86%. Interval: 70234 ms.\n\nA time-out occurred while waiting for buffer latch -- type 3, bp 0000010C1900C6C0, page 4:323520, stat 0x10f, database id: 2, allocation unit Id: 6488064, task 0x000000FE6076D088 : 0, waittime 300 seconds, flags 0x19, owning task 0x00000102ED0C8108. Not continuing to wait.<\/code><\/pre>\n<p>To detect if your SQL Server instance is experiencing excessive spinlock contention on DPList\u2014even if there is no non-yielding scheduler detected\u2014you can use the following diagnostics available in SQL Server.<\/p>\n<h2>Monitoring<\/h2>\n<p>Use <strong><code>sys.dm_os_spinlock_stats<\/code> DMV<\/strong> to look for a high number of spins and backoff events over periods of time. Run the following query multiple times at one-minute intervals. Calculate the difference between snapshots to identify the number of spins and backoffs between consecutive snapshots.<\/p>\n<p><strong>Note:<\/strong> It is expected to see some spins and backoffs due to healthy spinlock contention between the recovery writers and I\/O threads, so you will need to compare different snapshots to see if exponential backoff continues to increase.<\/p>\n<pre><code>SELECT GETDATE() AS [Date Captured], *\nFROM sys.dm_os_spinlock_stats\nWHERE name = 'DP_LIST';<\/code><\/pre>\n<p>You can use the following script to capture an Extended Event for spinlock backoff for DPList in your environment. The call stack captured from the XEvents can be analyzed using <a href=\"https:\/\/github.com\/arvindshmicrosoft\/SQLCallStackResolver\">SQLCallStackResolver<\/a> developed by Arvind Shyamsundar.<\/p>\n<h3>Interpretation Guidelines<\/h3>\n<table>\n<thead>\n<tr>\n<th>Metric<\/th>\n<th>What it means<\/th>\n<th>When to worry<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>collisions<\/strong><\/td>\n<td>Number of times threads tried to acquire the spinlock simultaneously<\/td>\n<td>High values are normal under load<\/td>\n<\/tr>\n<tr>\n<td><strong>spins<\/strong><\/td>\n<td>Total spin attempts<\/td>\n<td>High with low collisions = inefficiency<\/td>\n<\/tr>\n<tr>\n<td><strong>spins_per_collision<\/strong><\/td>\n<td>Avg spins per collision<\/td>\n<td>&gt;1000 may indicate contention<\/td>\n<\/tr>\n<tr>\n<td><strong>backoffs<\/strong><\/td>\n<td>Times a thread gave up spinning<\/td>\n<td>High and increasing = bad contention<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Azure SQL<\/h2>\n<p>All of this is applicable to Azure SQL: SQL Server on Azure VM, Azure SQL Managed Instance (SQL MI), and Azure SQL Database (SQL DB).<\/p>\n<p>Azure SQL Database does support indirect checkpoints, but with some important distinctions compared to SQL Server and Azure SQL Managed Instance.<\/p>\n<ul>\n<li>You cannot directly configure <code>target_recovery_time<\/code> in Azure SQL Database.<\/li>\n<li>Microsoft manages checkpoint behavior internally to optimize for high availability and fast recovery, especially in geo-replicated and hyperscale environments.<\/li>\n<li>You don\u2019t have access to all system-level DMVs like <code>sys.dm_os_spinlock_stats<\/code> or the ability to run custom Extended Events sessions that require elevated privileges.<\/li>\n<li>This limits your ability to diagnose spinlock contention or recovery writer behavior in the same way as on SQL Server or Managed Instance.<\/li>\n<li>If you&#8217;re using Azure SQL Database Hyperscale, the checkpointing mechanism is completely different. It uses page servers and a log service to manage durability and recovery, not traditional buffer pool flushing.<\/li>\n<li><code>tempdb<\/code> is shared and abstracted in Azure SQL Database. You don\u2019t manage it directly, so issues like skewed DPLists or spinlock contention in tempdb are not something you can monitor or tune.<\/li>\n<\/ul>\n<h2>Adjusting the Target Recovery Time<\/h2>\n<p>Raising or lowering the <code>target_recovery_time<\/code> in SQL Server (or Azure SQL Managed Instance) directly affects how aggressively SQL Server flushes dirty pages to disk, which in turn impacts I\/O performance and recovery time after a crash. The default is 60 seconds for most modern SQL Server versions and Azure SQL MI. The default value is appropriate for most workloads.<\/p>\n<h3>Best Practices for Adjusting <code>target_recovery_time<\/code><\/h3>\n<p><strong>1. Generally, leave it alone<\/strong><\/p>\n<p>The default is 60 seconds and works well for most modern SQL Server versions and Azure SQL MI.<\/p>\n<p>If you have a value of 0, this is likely because the database has migrated from an older version of SQL Server and has retained the original value. We generally recommend setting this to 60, then checking for performance improvement, performance degradation, or spinlock contention as described earlier. Larger memory systems\u2014say 500 GB\u2014benefit more by adjusting from 0 to 60.<\/p>\n<p>If you have a value other than 0 or 60, then it is not the default value from older or newer versions of SQL Server and was explicitly set. Investigate why it was set to the current value.<\/p>\n<p><strong>2. Consider the Workload<\/strong><\/p>\n<p>If you must adjust because you have identified one of the symptoms discussed earlier, consider the following suggestions based on your workload type. <strong>Always test with a production-like workload before adjusting <code>target_recovery_time<\/code> in production.<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Workload Type<\/th>\n<th>Suggested Setting<\/th>\n<th>Reason<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>OLTP (high transaction rate)<\/strong><\/td>\n<td><strong>Higher (e.g., 120\u2013300 s)<\/strong><\/td>\n<td>Reduces I\/O pressure and spinlock contention<\/td>\n<\/tr>\n<tr>\n<td><strong>Reporting \/ Read-heavy<\/strong><\/td>\n<td><strong>Higher<\/strong><\/td>\n<td>Less frequent writes; recovery time is less critical<\/td>\n<\/tr>\n<tr>\n<td><strong>Mission-critical \/ HA<\/strong><\/td>\n<td><strong>Lower (e.g., 20\u201360 s)<\/strong><\/td>\n<td>Ensures fast recovery and minimal data loss<\/td>\n<\/tr>\n<tr>\n<td><strong>Batch \/ ETL workloads<\/strong><\/td>\n<td><strong>Higher<\/strong><\/td>\n<td>Checkpoints can be scheduled around batch windows<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>3. Monitor and Adjust Iteratively<\/strong><\/p>\n<p>Make adjustments in small increments. Use <strong><code>sys.dm_db_log_stats<\/code><\/strong> to monitor actual recovery time and checkpoint behavior.<\/p>\n<ul>\n<li>Watch for:\n<ul>\n<li><strong>I\/O spikes<\/strong><\/li>\n<li><strong>Spinlock contention<\/strong> (e.g., DP_LIST)<\/li>\n<li><strong>Non-yielding schedulers<\/strong><\/li>\n<\/ul>\n<\/li>\n<li>Adjust in <strong>small increments<\/strong> (e.g., from 60 s \u2192 120 s) and observe impact.<\/li>\n<\/ul>\n<p><strong>4. Avoid Overly Aggressive Settings<\/strong><\/p>\n<p>Setting it too low (e.g., 10 s) can cause:<\/p>\n<ul>\n<li>Frequent checkpoints<\/li>\n<li>High I\/O load<\/li>\n<li>CPU overhead from recovery writer activity<\/li>\n<\/ul>\n<p><strong>5. Use Different Settings per Database<\/strong><\/p>\n<p>You can set <code>target_recovery_time<\/code> per database. Tune individually based on each database\u2019s role and workload. Again, the default value of 60 seconds works in most cases. Only adjust if you have detected the symptoms above.<\/p>\n<pre><code>ALTER DATABASE [MyDB] SET TARGET_RECOVERY_TIME = 120 SECONDS;<\/code><\/pre>\n<p><strong style=\"color: #d8000c;font-size: 1.0em\">The scripts in this article are for demonstration purposes only. Please test all scripts in a test environment.<\/strong><\/p>\n<h2>Conclusion<\/h2>\n<p>Indirect checkpoints improve how dirty pages are handled, particularly on large-memory machines. In most cases, setting the <code>TARGET_RECOVERY_TIME<\/code> to 60 seconds works for most databases. Review your error log for non-yielding scheduler entries and monitor the DP_LIST spinlock backoff over time.<\/p>\n<h2>Additional Reading<\/h2>\n<ul>\n<li><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/logs\/database-checkpoints-sql-server?view=sql-server-ver16\">Database Checkpoints (SQL Server) \u2013 SQL Server | Microsoft Learn<\/a><\/li>\n<li><a href=\"https:\/\/www.mssqltips.com\/sqlservertip\/7108\/sql-server-indirect-checkpoints-why-enable\/\">Why Enable SQL Server Indirect Checkpoints<\/a><\/li>\n<li><a href=\"https:\/\/sqlperformance.com\/2020\/05\/system-configuration\/0-to-60-switching-to-indirect-checkpoints\">\u201c0 to 60\u201d: Switching to indirect checkpoints \u2013 SQLPerformance.com<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/system-dynamic-management-views\/sys-dm-os-spinlock-stats-transact-sql?view=azuresqldb-current\">sys.dm_os_spinlock_stats (Transact-SQL) \u2013 SQL Server | Microsoft Learn<\/a><\/li>\n<\/ul>\n<p>Thank you to Yvonne Vanslageren for contributing to this article. You are greatly missed.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.<\/p>\n","protected":false},"author":49,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[101,35,12,6,5,13,18],"tags":[562,557,559,558,563,555,561,560,131,556],"class_list":["post-1041","post","type-post","status-publish","format-standard","hentry","category-azure","category-data-integrity","category-internals","category-memory","category-performance","category-storage-engine","category-transaction-logs","tag-azure-sql-checkpoints","tag-buffer-pool-management","tag-checkpoint-performance","tag-dp_list-spinlock","tag-i-o-optimization","tag-indirect-checkpoints","tag-monitoring-and-diagnostics","tag-recovery-writer","tag-sql-server","tag-target-recovery-time"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time - SQL Table Talk<\/title>\n<meta name=\"description\" content=\"A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sqltabletalk.com\/?p=1041\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time - SQL Table Talk\" \/>\n<meta property=\"og:description\" content=\"A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqltabletalk.com\/?p=1041\" \/>\n<meta property=\"og:site_name\" content=\"SQL Table Talk\" \/>\n<meta property=\"article:published_time\" content=\"2025-06-06T13:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-10T14:58:35+00:00\" \/>\n<meta name=\"author\" content=\"Jon Russell\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jon Russell\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.sqltabletalk.com\/?p=1041#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/?p=1041\"},\"author\":{\"name\":\"Jon Russell\",\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/8f5916388cc3b793a960dea33ff1ed86\"},\"headline\":\"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time\",\"datePublished\":\"2025-06-06T13:00:00+00:00\",\"dateModified\":\"2025-06-10T14:58:35+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/?p=1041\"},\"wordCount\":1615,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/1947e42a9438bccd91691d8b791888e0\"},\"keywords\":[\"Azure SQL Checkpoints\",\"Buffer Pool Management\",\"Checkpoint Performance\",\"DP_LIST Spinlock\",\"I\/O Optimization\",\"Indirect Checkpoints\",\"Monitoring and Diagnostics\",\"Recovery Writer\",\"SQL Server\",\"Target Recovery Time\"],\"articleSection\":[\"Azure\",\"Data Integrity\",\"Internals\",\"Memory\",\"Performance\",\"Storage Engine\",\"Transaction Logs\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.sqltabletalk.com\/?p=1041#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqltabletalk.com\/?p=1041\",\"url\":\"https:\/\/www.sqltabletalk.com\/?p=1041\",\"name\":\"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time - SQL Table Talk\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/#website\"},\"datePublished\":\"2025-06-06T13:00:00+00:00\",\"dateModified\":\"2025-06-10T14:58:35+00:00\",\"description\":\"A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/?p=1041#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqltabletalk.com\/?p=1041\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqltabletalk.com\/?p=1041#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqltabletalk.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqltabletalk.com\/#website\",\"url\":\"https:\/\/www.sqltabletalk.com\/\",\"name\":\"SQL Table Talk\",\"description\":\"Breaking Down SQL Server, One Post at a Time.\",\"publisher\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/1947e42a9438bccd91691d8b791888e0\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqltabletalk.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/1947e42a9438bccd91691d8b791888e0\",\"name\":\"Stephen Planck\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/64181114edc3de3d99072c049bcec024f025c9536dc89fc8ff1bac58976ca81e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/64181114edc3de3d99072c049bcec024f025c9536dc89fc8ff1bac58976ca81e?s=96&d=mm&r=g\",\"caption\":\"Stephen Planck\"},\"logo\":{\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/dexterwiki.com\",\"https:\/\/www.linkedin.com\/in\/stephen-planck-4611b692?trk=people-guest_people_search-card&challengeId=AQErf8gbBmcVMwAAAYsyIsxO-0UvU8z7cHrBpZoo_n3xt9qEKpRN5B_jd_LmAMu-OfeArkQ7GDjobJ2uRoQQV35EQdh_rR6kxA&submissionId=09de7067-c335-8e17-40b8-8dc32b60ed6c&challengeSource=AgEcUCw35zpPmAAAAYsyI4vAWhJTV7Nt4vZYKc3V1qiDBpCkKgUvtlOBgYXcE84&challegeType=AgE_wZiTT09IAQAAAYsyI4vDmNvbZIYe6XHju5V2bXVvM3IVxnJslgY&memberId=AgESFTkUShzs_gAAAYsyI4vGYk0Gic1uc5kB6cKOABA26Gw&recognizeDevice=AgHdSZyUSI5CEwAAAYsyI4vKd_koF9JgpsCJShT8QfbK1QMiv8SI\",\"https:\/\/www.youtube.com\/linuxmate\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/8f5916388cc3b793a960dea33ff1ed86\",\"name\":\"Jon Russell\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f35049bc3903cae9c596247b2dfe95c0d9b003dd0c758b9690cc00544216aa7c?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f35049bc3903cae9c596247b2dfe95c0d9b003dd0c758b9690cc00544216aa7c?s=96&d=mm&r=g\",\"caption\":\"Jon Russell\"},\"url\":\"https:\/\/www.sqltabletalk.com\/?author=49\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time - SQL Table Talk","description":"A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.sqltabletalk.com\/?p=1041","og_locale":"en_US","og_type":"article","og_title":"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time - SQL Table Talk","og_description":"A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.","og_url":"https:\/\/www.sqltabletalk.com\/?p=1041","og_site_name":"SQL Table Talk","article_published_time":"2025-06-06T13:00:00+00:00","article_modified_time":"2025-06-10T14:58:35+00:00","author":"Jon Russell","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jon Russell","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.sqltabletalk.com\/?p=1041#article","isPartOf":{"@id":"https:\/\/www.sqltabletalk.com\/?p=1041"},"author":{"name":"Jon Russell","@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/8f5916388cc3b793a960dea33ff1ed86"},"headline":"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time","datePublished":"2025-06-06T13:00:00+00:00","dateModified":"2025-06-10T14:58:35+00:00","mainEntityOfPage":{"@id":"https:\/\/www.sqltabletalk.com\/?p=1041"},"wordCount":1615,"commentCount":1,"publisher":{"@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/1947e42a9438bccd91691d8b791888e0"},"keywords":["Azure SQL Checkpoints","Buffer Pool Management","Checkpoint Performance","DP_LIST Spinlock","I\/O Optimization","Indirect Checkpoints","Monitoring and Diagnostics","Recovery Writer","SQL Server","Target Recovery Time"],"articleSection":["Azure","Data Integrity","Internals","Memory","Performance","Storage Engine","Transaction Logs"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.sqltabletalk.com\/?p=1041#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.sqltabletalk.com\/?p=1041","url":"https:\/\/www.sqltabletalk.com\/?p=1041","name":"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time - SQL Table Talk","isPartOf":{"@id":"https:\/\/www.sqltabletalk.com\/#website"},"datePublished":"2025-06-06T13:00:00+00:00","dateModified":"2025-06-10T14:58:35+00:00","description":"A checkpoint is a background process that writes dirty pages to disk. A checkpoint performs a full scan of the pages in the buffer pool, lists all the dirty pages that are yet to be written to disk, and finally writes those pages to disk. In SQL instances that do not have many dirty pages in the buffer pool, this is a trivial operation. However, with SQL instances that have OLTP databases, use more memory and\/or involve sequential scanning of all pages, the performance of the system could be impacted.","breadcrumb":{"@id":"https:\/\/www.sqltabletalk.com\/?p=1041#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqltabletalk.com\/?p=1041"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqltabletalk.com\/?p=1041#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqltabletalk.com\/"},{"@type":"ListItem","position":2,"name":"Optimizing SQL Server with Indirect Checkpoints and Target Recovery Time"}]},{"@type":"WebSite","@id":"https:\/\/www.sqltabletalk.com\/#website","url":"https:\/\/www.sqltabletalk.com\/","name":"SQL Table Talk","description":"Breaking Down SQL Server, One Post at a Time.","publisher":{"@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/1947e42a9438bccd91691d8b791888e0"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqltabletalk.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/1947e42a9438bccd91691d8b791888e0","name":"Stephen Planck","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/64181114edc3de3d99072c049bcec024f025c9536dc89fc8ff1bac58976ca81e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/64181114edc3de3d99072c049bcec024f025c9536dc89fc8ff1bac58976ca81e?s=96&d=mm&r=g","caption":"Stephen Planck"},"logo":{"@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/dexterwiki.com","https:\/\/www.linkedin.com\/in\/stephen-planck-4611b692?trk=people-guest_people_search-card&challengeId=AQErf8gbBmcVMwAAAYsyIsxO-0UvU8z7cHrBpZoo_n3xt9qEKpRN5B_jd_LmAMu-OfeArkQ7GDjobJ2uRoQQV35EQdh_rR6kxA&submissionId=09de7067-c335-8e17-40b8-8dc32b60ed6c&challengeSource=AgEcUCw35zpPmAAAAYsyI4vAWhJTV7Nt4vZYKc3V1qiDBpCkKgUvtlOBgYXcE84&challegeType=AgE_wZiTT09IAQAAAYsyI4vDmNvbZIYe6XHju5V2bXVvM3IVxnJslgY&memberId=AgESFTkUShzs_gAAAYsyI4vGYk0Gic1uc5kB6cKOABA26Gw&recognizeDevice=AgHdSZyUSI5CEwAAAYsyI4vKd_koF9JgpsCJShT8QfbK1QMiv8SI","https:\/\/www.youtube.com\/linuxmate"]},{"@type":"Person","@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/8f5916388cc3b793a960dea33ff1ed86","name":"Jon Russell","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqltabletalk.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f35049bc3903cae9c596247b2dfe95c0d9b003dd0c758b9690cc00544216aa7c?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f35049bc3903cae9c596247b2dfe95c0d9b003dd0c758b9690cc00544216aa7c?s=96&d=mm&r=g","caption":"Jon Russell"},"url":"https:\/\/www.sqltabletalk.com\/?author=49"}]}},"jetpack_featured_media_url":"","jetpack-related-posts":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=\/wp\/v2\/posts\/1041","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=\/wp\/v2\/users\/49"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1041"}],"version-history":[{"count":26,"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=\/wp\/v2\/posts\/1041\/revisions"}],"predecessor-version":[{"id":1082,"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=\/wp\/v2\/posts\/1041\/revisions\/1082"}],"wp:attachment":[{"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1041"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1041"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqltabletalk.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1041"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}