プライマリにデータベースがありますDC約15 TBパーティションテーブルは最大で約7 TBです。
ユーザーがLS設定を報告しないようにするために、上記のデータベースに対して異なるDC LS復元ジョブが毎晩実行される場所で行われました。
primayの場合-統計の更新と統計の更新AYNCは両方ともTrueに設定されているため、セカンダリに反映されます。なぜ誰かが両方を有効にしておいたのかはわかりません。
今度はセカンダリで、ユーザーからSELECTクエリを実行すると、その読み取り専用データベースの統計を自動更新するためのselect statmanステートメントが発生すると、多くのブロッキングが発生します。
自動更新非同期が有効になっているとなぜ存在するのかわかりません。
また、プライマリ(週に1回)で行われた更新統計は、LS復元中に復元されますか?そうでない場合、LSスタンバイモードに関与するセカンダリデータベースで統計の更新を実行するためのより良い方法は何ですか?選択したパフォーマンスを向上させるために毎日実行することでしょうか?
お知らせ下さい
TL; DR
読み取り専用データベースでは、通常の永続的統計を利用してクエリプランを満たすことができます。 SQLサーバーが読み取り専用データベースの新しい統計を必要とする場合、またはデータベース内のこれらの永続的な統計がstaleになった場合、temporary統計を作成/更新できます。これらの統計はTempDB
にあり、SQL Serverによって管理されます(削除のみ可能です)。
一時的な統計には2種類あります。統計が不足しているために作成されるものと、「更新」されるものです。
プライマリデータベースに統計を手動で追加するか、レポートクエリの推定実行計画を生成することにより、一時的な統計の作成を削除できます(プライマリインスタンス/ db)でも。詳細については、この回答で詳しく説明します。
永続的な統計読み取り専用データベースで、更新して一時的な統計に「変換」できます。
一時的な統計情報の更新は、プライマリデータベースの統計情報をより頻繁にpdatingまたはdisablingauto stat updateのいずれかで解決できます。ログの復元が行われた後の読み取り専用データベース。
自動更新統計を無効にするには、読み取り専用データベースでこれを実行します。ALTER DATABASE [Database] SET AUTO_UPDATE_STATISTICS OFF;
これにより、この読み取り専用データベースでの一時的な統計の更新が停止します。
スタンバイデータベースへの復元と一時的な統計に関するもう1つの重要な点は、ログバックアップを適用すると、一時的な統計がsys.statsにまだ存在していても、再度更新されることです。
あなたのケースではStatman
クエリで毎日の問題を説明しています(作成/更新が必要な場合は、一時的な統計を毎日再計算します)。
Norecovery->スタンバイ-> Norecovery ...一時的な統計を削除します
一時的な統計に関するもう1つの興味深い部分は、データベースの状態がRESTORE DATABASE ... WITH NORECOVERY
を使用した復元に変化すると失われることです。
use MASTER
GO
RESTORE DATABASE [ReadOnly2] with NORECOVERY
RESTORE DATABASE [ReadOnly2] WITH STANDBY = 'D:\temp\ReadOnly_Standby.bak'
すべてのオブジェクトの一時統計を効果的にフラッシュする
SELECT * From sys.stats where is_temporary = 1;
各状態変化とテストクエリの実行の間で同じ2つの統計を再計算します。
これらの永続的な統計はどちらも、実行計画のxmlに表示されます
<StatisticsInfo Database="[ReadOnly2]" Schema="[dbo]" Table="[Bla]" Statistics="[IX_Bla_indexedval]" ModificationCount="12000000" SamplingPercent="15.8812" LastUpdate="2019-06-12T10:52:32.25" />
<StatisticsInfo Database="[ReadOnly2]" Schema="[dbo]" Table="[Bla]" Statistics="[PK__Bla__3214EC075017BD54]" ModificationCount="12000000" SamplingPercent="15.2345" LastUpdate="2019-06-12T10:52:35.34" />
modificationcount
、samplingpercent
&lastupdate
は、一時的な統計を「更新」してクエリを再度実行した後に変更されました。
ModificationCount="0" SamplingPercent="5.71018" LastUpdate="2019-06-13T11:32:36.5"
一時的な統計の作成
定期的で非一時的な統計は更新されません/読み取り専用データベースの統計は(一時的でさえも)更新できません。
レポートインスタンスで表示されているのは、一時的な統計の作成/「更新」です。
これらの統計情報はTempDBとSQL Serverに存在し、それらを作成および更新します。
動作の複製
読み取り専用データベースの1億行のテーブルで一時的な統計作成の動作を再現できました(補遺#1)
あなたが言及した厄介なStatManクエリで。
SELECT StatMan([SC0], [SB0000]) FROM (SELECT TOP 100 PERCENT [SC0], step_direction([SC0]) over (order by NULL) AS [SB0000] FROM (SELECT [NonIndexedVal] AS [SC0] FROM [dbo].[Bla] TABLESAMPLE SYSTEM (7.707678e-001 PERCENT) WITH (READUNCOMMITTED) ) AS _MS_UPDSTATS_TBL_HELPER ORDER BY [SC0], [SB0000] ) AS _MS_UPDSTATS_TBL OPTION (MAXDOP 16)
テストマシンYMMVでMAXDOPが0に設定されているため、Maxdop 16(最大コア)
インスタンスの再起動
インスタンスを再起動すると、同じ動作が見られ、これらは実際には一時的な統計であることがわかります。一時的な統計に関するいくつかのQ&Aが見つかります here 。
これで、一時的な統計のcreationが表示され、クエリが実行される前に作成されます。
大きなテーブルの場合、サンプルレートは一時的な統計に引き続き適用されることに注意してください。
その他の注意点は、自動統計の一部として作成された統計はデータサンプリングを使用するため、これらの統計の作成は高速で、テーブルのサイズに依存しないということです
SQLサーバーの再起動時/データベースの復元時に統計情報の作成をどのように解決できますか?
それが可能な場合は、スクリプトを作成して、メインの「プライマリ」データベースに作成できます。
一時的な統計情報を見つける
SELECT OBJECT_ID, name, auto_created,
user_created, is_temporary
FROM sys.stats
WHERE is_temporary = 1;
統計のスクリプト
T-SQLなし または、質問に対してT-SQLの回答を使用する SQL Serverで統計をスクリプト化する方法(T-SQLを使用) 作成者 Martin Smith
これにより、統計が何度も作成されるという主な問題を解決できます。
一時的な統計が作成されているために問題が発生し、スクリプトを正しく作成できない場合の別のアイデアは、プライマリデータベースでレポートクエリの推定実行プランを作成することです。これにより、auto create statistics = onのときに必要な統計が作成されます。
一時的な統計の更新
発生する可能性のある他の問題は、古くなった永続的な統計です。この 前述のブログ で述べたように、古くなった永続的な統計を更新してis_temporary=1
に設定できます。
つまり、読み取り専用データベースの永続的な統計は、インスタンスが再起動されるまで一時的な統計になる可能性があります。プライマリの統計を更新すると、ログが適用されたときにセカンダリに引き継がれるはずです。
非同期の一時的な統計の更新
非同期統計の更新がこれらの一時的な統計でも機能していることがわかります!
addendum#1を実行した後、次のスニペットを実行します。
USE MASTER
GO
ALTER DATABASE [ReadOnly] SET READ_WRITE;
ALTER DATABASE [ReadOnly] SET AUTO_UPDATE_STATISTICS OFF
USE [ReadOnly]
GO
INSERT INTO dbo.Bla WITH(TABLOCK)(Indexedval,NonIndexedVal)
SELECT TOP(10000000) --10M
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum2
FROM master..spt_values spt1
CROSS JOIN master..spt_values spt2
CROSS JOIN master..spt_values spt3;
ALTER DATABASE [ReadOnly] SET AUTO_UPDATE_STATISTICS ON
USE MASTER
GO
ALTER DATABASE [ReadOnly] SET READ_ONLY;
SELECT Indexedval
FROM dbo.Bla
WHERE Indexedval =999999
AND 1= (SELECT 1);
上記のSELECT
は即座に実行されます。
クエリを実行すると、余波が表示されます。
一時統計の更新は、クエリの実行後に実行されます。
SELECT StatMan([SC0]) FROM (SELECT TOP 100 PERCENT [Indexedval] AS [SC0] FROM [dbo].[Bla] WITH (READUNCOMMITTED) ORDER BY [SC0] ) AS _MS_UPDSTATS_TBL OPTION (MAXDOP 1)
読み取り専用データベースの統計の自動更新を無効にする
次のステートメントを実行して、永続的な統計から一時的な統計への更新を無効にできます。読み取り専用データベースでこの設定を変更しても、引き続き機能します。
ALTER DATABASE [ReadOnly] SET AUTO_UPDATE_STATISTICS OFF;
一時的な統計アップグレードのソリューション。
また、プライマリ(週に1回)で行われた更新統計は、LS復元中に復元されますか?
統計の更新はセカンダリに引き継がれ、可能な限りより多く更新すると、古くなった統計が少なくなり、古くなった永続的な統計が一時的な統計に変換されなくなります。
これらの両方を調べることは、この問題に取り組む上での私の行くところでしょう。
トレースフラグを大規模に有効にして同僚を支援し、2から3千分まで掘り下げる場合、traceflag 2362を使用して一時的な統計を無効にできるようです。
あなたはこのようにそれらを有効にすることができます:
DBCC TRACEON(2362,-1);
また、すべての新たな一時統計は作成されません。既存の一時的な統計は、削除されるまで残ります。たとえば、dbをオフラインにして再度オンラインに設定します。
補遺2
addendum#2でクエリを実行し、スタンバイデータベースにログバックアップを適用すると、統計は各復元後に更新されます。
「空の」ログバックアップを適用した後でも。
補遺2では、各ログバックアップの復元の間に、次のクエリが実行されます。
SELECT Indexedval
FROM dbo.Bla
WHERE Indexedval =999999
AND 1= (SELECT 1);
これらは毎回temp statの更新をトリガーします。
証明
つまり、夜間にログを適用すると、インスタンスを再起動することなく、temp statの更新が毎日実行されます。
この問題の解決
ログバックアップを復元しても、それらはまだ存在します。
SELECT name, is_temporary From sys.stats where is_temporary = 1;
name is_temporary
PK__Bla__3214EC075017BD54 1
IX_Bla_indexedval 1
補遺#1(読み取り専用データベースの100Mレコードのテーブル)
CREATE DATABASE [ReadOnly]
CONTAINMENT = NONE
ON PRIMARY
( NAME = N'ReadOnly', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL02\MSSQL\DATA\ReadOnly.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
LOG ON
( NAME = N'ReadOnly_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL02\MSSQL\DATA\ReadOnly_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
GO
ALTER DATABASE [ReadOnly] SET COMPATIBILITY_LEVEL = 140
ALTER DATABASE [ReadOnly] SET AUTO_CREATE_STATISTICS ON(INCREMENTAL = OFF)
ALTER DATABASE [ReadOnly] SET AUTO_UPDATE_STATISTICS ON
ALTER DATABASE [ReadOnly] SET AUTO_UPDATE_STATISTICS_ASYNC ON
ALTER DATABASE [ReadOnly] SET READ_WRITE
ALTER DATABASE [ReadOnly] SET RECOVERY SIMPLE
ALTER DATABASE [ReadOnly] SET MULTI_USER
ALTER DATABASE [ReadOnly] SET PAGE_VERIFY CHECKSUM
USE [ReadOnly]
GO
CREATE TABLE dbo.Bla(Id INT IDENTITY(1,1) PRIMARY KEY NOT NULL, Indexedval INT,NonIndexedVal INT);
CREATE INDEX IX_Bla_indexedval on dbo.Bla(Indexedval);
INSERT INTO dbo.Bla WITH(TABLOCK)(Indexedval,NonIndexedVal)
SELECT TOP(10000000) --10M
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum2
FROM master..spt_values spt1
CROSS JOIN master..spt_values spt2
CROSS JOIN master..spt_values spt3;
GO 10
USE MASTER
GO
ALTER DATABASE [ReadOnly] SET READ_ONLY;
USE [ReadOnly]
GO
SELECT NonIndexedVal
FROM dbo.Bla
WHERE NonIndexedVal = 999999;
#補遺2
ALTER DATABASE [ReadOnly] SET READ_WRITE;
ALTER DATABASE [ReadOnly] SET RECOVERY FULL
BACKUP DATABASE [ReadOnly] to disk = 'D:\temp\ReadOnly.bak'
WITH COMPRESSION, STATS=5
RESTORE FILELISTONLY FROM DISK = 'D:\temp\ReadOnly.bak'
RESTORE DATABASE [ReadOnly2] FROM disk = 'D:\temp\ReadOnly.bak'
WITH MOVE 'ReadOnly' to 'D:\temp\ReadOnly2.mdf'
,MOVE 'ReadOnly_log' to 'F:\temp\ReadOnly_log2.ldf'
, STANDBY = 'D:\temp\ReadOnly_Standby.bak'
USE [ReadOnly2]
GO
ALTER DATABASE [ReadOnly2] SET AUTO_UPDATE_STATISTICS ON
SELECT Indexedval
FROM dbo.Bla
WHERE Indexedval =999999
AND 1= (SELECT 1);
USE [ReadOnly]
INSERT INTO dbo.Bla WITH(TABLOCK)(Indexedval,NonIndexedVal)
SELECT TOP(2000000) --2M
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum2
FROM master..spt_values spt1
CROSS JOIN master..spt_values spt2
CROSS JOIN master..spt_values spt3;
USE MASTER
GO
BACKUP LOG [ReadOnly] to disk = 'D:\temp\ReadOnlyLog.trn'
WITH COMPRESSION, STATS=5
RESTORE LOG [ReadOnly2] FROM DISK='D:\temp\ReadOnlyLog.trn'
WITH STANDBY = 'D:\temp\ReadOnly_Standby.bak'
USE [ReadOnly2]
SELECT Indexedval
FROM dbo.Bla
WHERE Indexedval =999999
AND 1= (SELECT 1);
BACKUP LOG [ReadOnly] to disk = 'D:\temp\ReadOnlyLog2.trn'
WITH COMPRESSION, STATS=5
RESTORE LOG [ReadOnly2] FROM DISK='D:\temp\ReadOnlyLog2.trn'
WITH STANDBY = 'D:\temp\ReadOnly_Standby.bak'
USE [ReadOnly2]
SELECT Indexedval
FROM dbo.Bla
WHERE Indexedval =999999
AND 1= (SELECT 1);
SELECT * From sys.stats where is_temporary = 1