<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>David Barbarin &#187; maintenance</title>
	<atom:link href="https://blog.developpez.com/mikedavem/ptag/maintenance/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/mikedavem</link>
	<description>MVP DataPlatform - MCM SQL Server</description>
	<lastBuildDate>Thu, 09 Sep 2021 21:19:50 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Building a more robust and efficient statistic maintenance with large tables</title>
		<link>https://blog.developpez.com/mikedavem/p13201/sql-server-vnext/building-a-more-robust-and-efficient-statistic-maintenance-with-large-tables</link>
		<comments>https://blog.developpez.com/mikedavem/p13201/sql-server-vnext/building-a-more-robust-and-efficient-statistic-maintenance-with-large-tables#comments</comments>
		<pubDate>Mon, 26 Oct 2020 21:05:34 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[SQL Server 2017]]></category>
		<category><![CDATA[maintenance]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[rebuild index]]></category>
		<category><![CDATA[update statistic]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/mikedavem/?p=1689</guid>
		<description><![CDATA[In a past, I went to different ways for improving update statistic maintenance in different shops according to their context, requirement and constraints as well as the SQL Server version used at this moment. All are important inputs for creating &#8230; <a href="https://blog.developpez.com/mikedavem/p13201/sql-server-vnext/building-a-more-robust-and-efficient-statistic-maintenance-with-large-tables">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>In a past, I went to different ways for improving update statistic maintenance in different shops according to their context, requirement and constraints as well as the SQL Server version used at this moment. All are important inputs for creating a good maintenance strategy which can be very simple with execution of sp_updatestats or specialized scripts to focus on some tables.  </p>
<p><span id="more-1689"></span></p>
<p>One of my latest experiences on this topic was probably one of the best although we go to circuitous way for dealing with long update statistic maintenance task on a large database. We used a mix of statistic analysis stuff and improvements provided by SQL Server 2014 SP1 CU6 and parallel update statistic capabilities. I wrote a <a href="https://blog.dbi-services.com/experiencing-updating-statistics-on-a-big-table-by-unusual-ways/" rel="noopener" target="_blank">blog post</a> if you are interested in learning more on this experience.</p>
<p>I’m working now for a new company meaning a different context … At the moment of this write-up, we are running on SQL Server 2017 CU21 and database sizes are in different order of magnitude (more than 100GB compressed) compared to my previous experience. However, switching from default sampling method to FULLSCAN for some large tables drastically increased the update statistic task beyond to the allowed Windows time frame (00:00AM to 03:00AM) without any optimization. </p>
<p><strong>Why to change the update statistic sampling method? </strong></p>
<p>Let’s start from the beginning: why we need to change default statistic sample? In fact, this topic has been already covered in detail in the internet and to make the story short, good statistics are part of the recipe for efficient execution plans and queries. Default sampling size used by both auto update mechanism or UPDATE STATISTIC command without any specification come from a <a href="https://docs.microsoft.com/en-us/archive/blogs/srgolla/sql-server-statistics-explained" rel="noopener" target="_blank">non-linear algorithm</a> and may not produce good histogram with large tables. Indeed, the sampling size decreases as the table get bigger leading to a rough picture of values in the table which may affect cardinality estimation in execution plan … Exactly the side effects we experienced on with a couple of our queries and we wanted to minimize in the future. Therefore, we decided to improve cardinality estimation by switching to FULLSCAN method only for some big tables to produce better histogram. But this method comes also at the cost of a direct impact on consumed resources and execution time because the optimizer needs to read more data to build a better picture of data distribution and sometimes with an higher <a href="https://docs.microsoft.com/en-us/sql/t-sql/statements/update-statistics-transact-sql?redirectedfrom=MSDN&amp;view=sql-server-ver15" rel="noopener" target="_blank">tempdb usage</a>. Our first attempt on ACC environment increased the update statistic maintenance task from initially 5min with default sampling size to 3.5 hours with the FULLSCAN method and only for large tables … Obviously an unsatisfactory solution because we were out of the allowed Windows maintenance timeframe. </p>
<p><strong>Context matters</strong></p>
<p>But first let’s set the context a little bit more: The term “large” can be relative according to the environment. In my context, it means tables with more than 100M of rows and less than 100GB in size for the biggest one and 10M of rows and 10GB in size for lower ones. In fact, for partitioned tables total size includes the archive partition’s compression. </p>
<p>Another gusty detail: concerned databases are part of availability groups and maxdop for primary replica was setup to 1. There is a long story behind this value with some side effects encountered in the past when switching to <strong>maxdop &gt; 1 and cost threshold for parallelism = 50</strong>. At certain times of the year, the workload increased a lot and we faced memory allocation issues for some parallel queries (parallel queries usually require more memory). This is something we need to investigate further but we switched back to maxdop=1 for now and I would say so far so good …</p>
<p>Because we don’t really have index structures heavily fragmented between two rebuild index operations, we’re not in favor of frequent rebuilding index operations. Even if such operation can be either done online or is resumable with SQL Server 2017 EE, it remains a very resource intensive operation including log block replication on the underlying Always On infrastructure. In addition, there is a strong commitment of minimizing resource overhead during the Windows maintenance because of concurrent business workload in the same timeframe.  </p>
<p><strong>Options available to speed-up update statistic task</strong></p>
<p> <strong>Using MAXDOP / PERSIST_SAMPLE_PERCENT with UPDATE STATISTICS command</strong></p>
<p><a href="https://support.microsoft.com/en-us/help/4041809/kb4041809-update-adds-support-for-maxdop-for-create-statistics-and-upd" rel="noopener" target="_blank">KB4041809</a> describes new support added for MAXDOP option for the CREATE STATISTICS and UPDATE STATISTICS statements in Microsoft SQL Server 2014, 2016 and 2017. This is especially helpful to override MAXDOP settings defined at the server or database-scope level. As a reminder, maxdop value is forced to 1 in our context on availability group primary replicas. </p>
<p>For partitioned tables we don’t go through this setting because update statistic is done at partition level (see next section).  The concerned tables own 2 partitions, respectively CURRENT and ARCHIVE. We keep the former small in size and with a relative low number of rows (only last 2 weeks of data). Therefore, there is no real benefit of using MAXDOP to force update statistics to run with parallelism in this case.</p>
<p>But non-partitioned large tables (&gt;=10 GB) are good candidate. According to the following picture, we noticed an execution time reduction of 57% by increasing maxdop value to 4 for some large tables with these specifications:<br />
&#8211;	~= 10GB<br />
&#8211;	~ 11M rows<br />
&#8211;	112 columns<br />
&#8211;	71 statistics</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-11-maxdop-nonpartitioned-tables.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-11-maxdop-nonpartitioned-tables.jpg" alt="168 - 11 - maxdop - nonpartitioned tables" width="481" height="289" class="alignnone size-full wp-image-1691" /></a></p>
<p>Another feature we went through is described in <a href="https://support.microsoft.com/en-us/help/4039284/kb4039284-enhancement-new-keyword-is-added-to-create-and-update-statis" rel="noopener" target="_blank">KB4039284</a> and available since with SQL Server 2016+.  In our context, the maintenance of statistics relies on a custom stored procedure (not Ola maintenance scripts yet) and we have configured default sampling rate method for all statistics and we wanted to make exception only for targeted large tables. In the past, we had to use <a href="https://docs.microsoft.com/en-us/sql/t-sql/statements/update-statistics-transact-sql?view=sql-server-ver15" rel="noopener" target="_blank">NO_RECOMPUTE</a> option to exclude statistics for automatic updates. The new PERSIST_SAMPLE_PERCENT option indicates SQL Server to lock the sampling rate for future update operations and we are using it for non-partitioned large tables. </p>
<p> <strong>Incremental statistics</strong></p>
<p>SQL Server 2017 provides interesting options to reduce maintenance overhead. Surprisingly some large tables were already partitioned but no incremental statistics were configured. Incremental statistics are especially useful for tables where only few partitions are changed at a time and are a great feature to improve efficiency of statistic maintenance because operations are done at the partition level since SQL Server 2014. Another <a href="https://blog.dbi-services.com/sql-server-2014-new-incremental-statistics/" rel="noopener" target="_blank">blog post</a> written a couple of years ago and here was a great opportunity to apply theorical concepts to a practical use case. Because we already implemented partition-level maintenance for indexes, it made sense to apply the same method for statistics to minimize overhead with FULLSCAN method and to benefit from statistic update threshold at the partition level. As said in the previous section, partitioned tables own 2 partitions CURRENT (last 2 weeks) and ARCHIVE and the goal was to only update statistics on the CURRENT partition on daily basis. However, let’s precise that although statistic objects are managed are the partition level, the SQL Server optimizer is not able to use them directly (no change since SQL Server 2014 to SQL Server 2019 as far as I know) and refers instead to the global statistic object.</p>
<p>Let’s demonstrate with the following example:</p>
<p>Let&rsquo;s consider BIG TABLE with 2 partitions for CURRENT (last 2 weeks) and ARCHIVE values as shown below:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT <br />
&nbsp; &nbsp; s.object_id,<br />
&nbsp; &nbsp; s.name AS stat_name,<br />
&nbsp; &nbsp; sp.rows,<br />
&nbsp; &nbsp; sp.rows_sampled,<br />
&nbsp; &nbsp; sp.node_id,<br />
&nbsp; &nbsp; sp.left_boundary,<br />
&nbsp; &nbsp; sp.right_boundary,<br />
&nbsp; &nbsp; sp.partition_number<br />
FROM sys.stats AS s<br />
CROSS APPLY sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) AS sp<br />
WHERE s.object_id = OBJECT_ID('[dbo].[BIG TABLE]')<br />
AND s.name = 'XXXX_OID'</div></div>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-2-Stats-Partition-e1603745274341.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-2-Stats-Partition-e1603745274341.jpg" alt="168 - 2 - Stats Partition" width="800" height="113" class="alignnone size-full wp-image-1692" /></a></p>
<p>Statistic object is incremental, and we got an internal picture of per-partition statistics and the global one. You need to enable trace flag 2309 and to add node id reference to the DBCC SHOW_STATISTICS command as well.  Let’s dig into the ARCHIVE partition to find a specific value within the histogram step:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DBCC TRACEON ( 2309 );<br />
GO<br />
DBCC SHOW_STATISTICS('[dbo].[BIG TABLE]', 'XXX_OID', 7) WITH HISTOGRAM;</div></div>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-3-histogram-partition-1.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-3-histogram-partition-1.jpg" alt="168 - 3 - histogram partition 1" width="825" height="157" class="alignnone size-full wp-image-1693" /></a></p>
<p>Then, I used the value 9246258 in the WHERE clause of the following query:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT *<br />
FROM dbo.[BIG TABLE]<br />
WHERE XXXX_OID = 9246258</div></div>
<p>It gives an estimated cardinality of 37.689 rows as show below …</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-4-query.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-4-query.jpg" alt="168 - 4 - query" width="614" height="186" class="alignnone size-full wp-image-1694" /></a></p>
<p>… Cardinality estimation is 37.689 while we should expect a value of 12 rows here referring to the statistic histogram above. Let’s now have a look at the global statistic (nodeid = 1):</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DBCC SHOW_STATISTICS('[dbo].[BIG TABLE]', 'XXX_OID', 1) WITH HISTOGRAM;</div></div>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-5-histogram-partition-global.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-5-histogram-partition-global.jpg" alt="168 - 5 - histogram partition global" width="822" height="139" class="alignnone size-full wp-image-1695" /></a></p>
<p>In fact, the query optimizer estimates rows by using AVG_RANGE_ROWS value between 9189129 and 9473685 in the global statistic. Well, it is likely not as perfect as we may expect. Incremental statistics do helps in reducing time taken to gather stats for sure, but it may not be enough to represent the entire data distribution in the table – We are still limited to 200 steps in the global statistic object. Pragmatically, I think we may mitigate this point by saying things could be worst somehow if we need either to use default sample algorithm or to decrease the sample size of your update statistic operation. </p>
<p>Let’s illustrate with the BIG TABLE. To keep things simple, I have voluntary chosen a (real) statistic where data is evenly distributed.  Here some pictures of real data distribution:</p>
<p>The first one is a simple view of MIN, MAX boundaries as well as AVG of occurrences (let’s say duplicate records for a better understanding) by distinct value:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-6-nb_occurences_per_value.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-6-nb_occurences_per_value.jpg" alt="168 - 6 - nb_occurences_per_value" width="457" height="104" class="alignnone size-full wp-image-1696" /></a></p>
<p>Referring to the picture above, we may notice there is no high variation of number of occurrences per distinct value represented by the leading XXX_OID column in the related index. In the picture below, another representation of data distribution where each histogram bucket includes the number of distinct values per number of occurrences. </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-10-histogram_per_nb-occurences.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-10-histogram_per_nb-occurences.jpg" alt="168 - 10 - histogram_per_nb occurences" width="481" height="289" class="alignnone size-full wp-image-1697" /></a></p>
<p>For example, we have roughly 2.3% of distinct values in the BIG TABLE with 29 duplicate records. The same applies for values 28, 31 and so on … In short, this histogram confirms a certain degree of homogeneity of data distribution and avg_occurences value is not so far from the truth.</p>
<p>Let’s using default sample value for UPDATE STATISTICS. A very low sample of rows are taken into account leading to very approximative statistics as show below:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT <br />
&nbsp; &nbsp; rows,<br />
&nbsp; &nbsp; rows_sampled,<br />
&nbsp; &nbsp; CAST(rows_sampled * 100. / rows AS DECIMAL(5,2)) AS [sample_%],<br />
&nbsp; &nbsp; steps<br />
FROM sys.dm_db_stats_properties(OBJECT_ID('[dbo].[BIG TABLE]), 1)</div></div>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-7-default_sample_value.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-7-default_sample_value.jpg" alt="168 - 7 - default_sample_value" width="421" height="56" class="alignnone size-full wp-image-1699" /></a></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT *<br />
FROM sys.dm_db_stats_histogram(OBJECT_ID('[dbo].[BIG TABLE]), 1)</div></div>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-8-default_sample_histogram-e1603745718861.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-8-default_sample_histogram-e1603745718861.jpg" alt="168 - 8 - default_sample_histogram" width="800" height="218" class="alignnone size-full wp-image-1700" /></a></p>
<p>Focusing on average_range_rows colum values, we may notice estimation is not representative of real distribution in the BIG TABLE. </p>
<p>After running FULLSCAN method with UPDATE STATISTICS command, the story has changed, and estimation is now closer to the reality:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-9-fullscan_histogram-e1603745769635.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-9-fullscan_histogram-e1603745769635.jpg" alt="168 - 9 - fullscan_histogram" width="800" height="255" class="alignnone size-full wp-image-1701" /></a></p>
<p>As a side note, one additional benefit of using FULLSCAN method is to get a representative statistic histogram in fewer steps. This is well-explained in the SQL Tiger team&rsquo;s <a href="https://docs.microsoft.com/en-us/archive/blogs/sql_server_team/perfect-statistics-histogram-in-just-few-steps" rel="noopener" target="_blank">blog post</a> and we noticed this specific behavior with some statistic histograms where frequency is low … mainly primary key and unique index related statistics.</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-1-statistic-histogram-before-after-e1603745894373.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-1-statistic-histogram-before-after-e1603745894373.jpg" alt="168 - 1 - statistic histogram before after" width="800" height="196" class="alignnone size-full wp-image-1702" /></a></p>
<p><strong>How benefit was incremental statistic? </strong></p>
<p>The picture below refers to one of our biggest partitioned large table with the following characteristics:<br />
&#8211;	~ 410M rows<br />
&#8211;	~ 63GB in size (including compressed partition size)<br />
&#8211;	67 columns<br />
&#8211;	30 statistics </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/10/168-12-maxdop-partitioned-tables.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/10/168-12-maxdop-partitioned-tables.jpg" alt="168 - 12 - maxdop - partitioned tables" width="738" height="289" class="alignnone size-full wp-image-1703" /></a></p>
<p>As noticed in the picture above, overriding maxdop setting at the database-scoped level resulted to an interesting drop in execution time when FULLSCAN method is used (from 03h30 to 17s in the best case)<br />
Similarly, combining efforts done for both non-partitioned and partitioned larges tables resulted to reduced execution time of update statistic task from ~ 03h30 to 15min – 30min in production that is a better fit with our requirements. </p>
<p>Going through more sophisticated process to update statistic may seem more complicated but strongly required in some specific scenarios. Fortunately, SQL Server provides different features to help optimizing this process. I’m looking forward to seeing features that will be shipped with next versions of SQL Server.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>SQL Server 2012 AlwaysOn et plans de maintenance par d&#233;faut</title>
		<link>https://blog.developpez.com/mikedavem/p12043/sql-server-2012/sql-server-2012-alwayson-et-plans-de-maintenance-par-dfaut</link>
		<comments>https://blog.developpez.com/mikedavem/p12043/sql-server-2012/sql-server-2012-alwayson-et-plans-de-maintenance-par-dfaut#comments</comments>
		<pubDate>Thu, 13 Jun 2013 14:26:38 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[SQL Server 2012]]></category>
		<category><![CDATA[AlwaysOn]]></category>
		<category><![CDATA[maintenance]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/mikedavem/?p=550</guid>
		<description><![CDATA[Lors de ma session GUSS du 23 avril 2013 à propos de AlwaysOn on m&#8217;a demandé si les plans de maintenance SQL Server pouvaient être utilisés dans ce contexte. J&#8217;ai répondu négativement à cette réponse sans pour autant être vraiment &#8230; <a href="https://blog.developpez.com/mikedavem/p12043/sql-server-2012/sql-server-2012-alwayson-et-plans-de-maintenance-par-dfaut">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Lors de ma session <a href="http://guss.fr/2013/06/01/webcast-guss-avril-2013-les-bases-alwayson/">GUSS</a> du 23 avril 2013 à propos de AlwaysOn on m&rsquo;a demandé si les plans de maintenance SQL Server pouvaient être utilisés dans ce contexte. J&rsquo;ai répondu négativement à cette réponse sans pour autant être vraiment explicite. Pourtant lorsqu&rsquo;on créé un plan de maintenance avec SQL Server 2012 et qu&rsquo;on utilise une tâche de sauvegarde des bases on peut s&rsquo;apercevoir que si une base de données est concernée par un groupe de disponibilité une option apparait automatiquement <em>&quot;For availability databases, ignore Replica Priority for Backup and Backup on Primary&#160; Settings</em>&quot;. L&rsquo;apparition de cette option implique que par défaut les plans de maintenance SQL Server ou du mois les tâches de sauvegarde sont en théorie capables de s&rsquo;aligner sur la politique de sauvegarde dictée par le groupe de disponibilité en question mais malgré ce je persiste à dire que les plans de maintenance par défaut ne sont pas adaptés (du moins encore) aux environnements AlwaysOn et c&rsquo;est ce que je vais tenter d&rsquo;expliquer dans ce billet.</p>
<p>&#160;</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/icon_arrow2.gif"><img title="icon_arrow" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="icon_arrow" src="http://blog.developpez.com/mikedavem/files/2013/06/icon_arrow_thumb2.gif" width="15" height="15" /></a>&#160;<strong><u>Des tâches de sauvegardes qui gèrent et prennent en compte les préférences de sauvegarde d&rsquo;un groupe de disponibilité</u></strong></p>
<p>Commençons par le début. Effectivement les tâches de sauvegardes des plans de maintenance par défaut détectent l&rsquo;appartenance ou non d&rsquo;une base de données à un groupe de disponibilité. </p>
<p>J&rsquo;ai créé une base de données DUMMY faisait parti du groupe de disponibilité </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/image4.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="http://blog.developpez.com/mikedavem/files/2013/06/image_thumb4.png" width="244" height="250" /></a></p>
<p>&#160;</p>
<p>J&rsquo;ai paramétré mes préférences de sauvegarde de la manière suivante :</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/image5.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="http://blog.developpez.com/mikedavem/files/2013/06/image_thumb5.png" width="443" height="288" /></a></p>
<p>&#160;</p>
<p>Dans un plan de maintenance par défaut je glisse une tâche de sauvegarde de type FULL en choisissant la base de données DUMMY &#8230;</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/image6.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="http://blog.developpez.com/mikedavem/files/2013/06/image_thumb6.png" width="426" height="359" /></a></p>
<p>&#160;</p>
<p>&#8230; et on s&rsquo;aperçoit qu&rsquo;une option supplémentaire apparait </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/image7.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="http://blog.developpez.com/mikedavem/files/2013/06/image_thumb7.png" width="515" height="230" /></a></p>
<p>&#160;</p>
<p>Cette option permet de ne pas tenir compte des préférences de sauvegarde configurés dans les groupes de disponibilités pour un réplica. Par défaut cette option n&rsquo;est pas activée et la tâche de sauvegarde prendra en compte les préférences de sauvegardes des groupes de disponibilités (de préférence sur les secondaires, uniquement sur les secondaires, uniquement sur le primaire, sur n&rsquo;importe quel réplica). </p>
<p>On peut voir que la commande T-SQL générée utilise la fonction sys.fn_hadr_backup_is_preferred_replica() pour la base de données DUMMY</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/image8.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="http://blog.developpez.com/mikedavem/files/2013/06/image_thumb8.png" width="451" height="351" /></a></p>
<p>&#160;</p>
<p>Notez également que j&rsquo;ai activé l&rsquo;option COPY_ONLY pour les sauvegardes. En effet comme ma politique de sauvegarde stipule que les réplicas secondaires seront utilisés en priorité, seules les sauvegardes avec l&rsquo;option COPY_ONLY fonctionneront si le réplica concerné sera secondaire au moment de la sauvegarde. Mon plan de maintenance fonctionne parfaitement même après test de failover d&rsquo;un réplica vers un autre. Cependant comme vous le savez certainement un plan de maintenance n&rsquo;est jamais aussi simple. </p>
<p>Restons sur notre tâche de sauvegarde et ajoutons-y quelques bases de données utilisateurs en mode de récupération FULL et qui ne font pas parti d&rsquo;un groupe de disponibilité et c&rsquo;est la que le premier problème arrive. Ma politique de sauvegarde de type full se fait toujours avec l&rsquo;option COPY_ONLY dans mon cas puisque mes sauvegardes se feront en priorité sur le secondaire et que ceux-ci ne supportent que les sauvegardes avec l&rsquo;option COPY_ONLY. Le problème est qu&rsquo;ici la création d&rsquo;une base de données utilisateur en mode de récupération FULL fera planté la tâche de sauvegarde des journaux de transactions. Pourquoi ? Tout simplement parce qu&rsquo;une sauvegarde FULL avec l&rsquo;option COPY_ONLY ne va pas initialiser la séquence des LSN utiles pour nos sauvegardes des journaux de transactions. Alors bien entendu je peux créer 2 plans de maintenance par exemple :</p>
<ul>
<li>Un plan de maintenance qui concernera uniquement les bases de données en mode de récupération FULL associées à un groupe de disponibilité </li>
<li>Un plan de maintenance qui concernera les autres bases de données </li>
</ul>
<p>Il faudra dans ce cas répartir manuellement les bases de données dans les différents plans de maintenance. On perd ici toute notion de gestion dynamique des plans de maintenance selon moi. Si j&rsquo;ajoute une base de données par exemple, je n&rsquo;ai pas forcément envie de modifier ensuite mon plan de maintenance &#8230; Bien entendu ce raisonnement ici ne tient que pour les cas où les sauvegardes sont déportés sur le secondaire. Si les sauvegardes sont effectuées uniquement sur le primaire, l&rsquo;utilisation de l&rsquo;option COPY_ONLY n&rsquo;a plus lieu d&rsquo;être. </p>
<p>Une autre chose à noter ici est le manque de support de l&rsquo;option CHECKSUM qui permet pour rappel d&rsquo;une part de vérifier le checksum de chaque page de données sauvegardée d&rsquo;une base de données (si l&rsquo;option est bien entendu activée au niveau de celle-ci) et de générer un checksum global pour le média de sauvegarde. </p>
<p>&#160;</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/icon_arrow3.gif"><img title="icon_arrow" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;margin: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="icon_arrow" src="http://blog.developpez.com/mikedavem/files/2013/06/icon_arrow_thumb3.gif" width="15" height="15" /></a>&#160;<strong><u>La tâche de vérification d&rsquo;intégrité</u></strong> </p>
<p>Vérifier l&rsquo;intégrité de ses bases de données avant de les sauvegarder est plutôt une opération recommandée. Mais dans une architecture AlwaysOn on peut se poser la question de savoir où est-ce que je dois lancer cette tâche. Si dans d&rsquo;autres environnements le choix est plutôt évident car les sauvegardes ne peuvent se faire que sur le serveur &quot;principal&quot; , le raisonnement n&rsquo;est pas tout à fait le même quand on parle de déplacement des sauvegardes sur un réplica secondaire. En effet, vu que ma politique de sauvegarde gère des préférences et des priorités de réplicas pour les sauvegardes ces dernières peuvent être lancés de n&rsquo;importe où en fonction de la situation présente et des paramètres de configuration. En plus mes bases de données sont accessibles en lecture écriture depuis un réplica primaire et peuvent être inaccessible depuis mes les réplicas secondaires. Il faut également que je vérifie l&rsquo;intégrité de ma base à ce niveau. Cela fait potentiellement 2 points de vérification d&rsquo;intégrité des bases sur n réplicas (n pouvant aller jusqu&rsquo;à 5 pour le moment mais avec SQL14 on pourra monter jusqu&rsquo;à 8 !). </p>
<p>Si on regarde de plus près la tâche de vérification d&rsquo;intégrité des plans de maintenance par défaut de SQL Server on s&rsquo;aperçoit qu&rsquo;il n&rsquo;y a aucun paramètre de gestion des groupes de disponibilités : </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/image9.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="http://blog.developpez.com/mikedavem/files/2013/06/image_thumb9.png" width="393" height="488" /></a></p>
<p>&#160;</p>
<p>Dans ce cas comment puis-je faire pour lancer ma tâche ? On peut prendre le cas extrême qui dit qu&rsquo;il faut lancer cette tâche de vérification des bases sur tous les réplicas .. et pourquoi pas après tout ? </p>
<p>Je vois déjà 2 problèmes à cela :</p>
<ul>
<li>Même si la vérification d&rsquo;intégrité initiée par la commande DBCC CHECKDB ne génère pas de verrou par défaut (utilisation des snapshots de bases de données) celle-ci peut avoir un impact négatif sur les performances IO et sur l&rsquo;augmentation de la REDO queue sur les réplicas secondaires. Une augmentation de cette file d&rsquo;attente sur les secondaires peuvent avoir un impact sur le RPO et RTO en fonction du type de réplication choisi. </li>
<li>On peut tout à fait utiliser un réplica secondaire pour faire des sauvegardes et second réplica secondaire en standby uniquement pour effectuer du failover ou dans une procédure de disaster recovery. Dans ce cas la tâche de vérification d&rsquo;intégrité des bases des plans de maintenance par défaut SQL Server étant incapable de savoir si la base de données concernée fait parti ou non d&rsquo;un groupe de disponibilité et si la base de données en question est accessible en lecture seule ou non provoquera une erreur dans le plan de maintenance en fonction de la situation </li>
</ul>
<p>&#160;</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2013/06/icon_arrow4.gif"><img title="icon_arrow" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;margin: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="icon_arrow" src="http://blog.developpez.com/mikedavem/files/2013/06/icon_arrow_thumb4.gif" width="15" height="15" /></a>&#160;<strong><u>Les tâches de mise à jour des statistiques et des indexes</u></strong></p>
<p>On a exactement le même problème ici. En regardant les tâches du plan de maintenance associées aux statistiques et aux indexes il n&rsquo;y a aucun paramètre de configuration lié aux groupes de disponibilité. Dans une architecture AlwaysOn on ne peut mettre à jour les indexes et les statistiques&#160; d&rsquo;une base que sur le réplica primaire. Je peux également avoir des bases de données qui ne sont pas concernées par un groupe de disponibilité. Dans ce cas il est tout à fait possible qu&rsquo;un réplica secondaire d&rsquo;un groupe de disponibilité soit l&rsquo;instance principale d&rsquo;une autre base de données. Je peux effectivement créer un plan de maintenance spécifique pour cela et activer / désactiver les jobs SQL Server associées en fonction du cas mais encore une fois cela m&rsquo;oblige à gérer manuellement les bases de données à répartir dans les plans de maintenance &#8230;</p>
<p>De plus dans ce genre d&rsquo;architecture l&rsquo;utilisation des tâches par défaut de maintenance des indexes ne sont pas forcément conseillés car elles peuvent être excréments couteuse en écriture journalisée et de bande passante pour la réplication de ces écritures vers les autres réplicas. En effet, et beaucoup d&rsquo;articles l&rsquo;expliquent déjà, il n&rsquo;y a pas la possibilité de définir des seuils de réorganisation ou de reconstruction d&rsquo;indexes. La politique du tout ou rien ici n&rsquo;est pas forcément très approprié.</p>
<p>&#160;</p>
<p>Pas besoin d&rsquo;aller plus loin je pense pour vous dire que les plans de maintenance SQL Server ne sont pas adaptés pour SQL Server 2012 AlwaysOn. Que fais-t-on dans ce cas là ? Pour le moment pour ma part j&rsquo;ai créé un jeu de script de maintenance des bases de données pour des architectures AlwaysOn installées chez nos clients. Je n&rsquo;ai pour le moment pas trouvé d&rsquo;outil tiers permettant de gérer la maintenance des bases de données de A à Z pour ce type d&rsquo;environnement mais je reste à l&rsquo;écoute si jamais vous avez plus d&rsquo;information là dessus <img class="wlEmoticon wlEmoticon-smile" style="border-top-style: none;border-left-style: none;border-bottom-style: none;border-right-style: none" alt="Sourire" src="http://blog.developpez.com/mikedavem/files/2013/06/wlEmoticon-smile.png" /></p>
<p>&#160;</p>
<p>Bonne maintenance de vos architectures AlwaysOn !</p>
<p>&#160;</p>
<p>David BARBARIN (Mikedavem)    <br />MVP SQL Server</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
