{"id":4794,"date":"2017-08-16T08:11:12","date_gmt":"2017-08-16T15:11:12","guid":{"rendered":"http:\/\/3.209.169.194\/blogs\/paul\/?p=4794"},"modified":"2025-03-08T14:46:40","modified_gmt":"2025-03-08T22:46:40","slug":"sqlskills-sql101-readable-secondary-performance-problems","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/","title":{"rendered":"SQLskills SQL101: Readable secondary performance problems"},"content":{"rendered":"<p style=\"text-align: justify;\"><em>As\u00a0<a href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/sqlskills-sql101-stored-procedures\/\" target=\"_blank\" rel=\"noopener noreferrer\">Kimberly blogged about earlier this year<\/a>, SQLskills has an ongoing\u00a0initiative to\u00a0blog about basic topics, which we\u2019re calling SQL101. We\u2019re all blogging about\u00a0things\u00a0that we often see done incorrectly, technologies used the wrong way,\u00a0or where there are many misunderstandings that lead to serious problems. If you want to find all of our\u00a0<strong>SQLskills SQL101<\/strong>\u00a0blog posts, check out\u00a0<a href=\"https:\/\/www.sqlskills.com\/help\/SQL101\" target=\"_blank\" rel=\"noopener noreferrer\">SQLskills.com\/help\/SQL101<\/a>.<\/em><\/p>\n<p style=\"text-align: justify;\">Yesterday I blogged about log shipping performance issues and mentioned a performance problem that can be caused by using availability group readable secondaries, and then realized I hadn&#8217;t blogged about the problem, only described it in our <a href=\"https:\/\/www.sqlskills.com\/join-the-sqlskills-insider-community\/\" target=\"_blank\" rel=\"noopener noreferrer\">Insider newsletter<\/a>. So here&#8217;s a post about it!<\/p>\n<p style=\"text-align: justify;\">Availability groups (AGs) are pretty cool, and one of the most useful features of them is the\u00a0ability to read directly from one of the secondary replicas. Before, with database mirroring, the\u00a0only way to access the mirror database was through the creation of a database snapshot, which\u00a0only gave a single, static view of the data. Readable secondaries are constantly updated from the\u00a0primary so are far more versatile as a reporting or non-production querying platform.<\/p>\n<p style=\"text-align: justify;\">But I bet you didn\u2019t know that using this feature can cause performance problems on your\u00a0primary replica?<\/p>\n<p style=\"text-align: justify;\">As with most things in life, you don\u2019t get anything for free. Readable secondaries are really useful, but there is a performance trade off you need to be aware of. All queries that are executed against a readable secondary are automatically run using snapshot isolation. This means they do not require share locks and so will not block any database changes being replayed from the primary replica (i.e. the constant redo of log records on the secondary replica that have been sent from the primary replica).<\/p>\n<p style=\"text-align: justify;\">To do this requires the use of the versioning system, where (simplistically) pre-change versions\u00a0of records are copied into the version store in tempdb and queries work out which version of the\u00a0record is the correct one for them to process, based on the query\u2019s starting time. All records that\u00a0change get a 14-byte tag added on the end of the record that allows a query to see if this is the\u00a0correct record, and if not to follow a pointer to the previous version of the record in the version\u00a0store. This has been the mechanism since snapshot isolation and read-committed snapshot\u00a0isolation were introduced in SQL Server 2005.<\/p>\n<p style=\"text-align: justify;\">Now consider this: all AG replicas are exact copies of the primary replica. So how can\u00a0versioning work on the readable secondary, adding 14-byte tags to some records? That must\u00a0break the \u2018exact copy\u2019 rule, right?<\/p>\n<p style=\"text-align: justify;\">Well, yes, it would\u2026 if the primary replica didn\u2019t also change.<\/p>\n<p style=\"text-align: justify;\">When a readable secondary is configured in an AG environment, all changing records on the\u00a0primary replica start getting empty 14-byte versioning tags added to them. This is so that the 14-bytes of extra space on the record is noted in the transaction log and replayed on the secondary\u00a0replicas, allowing the readable secondary to make use of the empty 14-byte space to store the\u00a0versioning tag it needs.<\/p>\n<p style=\"text-align: justify;\">This doesn\u2019t break the \u2018exact copy\u2019 rule because the 14-bytes isn\u2019t used for anything to do with\u00a0recovery, there just has to be 14-bytes there.<\/p>\n<p style=\"text-align: justify;\">So versioning tags start getting added to changing records on the primary (to be clear, it doesn\u2019t\u00a0turn on versioning on the primary) so table and index records start to get 14-bytes longer. And\u00a0what happens when records get longer on pages where there isn\u2019t enough space? Page splits in\u00a0your indexes (and forwarded records in heaps \u2013 but I\u2019ll concentrate on indexes here) leading to\u00a0low page densities (wasted disk space and buffer pool memory), logical fragmentation (poor scan performance), and a bunch of extra, expensive log record generation from the page\u00a0splits themselves.<\/p>\n<p style=\"text-align: justify;\">To counteract this, you\u2019ll need to implement (and\/or possibly lower existing) fill factors on your indexes and even potentially start doing index maintenance on indexes that may not have\u00a0required it previously. Quite an insidious problem that can be hard to figure out unless you know\u00a0what\u2019s going on under the covers!<\/p>\n<p style=\"text-align: justify;\">See the following blog posts for more info:<\/p>\n<ul style=\"text-align: justify;\">\n<li><a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/performance-issues-from-wasted-buffer-pool-memory\/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Performance issues from wasted buffer pool memory<\/em><\/a><\/li>\n<li><a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-expensive-are-page-splits-in-terms-of-transaction-log\/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>How expensive are page splits in terms of transaction log?<\/em><\/a><\/li>\n<li><a href=\"http:\/\/sqlmag.com\/blog\/how-choose-good-index-fill-factor\" target=\"_blank\" rel=\"noopener noreferrer\"><em>How to choose a good index fillfactor?<\/em><\/a><\/li>\n<li><a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/easy-automation-of-sql-server-database-maintenance\/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Easy automation of SQL Server database maintenance<\/em><\/a><\/li>\n<\/ul>\n<p style=\"text-align: justify;\">This <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/database-engine\/availability-groups\/windows\/active-secondaries-readable-secondary-replicas-always-on-availability-groups\" target=\"_blank\" rel=\"noopener noreferrer\">MSDN page<\/a> has more general information and\u00a0this whitepaper from Microsoft explains in more depth the various performance impacts from using readable secondaries:\u00a0<em><a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/jj542414.aspx\" target=\"_blank\" rel=\"noopener noreferrer\">AlwaysOn Solution Guide: Offloading Read-Only Workloads to Secondary Replicas<\/a>.<\/em><\/p>\n<p style=\"text-align: justify;\">If you\u2019re implementing readable secondaries in your AG configuration, make\u00a0sure that you also investigate and implement index fill factors in the database so that the\u00a0versioning tags that are added under the covers don\u2019t start causing page splits and fragmentation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As\u00a0Kimberly blogged about earlier this year, SQLskills has an ongoing\u00a0initiative to\u00a0blog about basic topics, which we\u2019re calling SQL101. We\u2019re all blogging about\u00a0things\u00a0that we often see done incorrectly, technologies used the wrong way,\u00a0or where there are many misunderstandings that lead to serious problems. If you want to find all of our\u00a0SQLskills SQL101\u00a0blog posts, check out\u00a0SQLskills.com\/help\/SQL101. Yesterday [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[110,42,82,108],"tags":[],"class_list":["post-4794","post","type-post","status-publish","format-standard","hentry","category-availability-groups","category-fragmentation","category-snapshot-isolation","category-sql101"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>SQLskills SQL101: Readable secondary performance problems - Paul S. Randal<\/title>\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.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SQLskills SQL101: Readable secondary performance problems - Paul S. Randal\" \/>\n<meta property=\"og:description\" content=\"As\u00a0Kimberly blogged about earlier this year, SQLskills has an ongoing\u00a0initiative to\u00a0blog about basic topics, which we\u2019re calling SQL101. We\u2019re all blogging about\u00a0things\u00a0that we often see done incorrectly, technologies used the wrong way,\u00a0or where there are many misunderstandings that lead to serious problems. If you want to find all of our\u00a0SQLskills SQL101\u00a0blog posts, check out\u00a0SQLskills.com\/help\/SQL101. Yesterday [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/\" \/>\n<meta property=\"og:site_name\" content=\"Paul S. Randal\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-16T15:11:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-08T22:46:40+00:00\" \/>\n<meta name=\"author\" content=\"Paul Randal\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Paul Randal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/\",\"name\":\"SQLskills SQL101: Readable secondary performance problems - Paul S. Randal\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#website\"},\"datePublished\":\"2017-08-16T15:11:12+00:00\",\"dateModified\":\"2025-03-08T22:46:40+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SQLskills SQL101: Readable secondary performance problems\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#website\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/\",\"name\":\"Paul S. Randal\",\"description\":\"In Recovery...\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce\",\"name\":\"Paul Randal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g\",\"caption\":\"Paul Randal\"},\"sameAs\":[\"http:\/\/3.209.169.194\/blogs\/paul\"],\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/author\/paul\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"SQLskills SQL101: Readable secondary performance problems - Paul S. Randal","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.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/","og_locale":"en_US","og_type":"article","og_title":"SQLskills SQL101: Readable secondary performance problems - Paul S. Randal","og_description":"As\u00a0Kimberly blogged about earlier this year, SQLskills has an ongoing\u00a0initiative to\u00a0blog about basic topics, which we\u2019re calling SQL101. We\u2019re all blogging about\u00a0things\u00a0that we often see done incorrectly, technologies used the wrong way,\u00a0or where there are many misunderstandings that lead to serious problems. If you want to find all of our\u00a0SQLskills SQL101\u00a0blog posts, check out\u00a0SQLskills.com\/help\/SQL101. Yesterday [&hellip;]","og_url":"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/","og_site_name":"Paul S. Randal","article_published_time":"2017-08-16T15:11:12+00:00","article_modified_time":"2025-03-08T22:46:40+00:00","author":"Paul Randal","twitter_misc":{"Written by":"Paul Randal","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/","name":"SQLskills SQL101: Readable secondary performance problems - Paul S. Randal","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#website"},"datePublished":"2017-08-16T15:11:12+00:00","dateModified":"2025-03-08T22:46:40+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce"},"breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/sqlskills-sql101-readable-secondary-performance-problems\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/paul\/"},{"@type":"ListItem","position":2,"name":"SQLskills SQL101: Readable secondary performance problems"}]},{"@type":"WebSite","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#website","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/","name":"Paul S. Randal","description":"In Recovery...","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqlskills.com\/blogs\/paul\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce","name":"Paul Randal","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g","caption":"Paul Randal"},"sameAs":["http:\/\/3.209.169.194\/blogs\/paul"],"url":"https:\/\/www.sqlskills.com\/blogs\/paul\/author\/paul\/"}]}},"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts\/4794","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/comments?post=4794"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts\/4794\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/media?parent=4794"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/categories?post=4794"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/tags?post=4794"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}