Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Gilt für:SQL Server
Azure SQL-Datenbank
Übersicht
In diesem Beispiel wird das OLTP-Feature im Arbeitsspeicher vorgestellt. Es zeigt speicheroptimierte Tabellen und nativ kompilierte gespeicherte Prozeduren und kann verwendet werden, um die Leistungsvorteile von IN-Memory-OLTP zu veranschaulichen.
Hinweis
Um diesen Artikel für SQL Server 2014 (12.x) anzuzeigen, siehe Erweiterungen für AdventureWorks zur Veranschaulichung von In-Memory OLTP.
Im Beispiel werden fünf Tabellen aus der AdventureWorks2025-Datenbank zu speicheroptimierten Tabellen migriert. Zusätzlich enthält es eine exemplarische Arbeitsauslastung zur Abwicklung von Verkaufsaufträgen. Sie können diese Demo-Workload verwenden, um den Leistungsvorteil der Verwendung von OLTP im Arbeitsspeicher auf Ihrem Server zu sehen.
In der Beschreibung des Beispiels befassen wir uns mit den Kompromissen, die bei der Migration der Tabellen in den Arbeitsspeicher OLTP vorgenommen wurden, um die Features zu berücksichtigen, die für speicheroptimierte Tabellen nicht (noch) unterstützt werden.
Die Dokumentation dieses Beispiels ist wie folgt gegliedert:
Erforderliche Komponenten für die Installation des Beispiels und die Ausführung der exemplarischen Arbeitsauslastung.
Anweisungen zum Installieren des In-Memory OLTP-Beispiels basierend auf AdventureWorks.
Beschreibung der Beispieltabellen und -verfahren – enthält Beschreibungen der Tabellen und Verfahren, die vom In-Memory-OLTP-Beispiel hinzugefügt
AdventureWorks2025werden, sowie Überlegungen zum Migrieren einiger der ursprünglichenAdventureWorks2025Tabellen zu speicheroptimiertem Zustand.Anweisungen zur Durchführung von Leistungsmessungen mithilfe der Demo-Workload – enthält Anweisungen zum Installieren und Ausführen von ostress, einem Tool, das zur Steuerung der Workload verwendet wird, sowie zum Ausführen der Demo-Workload selbst.
Voraussetzungen
-
SQL Server 2016 (13.x)
Für Leistungstests benötigen Sie einen Server, dessen Kapazität ungefähr der eines Servers in Ihrer Produktionsumgebung entspricht. Für dieses spezielle Beispiel sollten SQL Server mindestens 16 GB Arbeitsspeicher zur Verfügung stehen. Allgemeine Richtlinien für Hardware für IN-Memory OLTP finden Sie im folgenden Blogbeitrag: Hardwareüberlegungen für In-Memory OLTP in SQL Server
Installieren Sie das In-Memory OLTP-Beispiel basierend auf AdventureWorks
Führen Sie die folgenden Schritte aus, um das Beispiel zu installieren:
Herunterladen von
AdventureWorks2016_EXT.bakundSQLServer2016Samples.zipvon: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks in einen lokalen Ordner, z. B.C:\Temp.Wiederherstellen des Datenbank-Backups mit Transact-SQL oder SQL Server Management Studio:
Identifizieren Sie den Zielordner und den Dateinamen für die Datendatei, z. B.:
H:\DATA\AdventureWorks2022_Data.mdfIdentifizieren Sie den Zielordner und dateinamen für die Protokolldatei, z. B.:
I:\DATA\AdventureWorks2022_log.ldf- Um optimale Leistung zu gewährleisten, sollte die Protokolldatei auf einem anderen Laufwerk als die Datendatei gespeichert werden, idealerweise auf einem Laufwerk mit niedriger Latenz wie einem SSD- oder PCIe-Speichermedium.
T-SQL-Beispielskript:
RESTORE DATABASE [AdventureWorks2022] FROM DISK = N'C:\temp\AdventureWorks2022.bak' WITH FILE = 1, MOVE N'AdventureWorks2022_Data' TO N'h:\DATA\AdventureWorks2022_Data.mdf', MOVE N'AdventureWorks2022_Log' TO N'i:\DATA\AdventureWorks2022_log.ldf', MOVE N'AdventureWorks2022_mod' TO N'h:\data\AdventureWorks2022_mod' GOUm die Beispielskripts und die Workload anzuzeigen, entpacken Sie die Datei
SQLServer2016Samples.zipin einen lokalen Ordner. Anweisungen zum Ausführen der Workload finden Sie in der DateiIn-Memory OLTP\readme.txt.
Beschreibung der Beispieltabellen und -prozeduren
Im Beispiel werden neue Tabellen für Produkte und Verkaufsaufträge auf Grundlage vorhandener AdventureWorks2025-Tabellen erstellt. Das Schema der neuen Tabellen ähnelt den vorhandenen Tabellen mit einigen Unterschieden, wie weiter unten in diesem Abschnitt erläutert.
Die neuen speicheroptimierten Tabellen tragen das Suffix _inmem. Das Beispiel enthält auch entsprechende Tabellen mit dem Suffix _ondisk – diese Tabellen können verwendet werden, um einen 1:1-Vergleich zwischen der Leistung von speicheroptimierten Tabellen und datenträgerbasierten Tabellen auf Ihrem System zu erstellen.
Die in der Arbeitsauslastung für Leistungsvergleiche verwendeten speicheroptimierten Tabellen sind vollständig dauerhaft und vollständig protokolliert, Sie opfern keine Haltbarkeit oder Zuverlässigkeit, um den Leistungsgewinn zu erzielen.
Die Zielarbeitsauslastung in diesem Beispiel dient der Abwicklung von Verkaufsaufträgen. Dabei fließen auch Produkt- und Rabattinformationen Zu diesem Zweck verwenden wir die Tabellen SalesOrderHeader, SalesOrderDetail, Product, SpecialOfferund SpecialOfferProduct.
Zwei neue gespeicherte Prozeduren, Sales.usp_InsertSalesOrder_inmem und Sales.usp_UpdateSalesOrderShipInfo_inmem, werden zum Einfügen von Verkaufsaufträgen und zur Aktualisierung der Versandinformationen eines bestimmten Verkaufsauftrags verwendet.
Das neue Schema Demo enthält Hilfstabellen und gespeicherte Prozeduren zum Ausführen einer exemplarischen Arbeitsauslastung.
Durch das In-Memory OLTP-Beispiel werden AdventureWorks2025 im Einzelnen die folgenden Objekte hinzugefügt:
Tabellen, die durch das Beispiel hinzugefügt werden
Die neuen Tabellen
Sales.SalesOrderHeader_inmem
- Kopfzeileninformationen zu Verkaufsaufträgen. Diese Tabelle enthält eine Zeile für jeden Verkaufsauftrag.
Sales.SalesOrderDetail_inmem
- Detailinformationen zu Verkaufsaufträgen. Diese Tabelle enthält eine Zeile für jeden Einzelposten eines Verkaufsauftrags.
Sales.SpecialOffer_inmem
- Informationen zu Sonderangeboten, einschließlich des prozentualen Rabatts, der den einzelnen Sonderangeboten zugeordnet ist.
Sales.SpecialOfferProduct_inmem
- Verweistabelle zwischen Sonderangeboten und Produkten. Jedes Sonderangebot kann für 0 (null) oder mehrere Produkte gelten, und jedes Produkt kann in 0 oder mehreren Sonderangeboten vertreten sein.
Production.Product_inmem
- Informationen zu Produkten, einschließlich der Listenpreise.
Demo.DemoSalesOrderDetailSeed
- Wird in der exemplarischen Arbeitsauslastung zum Erstellen von Beispielverkaufsaufträgen verwendet.
Datenträgerbasierte Tabellenvarianten:
Sales.SalesOrderHeader_ondiskSales.SalesOrderDetail_ondiskSales.SpecialOffer_ondiskSales.SpecialOfferProduct_ondiskProduction.Product_ondisk
Unterschiede zwischen ursprünglichen datenträgerbasierten und neuen speicheroptimierten Tabellen
In der Regel verwenden die in diesem Beispiel eingeführten neuen Tabellen dieselben Spalten und dieselben Datentypen wie die ursprünglichen Tabellen. wie die ursprünglichen Tabellen. Wir führen die Unterschiede in diesem Abschnitt zusammen mit einer Begründung für die Änderungen auf.
Sales.SalesOrderHeader_inmem
DaStandardeinschränkungen bei speicheroptimierten Tabellen unterstützt werden, wurden die meisten Standardeinschränkungen unverändert migriert. Die ursprüngliche Tabelle
Sales.SalesOrderHeaderenthält jedoch zwei Standardeinschränkungen, durch die für die SpaltenOrderDateundModifiedDatedas aktuelle Datum abgerufen wird. In einer Arbeitsauslastung für die Auftragsverarbeitung mit hohem Durchsatz und einem hohen Maß an Parallelität können globale Ressourcen zu Konflikten führen. Systemzeit ist eine solche globale Ressource, und wir haben festgestellt, dass sie zu einem Engpass werden kann, wenn eine In-Memory OLTP-Workload ausgeführt wird, die Verkaufsaufträge einfügt, insbesondere wenn die Systemzeit für mehrere Spalten im Header und in den Details des Verkaufsauftrags abgerufen werden muss. In diesem Beispiel wird das Problem umgangen, indem die Systemzeit für jeden eingefügten Verkaufsauftrag nur einmal abgerufen und dieser Wert in der gespeicherten ProzedurSalesOrderHeader_inmemfür die datetime-Spalten inSalesOrderDetail_inmemundSales.usp_InsertSalesOrder_inmemverwendet wird.Alias-UDTs (User-defined Data Types, benutzerdefinierte Datentypen): Die Originaltabelle verwendet zwei Alias-UDTs
dbo.OrderNumberunddbo.AccountNumberfür die SpaltenPurchaseOrderNumberbzw.AccountNumber. SQL Server 2016 (13.x) unterstützt keinen Alias UDT für speicheroptimierte Tabellen, daher verwenden die neuen Tabellen systemdatentypen nvarchar(25) bzw. nvarchar(15).Nullfähige Spalten in Indexschlüsseln – In der ursprünglichen Tabelle ist die Spalte
SalesPersonIDnullfähig, während in den neuen Tabellen die Spalte nicht nullwertebar ist und eine Standardeinschränkung mit dem Wert (-1) aufweist. Dieser Umstand liegt daran, dass Indizes für speicheroptimierte Tabellen keine nullfähigen Spalten im Indexschlüssel aufweisen können. -1 ist in diesem Fall ein Ersatz für NULL.Berechnete Spalten – Die berechneten Spalten
SalesOrderNumberundTotalDuewerden weggelassen, da SQL Server 2016 (13.x) berechnete Spalten in speicheroptimierten Tabellen nicht unterstützt. Die neue AnsichtSales.vSalesOrderHeader_extended_inmemspiegelt die SpaltenSalesOrderNumberundTotalDue. Falls diese Spalten benötigt werden, können Sie diese Sicht verwenden.- Gilt für: SQL Server 2017 (14.x). Ab SQL Server 2017 (14.x) werden berechnete Spalten in speicheroptimierten Tabellen und Indizes unterstützt.
Fremdschlüsseleinschränkungen werden für speicheroptimierte Tabellen in SQL Server 2016 (13.x) nur dann unterstützt, wenn die referenzierten Tabellen ebenfalls speicheroptimiert sind. Fremdschlüssel mit Verweis auf Tabellen, die ebenfalls zu speicheroptimierten Tabellen migriert werden, werden in den migrierten Tabellen beibehalten, während andere Fremdschlüssel ausgelassen werden. Darüber hinaus ist
SalesOrderHeader_inmemeine heiße Tabelle in der Beispielarbeitslast, und Fremdschlüsselbeschränkungen erfordern zusätzlichen Verarbeitungsaufwand für alle DML-Operationen, da sie Nachschlagen in allen anderen Tabellen erfordern, die in diesen Beschränkungen referenziert werden. Daher wird davon ausgegangen, dass die App die referenzielle Integrität für dieSales.SalesOrderHeader_inmemTabelle sicherstellt und die referenzielle Integrität beim Einfügen von Zeilen nicht überprüft wird.Rowguid : Die rowguid-Spalte wird nicht verwendet. Während eindeutiger Bezeichner für speicheroptimierte Tabellen unterstützt wird, wird die Option ROWGUIDCOL in SQL Server 2016 (13.x) nicht unterstützt. Spalten dieses Typs werden normalerweise für Mergereplikationen oder für Tabellen mit FILESTREAM-Spalten verwendet, die in diesem Beispiel beide nicht zum Einsatz kommen.
Sales.SalesOrderDetail
Standardeinschränkungen – ähnlich wie
SalesOrderHeaderdie Standardeinschränkung, die das Systemdatum/die Systemzeit erfordert, wird nicht migriert. Stattdessen sorgt die gespeicherte Prozedur für das Einfügen von Verkaufsaufträgen dafür, dass beim ersten Einfügen das aktuelle Systemdatum und die aktuelle Systemzeit gesetzt werden.Berechnete Spalten – die berechnete Spalte
LineTotalwurde nicht migriert, da berechnete Spalten mit speicheroptimierten Tabellen in SQL Server 2016 (13.x) nicht unterstützt werden. Verwenden Sie dieSales.vSalesOrderDetail_extended_inmemAnsicht, um auf diese Spalte zuzugreifen.Rowguid: Die
rowguid-Spalte wird nicht verwendet. Ausführliche Informationen finden Sie in der Beschreibung zur TabelleSalesOrderHeader.
Production.Product
Alias-UDTs: Die ursprüngliche Tabelle verwendet den benutzerdefinierten Datentyp
dbo.Flag, der dem Systemdatentyp „bit“ entspricht. Die migrierte Tabelle verwendet stattdessen den Datentyp bit.Rowguid: Die
rowguid-Spalte wird nicht verwendet. Ausführliche Informationen finden Sie in der Beschreibung zur TabelleSalesOrderHeader.
Sales.SpecialOffer
-
Rowguid: Die
rowguid-Spalte wird nicht verwendet. Ausführliche Informationen finden Sie in der Beschreibung zur TabelleSalesOrderHeader.
Sales.SpecialOfferProduct
-
Rowguid: Die
rowguid-Spalte wird nicht verwendet. Ausführliche Informationen finden Sie in der Beschreibung zur TabelleSalesOrderHeader.
Überlegungen zu Indizes bei speicheroptimierten Tabellen
Der Basisindex für speicheroptimierte Tabellen ist der NONCLUSTERED-Index, der Punktsuchen (Indexsuche in Gleichheitsprädikaten), Bereichsscans (Indexsuche in Ungleichheitsprädikaten), vollständige Indexscans und sortierte Scans unterstützt. Zusätzlich unterstützen NONCLUSTERED-Indizes Suchen in führenden Indexschlüsselspalten. Bis auf Rückwärtsscans unterstützen speicheroptimierte NONCLUSTERED-Indizes alle Vorgänge, die auch von datenträgerbasierten NONCLUSTERED-Indizes unterstützt werden. Daher sind NONCLUSTERED-Indizes eine sichere Wahl für Ihre Indizes.
Mit HASH-Indizes kann die Workload weiter optimiert werden. Sie sind für Punktsuchen und Zeileneinfügungen optimiert. Man muss jedoch berücksichtigen, dass sie Bereichsscans, sortierte Scans oder die Suche nach führenden Indexschlüsselspalten nicht unterstützen. Daher sollten diese Indizes mit Vorsicht verwendet werden. Darüber hinaus ist es erforderlich, bucket_count bei der Erstellung anzugeben. Die Bucketanzahl sollte normalerweise auf einen Wert zwischen der einfachen und doppelten Anzahl von Indexschlüsselwerten festgelegt werden. Ein zu hoher Wert stellt in der Regel aber auch kein Problem dar.
Weitere Informationen finden Sie unter:
- Richtlinien für Onlineindexvorgänge
- Auswählen des geeigneten bucket_count-Werts
- Indizes für speicheroptimierte Tabellen
Die Indizes der migrierten Tabellen wurden für die Demo-Arbeitsauslastung für die Auftragsverarbeitung optimiert. Die Workload stützt sich auf Einfügungen und Punktsuchen in den Tabellen Sales.SalesOrderHeader_inmem und Sales.SalesOrderDetail_inmem, und sie stützt sich auch auf Punktsuchen in den Primärschlüsselspalten in den Tabellen Production.Product_inmem und Sales.SpecialOffer_inmem.
Sales.SalesOrderHeader_inmem verfügt über drei Indizes, die aus Leistungsgründen und weil für die Workload keine sortierten oder Bereichsscans erforderlich sind, alle HASH-Indizes sind.
HASH-Index für (
SalesOrderID): bucket_count hat einen Wert von 10 Millionen (und wird auf 16 Millionen aufgerundet), da die erwartete Anzahl von Verkaufsaufträgen 10 Millionen beträgt.HASH-Index für (
SalesPersonID): bucket_count beträgt 1 Million. Der bereitgestellte Datensatz verfügt nicht über viele Vertriebsmitarbeiter. Dieser große bucket_count-Wert bietet jedoch genügend Platz für zukünftiges Wachstum. Und Sie müssen keine Leistungseinbußen für Punktsuchen in Kauf nehmen, wenn bucket_count überdimensioniert ist.HASH-Index für (
CustomerID): bucket_count beträgt 1 Million. Das bereitgestellte Dataset verfügt nicht über viele Kunden, aber dies ermöglicht zukünftiges Wachstum.
Sales.SalesOrderDetail_inmem verfügt über drei Indizes, die aus Leistungsgründen und weil für die Workload keine sortierten oder Bereichsscans erforderlich sind, alle HASH-Indizes sind.
HASH-Index auf (
SalesOrderID,SalesOrderDetailID): Dies ist der Primärschlüsselindex, und auch wenn Abfragen auf (SalesOrderID,SalesOrderDetailID) selten sind, beschleunigt die Verwendung eines Hash-Index für den Schlüssel das Einfügen von Zeilen. Der bucket_count-Wert ist auf 50 Millionen festgelegt (und wird auf 67 Millionen aufgerundet): Die erwartete Anzahl von Verkaufsaufträgen beträgt 10 Millionen mit durchschnittlich fünf Einzelposten pro Auftrag.HASH-Index am (
SalesOrderID): Nachschlagevorgänge nach Verkaufsauftrag sind häufig: Sie möchten alle Positionen suchen, die einer einzelnen Bestellung entsprechen. bucket_count hat einen Wert von 10 Millionen (und wird auf 16 Millionen aufgerundet), da die erwartete Anzahl von Verkaufsaufträgen 10 Millionen beträgt.HASH-Index für (
ProductID): bucket_count beträgt 1 Million. Das bereitgestellte Dataset verfügt nicht über viele Produkte, aber dies ermöglicht zukünftiges Wachstum.
Production.Product_inmem hat drei Indizes
HASH-Index für (
ProductID): Da Suchen nachProductIDein wesentlicher Bestandteil der exemplarischen Workload sind, wird hier ein HASH-Index verwendet.NONCLUSTERED index on (
Name): Dies ermöglicht geordnete Scans von ProduktnamenNicht gruppierter Index auf (
ProductNumber): Dies ermöglicht geordnete Scans von Produktnummern
Sales.SpecialOffer_inmem hat einen HASH-Index für (SpecialOfferID): Punktsuchen nach Sonderangeboten sind ein wesentlicher Bestandteil der exemplarischen Workload.
bucket_count beträgt 1 Million und ist auf zukünftiges Wachstum ausgelegt.
Sales.SpecialOfferProduct_inmem wird nicht auf den Demoworkload verwiesen, und daher ist es nicht offensichtlich erforderlich, Hashindizes für diese Tabelle zu verwenden, um die Workload zu optimieren – die Indizes für (SpecialOfferID, ProductID) und (ProductID) sind NONCLUSTERED.
Im vorherigen Beispiel sind einige der Bucket-Anzahlen überdimensioniert, jedoch nicht die Bucket-Anzahlen für die Indizes auf SalesOrderHeader_inmem und SalesOrderDetail_inmem: sie sind ausgelegt für nur 10 Millionen Bestellungen. Dies wurde getan, um die Installation des Beispiels auf Systemen mit geringer Speicherverfügbarkeit zu ermöglichen, obwohl in diesen Fällen die Demo-Workload mit einem Fehler außerhalb des Arbeitsspeichers fehlschlägt. Wenn Sie einen Wert festlegen möchten, der 10 Millionen Verkaufsaufträge erheblich überschreitet, können Sie die Bucketanzahlen einfach entsprechend erhöhen.
Überlegungen zur Arbeitsspeichernutzung
Die Arbeitsspeichernutzung der Beispieldatenbank vor und nach der Ausführung der exemplarischen Arbeitsauslastung wird im Abschnitt Arbeitsspeichernutzung für speicheroptimierte Tabellenerörtert.
Gespeicherte Prozeduren, die vom Beispiel hinzugefügt wurden
Die beiden wichtigsten gespeicherten Prozeduren zum Einfügen von Verkaufsaufträgen und Aktualisieren von Versanddetails lauten:
Sales.usp_InsertSalesOrder_inmemFügt einen neuen Verkaufsauftrag in die Datenbank ein und gibt die
SalesOrderIDfür den Verkaufsauftrag aus. Als Eingabeparameter werden Details für die Kopfzeile des Verkaufsauftrags und die Einzelposten in der Bestellung benötigt.Ausgabeparameter:
-
@SalesOrderID int - die
SalesOrderIDfür den soeben eingefügten Verkaufsauftrag
-
@SalesOrderID int - die
Eingabeparameter (erforderlich):
- @DueDatedatetime2
- @CustomerIDint
- @BillToAddressIDint
- @ShipToAddressIDint
- @ShipMethodIDint
-
Sales.SalesOrderDetailType_inmem@SalesOrderDetails - Tabellenwertparameter (TVP), der die Positionen der Bestellung enthält
Eingabeparameter (optional):
- @Statustinyint
- @OnlineOrderFlagBit
- @PurchaseOrderNumbernvarchar(25)
- @AccountNumbernvarchar(15)
- @SalesPersonIDint
- @TerritoryIDint
- @CreditCardIDint
- @CreditCardApprovalCodevarchar(15)
- @CurrencyRateIDint
- @Commentnvarchar(128)
Sales.usp_UpdateSalesOrderShipInfo_inmemAktualisiert die Versandinformationen für einen bestimmten Verkaufsauftrag. Dadurch werden auch die Versandinformationen für alle Positionen des Verkaufsauftrags aktualisiert.
Dies ist eine Wrapperprozedur für die systemintern kompilierte gespeicherte Prozedur
Sales.usp_UpdateSalesOrderShipInfo_native. Sie verfügt über eine Wiederholungslogik zur Behandlung (unerwarteter) potenzieller Konflikte mit Transaktionen, die gleichzeitig ausgeführt werden und denselben Auftrag aktualisieren. Weitere Informationen finden Sie unter Wiederholungslogik.
Sales.usp_UpdateSalesOrderShipInfo_native- Dies ist die systemintern kompilierte gespeicherte Prozedur, durch die das Update der Versandinformationen tatsächlich verarbeitet wird. Es soll aus der gespeicherten Wrapper-Prozedur
Sales.usp_UpdateSalesOrderShipInfo_inmemaufgerufen werden. Wenn der Client Fehler behandeln kann und eine Wiederholungslogik implementiert wurde, können Sie diese Prozedur direkt aufrufen, anstatt die gespeicherte Wrapperprozedur zu verwenden.
- Dies ist die systemintern kompilierte gespeicherte Prozedur, durch die das Update der Versandinformationen tatsächlich verarbeitet wird. Es soll aus der gespeicherten Wrapper-Prozedur
Die folgende gespeicherte Prozedur wird für die exemplarische Arbeitsauslastung verwendet.
Demo.usp_DemoReset- Sie setzt die exemplarische Workload zurück, indem sie die Tabellen
SalesOrderHeaderundSalesOrderDetailleert und neu befüllt.
- Sie setzt die exemplarische Workload zurück, indem sie die Tabellen
Mit den folgenden gespeicherten Prozeduren werden Daten in speicheroptimierten Tabellen eingefügt und daraus gelöscht, ohne die Domänenintegrität und referenzielle Integrität zu gefährden.
Production.usp_InsertProduct_inmemProduction.usp_DeleteProduct_inmemSales.usp_InsertSpecialOffer_inmemSales.usp_DeleteSpecialOffer_inmemSales.usp_InsertSpecialOfferProduct_inmem
Zum Schluss werden Domänenintegrität und referenzielle Integrität mit der folgenden gespeicherten Prozedur überprüft.
dbo.usp_ValidateIntegrityOptionaler Parameter: @object_id – ID des Objekts zur Überprüfung der Integrität für
Diese Prozedur ermittelt anhand der Tabellen
dbo.DomainIntegrity,dbo.ReferentialIntegrityunddbo.UniqueIntegrity, welche Integritätsregeln überprüft werden müssen. Im Beispiel werden diese Tabellen auf der Grundlage der CHECK-, FOREIGN KEY- und UNIQUE-Einschränkungen der ursprünglichen Tabellen in derAdventureWorks2025-Datenbank aufgefüllt.Sie generiert mithilfe der Hilfsprozeduren
dbo.usp_GenerateCKCheck,dbo.usp_GenerateFKCheckunddbo.GenerateUQCheckden T-SQL-Code, der für die Durchführung der Integritätsprüfungen benötigt wird.
Leistungsmessungen mithilfe der Demo-Arbeitslast
ostress ist ein Befehlszeilentool, das vom Microsoft CSS SQL Server-Supportteam entwickelt wurde. Mit diesem Tool können Abfragen ausgeführt oder gespeicherte Prozeduren parallel aufgerufen werden. Sie können die Anzahl der Threads so konfigurieren, dass eine bestimmte T-SQL-Anweisung parallel ausgeführt wird, und Sie können angeben, wie oft die Anweisung in diesem Thread ausgeführt werden soll. ostress startet die Threads und führt die Anweisung auf allen Threads parallel aus. Nach Abschluss der Ausführung für alle Threads meldet Ostress die Zeit, die für alle Threads zum Abschließen der Ausführung benötigt wurde.
Ostress installieren
ostress wird als Teil der RmL-Hilfsprogramme (Report Markup Language) installiert; es gibt keine eigenständige Installation für Ostress.
Installationsschritte:
Laden Sie die x64-Version des Installationspakets für die RML-Hilfsprogramme von folgender Seite herunter, und führen Sie das Paket aus: RML für SQL Server herunterladen.
Wenn ein Dialogfeld angezeigt wird, in dem bestimmte Dateien verwendet werden, wählen Sie "Weiter" aus.
Ausführen von ostress
OSTRESS wird an der Eingabeaufforderung ausgeführt. Es ist am bequemsten, das Tool aus der RML Cmd Prompt auszuführen, die als Teil der RML-Dienstprogramme installiert ist.
Führen Sie die folgenden Schritte aus, um die RML-Eingabeaufforderung zu öffnen:
Öffnen Sie unter Windows das Startmenü, indem Sie die Windows-Taste drücken, und geben Sie rml ein. Wählen Sie RML Cmd Prompt aus, die sich in der Liste der Suchergebnisse befindet.
Vergewissern Sie sich, dass sich die Eingabeaufforderung im Installationsordner für die RML-Hilfsprogramme befindet.
Die Befehlszeilenoptionen für Ostress werden angezeigt, wenn ostress.exe ohne Befehlszeilenoptionen ausgeführt wird. Die wichtigsten Optionen für die Ausführung von ostress mit diesem Beispiel sind wie folgt:
| Option | Description |
|---|---|
-S |
Name der SQL Server-Instanz, mit der eine Verbindung hergestellt werden soll. |
-E |
Verwenden Sie die Windows-Authentifizierung, um eine Verbindung herzustellen (Standard); Wenn Sie die SQL Server-Authentifizierung verwenden, verwenden Sie die Optionen -U und -P geben Sie den Benutzernamen bzw. das Kennwort an. |
-d |
Name der Datenbank, für dieses Beispiel AdventureWorks2025. |
-Q |
Die auszuführende T-SQL-Anweisung. |
-n |
Anzahl der Verbindungen, die jede Eingabedatei/Abfrage verarbeiten. |
-r |
Die Anzahl der Iterationen für jede Verbindung, um jede Eingabedatei/Abfrage auszuführen. |
Demoworkload
Die wichtigste in der exemplarischen Workload verwendete gespeicherte Prozedur ist Sales.usp_InsertSalesOrder_inmem/ondisk. Das Skript im folgenden Beispiel erstellt einen Tabellenwertparameter (TVP) mit Beispieldaten und ruft die Prozedur auf, um eine Bestellung mit fünf Positionen einzufügen.
Das Ostress-Tool wird verwendet, um die gespeicherten Prozeduraufrufe parallel auszuführen, um Clients zu simulieren, die Verkaufsaufträge gleichzeitig einfügen.
Setzen Sie die exemplarische Workload nach jedem Belastungstest zurück, indem Sie Demo.usp_DemoReset ausführen. Durch diese Prozedur werden die Zeilen in den speicheroptimierten Tabellen gelöscht, die datenträgerbasierten Tabellen abgeschnitten und ein Datenbankprüfpunkt ausgeführt.
Das folgende Skript wird gleichzeitig ausgeführt, um eine Arbeitsauslastung zur Auftragsabwicklung zu simulieren:
DECLARE @i AS INT = 0, @od AS Sales.SalesOrderDetailType_inmem, @SalesOrderID AS INT, @DueDate AS DATETIME2 = sysdatetime(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS INT = (RAND() * 5) + 1;
INSERT INTO @od
SELECT OrderQty,
ProductID,
SpecialOfferID
FROM Demo.DemoSalesOrderDetailSeed
WHERE OrderID = CAST ((RAND() * 106) + 1 AS INT);
WHILE (@i < 20)
BEGIN
EXECUTE Sales.usp_InsertSalesOrder_inmem
@SalesOrderID OUTPUT,
@DueDate,
@CustomerID,
@BillToAddressID,
@ShipToAddressID,
@ShipMethodID,
@od;
SET @i + = 1;
END
Mit diesem Skript wird jeder erstellte Beispielauftrag durch 20 in einer WHILE-Schleife ausgeführte gespeicherte Prozeduren 20 Mal eingefügt. Die Schleife wird verwendet, weil die Datenbank zum Erstellen des Beispielauftrags verwendet wird. In typischen Produktionsumgebungen erstellt die Mid-Tier-Anwendung den einzufügenden Verkaufsauftrag.
Das vorherige Skript fügt Verkaufsaufträge in speicheroptimierte Tabellen ein. Das Skript zum Einfügen von Verkaufsaufträgen in datenträgerbasierte Tabellen wird abgeleitet, indem die beiden Vorkommen von _inmem durch _ondisk ersetzt werden.
Wir verwenden das Ostress-Tool , um die Skripts mit mehreren gleichzeitigen Verbindungen auszuführen. Wir verwenden den Parameter -n , um die Anzahl der Verbindungen und den Parameter r zu steuern, wie oft das Skript für jede Verbindung ausgeführt wird.
Workload ausführen
Um das Verhalten in einem größeren Szenario zu testen, fügen wir unter Verwendung von 100 Verbindungen 10 Millionen Verkaufsaufträge ein. Bei einem einfach ausgestatteten Server (z. B. mit 8 physischen und 16 logischen Kernen) und SSD-Basisspeicher für das Protokoll liefert der Test zufriedenstellende Ergebnisse. Wenn der Test auf Ihrer Hardware nicht gut funktioniert, sehen Sie sich den Abschnitt "Problembehandlung bei langsam ausgeführten Tests" an. Wenn Sie den Stressfaktor für diesen Test verringern möchten, verringern Sie die Anzahl der Verbindungen, indem Sie den Parameter -nändern. Wenn Sie beispielsweise die Verbindungsanzahl auf 40 senken möchten, ändern Sie den Parameter -n100 in -n40.
Als Leistungskennzahl für die Workload verwenden wir die verstrichene Laufzeit, die nach der Ausführung der Workload von ostress.exe gemeldet wird.
Die folgenden Anweisungen und Messungen verwenden eine Arbeitsauslastung, die 10 Millionen Verkaufsaufträge einfügt. Anweisungen zum Ausführen einer reduzierten Arbeitsauslastung, die 1 Million Verkaufsaufträge einfügt, finden Sie in den Anweisungen, in In-Memory OLTP\readme.txt, die Teil des SQLServer2016Samples.zip Archivs sind.
Speicheroptimierte Tabellen
Wir beginnen damit, die Workload auf speicheroptimierten Tabellen auszuführen. Mit dem folgenden Befehl werden 100 Threads geöffnet, die jeweils für 5.000 Iterationen ausgeführt werden. Pro Iteration werden 20 Verkaufsaufträge in getrennten Transaktionen eingefügt. Die 20 Einfügungen pro Iteration sind darauf zurückzuführen, dass die einzufügenden Daten unter Verwendung der Datenbank generiert werden. Daraus ergeben sich insgesamt 20 * 5.000 * 100 = 10.000.000 eingefügte Verkaufsaufträge.
Öffnen Sie die RML Cmd-Eingabeaufforderung, und führen Sie den folgenden Befehl aus:
Wählen Sie die Schaltfläche Kopieren aus, um den Befehl zu kopieren, und fügen Sie ihn in die Eingabeaufforderung der RML-Hilfsprogramme ein.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i AS INT = 0, @od AS Sales.SalesOrderDetailType_inmem, @SalesOrderID AS INT, @DueDate AS DATETIME2 = SYSDATETIME(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS INT = (RAND() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID = CAST ((RAND() * 106) + 1 AS INT); WHILE (@i < 20) BEGIN EXECUTE Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; SET @i + = 1; END"
Auf einem Testserver mit insgesamt 8 physischen (16 logischen) Kernen betrug die Dauer zwei Minuten und fünf Sekunden. Auf einem zweiten Testserver mit 24 physischen (48 logischen) Kernen dauerte der Vorgang eine Minute und 0 (null) Sekunden.
Beobachten Sie bei der Ausführung der Arbeitsauslastung die CPU-Auslastung, beispielsweise mit dem Task-Manager. Sie sehen, dass die CPU-Auslastung nahezu 100%ist. Wenn dies nicht der Fall ist, haben Sie einen Protokoll-E/A-Engpass, siehe auch Problembehandlung bei langsam ausgeführten Tests.
Datenträgerbasierte Tabellen
Der folgende Befehl führt die Arbeitsauslastung auf datenträgerbasierten Tabellen aus. Diese Arbeitsauslastung kann eine Weile dauern, was größtenteils auf eine Riegelung im System zurückzuführen ist. Speicheroptimierte Tabellen sind latenzfrei und deshalb tritt dieses Problem nicht auf.
Öffnen Sie die RML-Eingabeaufforderung, und führen Sie folgenden Befehl aus:
Wählen Sie die Schaltfläche Kopieren aus, um den Befehl zu kopieren, und fügen Sie ihn in die Eingabeaufforderung der RML-Hilfsprogramme ein.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i AS INT = 0, @od AS Sales.SalesOrderDetailType_ondisk, @SalesOrderID AS INT, @DueDate AS DATETIME2 = sysdatetime(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS INT = (RAND() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID = CAST ((RAND() * 106) + 1 AS INT); WHILE (@i < 20) BEGIN EXECUTE Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; SET @i + = 1; END"
Auf einem Testserver mit insgesamt 8 physischen (16 logischen) Kernen betrugt die Dauer 41 Minuten und 25 Sekunden. Auf einem zweiten Testserver mit 24 physischen (48 logischen) Kernen dauerte der Vorgang 52 Minuten und 16 Sekunden.
Der Hauptfaktor des Leistungsunterschieds zwischen speicheroptimierten Tabellen und datenträgerbasierten Tabellen in diesem Test besteht darin, dass SQL Server bei Verwendung datenträgerbasierter Tabellen die CPU nicht vollständig nutzen kann. Die Ursache sind Latchkonflikte: Wenn gleichzeitige Transaktionen versuchen, Daten in dieselbe Datenseite zu schreiben, wird mithilfe von Latches sichergestellt, dass jeweils nur eine Transaktion Schreibzugriff auf eine Seite hat. Das In-Memory OLTP-Modul ist sperrfrei, und Datenzeilen werden nicht auf Seiten organisiert. Daher blockieren gleichzeitige Transaktionen die Einfügungen gegenseitig nicht, sodass SQL Server die CPU vollständig nutzen kann.
Sie können die CPU-Auslastung bei der Ausführung der Arbeitsauslastung beispielsweise mit dem Task-Manager beobachten. Bei datenträgerbasierten Tabellen liegt die CPU-Auslastung deutlich unter 100 %. In einer Testkonfiguration mit 16 logischen Prozessoren würde sich die Auslastung um 24 % bewegen.
Optional können Sie den Leistungsindikator \SQL Server:Latches\Latch Waits/sec im Systemmonitor verwenden, um die Anzahl der Latchwartevorgänge pro Sekunde anzuzeigen.
Zurücksetzen der Demo
Um die exemplarische Arbeitsauslastung zurückzusetzen, öffnen Sie die RML-Eingabeaufforderung und führen folgenden Befehl aus:
ostress.exe -S. -E -dAdventureWorks2022 -Q"EXEC Demo.usp_DemoReset"
Je nach Hardware kann dies einige Minuten dauern.
Es wird empfohlen, die Arbeitsauslastung nach jedem Durchgang zurückzusetzen. Da diese Workload nur einfügend ist, verbraucht jede Ausführung mehr Arbeitsspeicher und daher ist eine Zurücksetzung erforderlich, um zu verhindern, dass der Arbeitsspeicher ausgeht. Der Abschnitt Arbeitsspeichernutzung nach dem Ausführen der Arbeitsauslastungenthält Informationen darüber, wie viel Arbeitsspeicher nach einer Ausführung belegt ist.
Fehlerbehebung bei langsamen Tests
Testergebnisse variieren in der Regel je nach Hardware und dem Grad der Parallelität, der im Testlauf verwendet wird. Ein paar Dinge, nach denen gesucht werden muss, wenn die Ergebnisse nicht wie erwartet sind:
Anzahl gleichzeitiger Transaktionen: Beim Ausführen der Workload auf einem einzelnen Thread ist die Leistungssteigerung mit In-Memory OLTP wahrscheinlich weniger als 2X. Latch Contention ist nur ein erhebliches Problem, wenn es eine hohe Parallelität gibt.
Geringe Anzahl von Kernen, die sql Server zur Verfügung stehen: Dies bedeutet, dass eine geringe Parallelität im System vorhanden ist, da es nur so viele gleichzeitig ausgeführte Transaktionen geben kann, wie kerne für SQL verfügbar sind.
- Symptom: Wenn die CPU-Auslastung bei der Bearbeitung der Arbeitslast auf datenträgerbasierten Tabellen hoch ist, bedeutet dies, dass es wenig Konflikt gibt, was auf einen Mangel an Parallelität hinweist.
Geschwindigkeit des Protokolllaufwerks: Wenn das Protokolllaufwerk nicht mit dem Transaktionsdurchsatz im System Schritt halten kann, entsteht ein Engpass bei der Protokoll-E/A der Arbeitslast. Obwohl die Protokollierung mit In-Memory OLTP effizienter ist, wenn E/A-Protokollvorgänge einen Engpass verursachen, ist der potenzielle Leistungsgewinn begrenzt.
- Symptom: Wenn die CPU-Auslastung nicht bei 100% liegt oder stark schwankend ist, wenn die Workload auf speicheroptimierten Tabellen ausgeführt wird, ist es möglich, dass es einen Log-E/A-Engpass gibt. Sie können die Ursache im Ressourcenmonitor anhand der Warteschlangenlänge für das Protokolllaufwerk ermitteln.
Arbeitsspeicher- und Speicherplatzauslastung im Beispiel
Im folgenden Beispiel wird beschrieben, was in Bezug auf Arbeitsspeicher und Speicherplatznutzung für die Beispieldatenbank zu erwarten ist. Außerdem zeigen wir die Ergebnisse auf einem Testserver mit 16 logischen Kernen an.
Arbeitsspeichernutzung für speicheroptimierte Tabellen
Gesamtnutzung der Datenbank
Mithilfe der folgenden Abfrage kann die gesamte Arbeitsspeichernutzung für In-Memory OLTP im System ermittelt werden.
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Momentaufnahme direkt nach der Erstellung der Datenbank:
| Typ | name | pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Standard | 94 |
| MEMORYCLERK_XTP | DB_ID_5 | 877 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Die standardmäßigen Arbeitsspeicherclerks basieren auf systemweiten Strukturen und sind relativ klein. Der Speicherverwalter für die Benutzerdatenbank, in diesem Fall die Datenbank mit der ID 5 (die in Ihrer Instanz abweichen kann), liegt bei etwa 900 MB.
Arbeitsspeichernutzung pro Tabelle
Mithilfe der folgenden Abfrage kann ein Drilldown ausgeführt werden, um die Arbeitsspeichernutzung der einzelnen Tabellen und ihrer Indizes zu ermitteln:
SELECT object_name(t.object_id) AS [Table name],
memory_allocated_for_table_kb,
memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats AS dms
INNER JOIN sys.tables AS t
ON dms.object_id = t.object_id
WHERE t.type = 'U';
Die folgende Tabelle zeigt die Ergebnisse, die diese Abfrage bei einer Neuinstallation des Beispiels zurückgibt:
| Tabellenname | memory_allocated_for_table_kb |
memory_allocated_for_indexes_kb |
|---|---|---|
SpecialOfferProduct_inmem |
64 | 3840 |
DemoSalesOrderHeaderSeed |
1984 | 5,504 |
SalesOrderDetail_inmem |
15316 | 663552 |
DemoSalesOrderDetailSeed |
64 | 10432 |
SpecialOffer_inmem |
3 | 8192 |
SalesOrderHeader_inmem |
7168 | 147456 |
Product_inmem |
124 | 12352 |
Wie Sie sehen können, sind die Tabellen relativ klein: SalesOrderHeader_inmem beträgt etwa 7 MB und SalesOrderDetail_inmem ist etwa 15 MB groß.
Hier fällt auf, dass die den Indizes zugeordnete Arbeitsspeicherkapazität deutlich über der Kapazität der Tabellendaten liegt. Der Grund dafür ist, dass die Hash-Indizes im Beispiel für eine größere Datenmenge vorgesehen sind. Hashindizes weisen eine feste Größe auf, sodass ihre Größe nicht mit der Größe von Daten in der Tabelle vergrößert wird.
Arbeitsspeichernutzung nach dem Ausführen der Arbeitsauslastung
Nach dem Einfügen von 10 Millionen Verkaufsaufträgen sieht die gesamte Speicherauslastung ähnlich der folgenden Abfrage aus:
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Hier ist das Ergebnis.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Standard | 146 |
| MEMORYCLERK_XTP | DB_ID_5 | 7374 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Wie Sie sehen, belegt SQL Server etwas weniger als 8 GB für die speicheroptimierten Tabellen und Indizes in der Beispieldatenbank.
Nach einem Testlauf ergibt sich die folgende Arbeitsspeichernutzung nach Tabellen:
SELECT object_name(t.object_id) AS [Table name],
memory_allocated_for_table_kb,
memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats AS dms
INNER JOIN sys.tables AS t
ON dms.object_id = t.object_id
WHERE t.type = 'U';
Hier ist das Ergebnis.
Table name |
memory_allocated_for_table_kb |
memory_allocated_for_indexes_kb |
|---|---|---|
| SalesOrderDetail_inmem | 5113761 | 663552 |
| DemoSalesOrderDetailSeed | 64 | 10368 |
| SpecialOffer_inmem | 2 | 8192 |
| SalesOrderHeader_inmem | 1575679 | 147456 |
| Product_inmem | 111 | 12032 |
| SpecialOfferProduct_inmem | 64 | 3712 |
| DemoSalesOrderHeaderSeed | 1984 | 5,504 |
Die Gesamtmenge an Daten beträgt etwa 6,5 GB. Die Größe der Indizes in der Tabelle SalesOrderHeader_inmem und SalesOrderDetail_inmem entspricht der Größe der Indizes vor dem Einfügen der Verkaufsaufträge. Die Indexgröße hat sich nicht geändert, da beide Tabellen Hashindizes verwenden, und Hashindizes sind statisch.
Nach dem Zurücksetzen der exemplarischen Arbeitauslastung
Die gespeicherte Prozedur Demo.usp_DemoReset kann verwendet werden, um die exemplarische Workload zurückzusetzen. Sie löscht die Daten in den Tabellen SalesOrderHeader_inmem und SalesOrderDetail_inmemund sendet die Daten erneut aus den ursprünglichen Tabellen SalesOrderHeader und SalesOrderDetail.
Obwohl die Zeilen in den Tabellen gelöscht wurden, bedeutet dies nicht, dass der Speicher sofort wieder beansprucht wird. SQL Server gibt den Arbeitsspeicher, der von den aus speicheroptimierten Tabellen gelöschten Zeilen belegt wurde, nach Bedarf im Hintergrund frei. Sie sehen, dass unmittelbar nach dem Zurücksetzen der Demo, ohne transaktionale Arbeitslast auf dem System, der Arbeitsspeicher noch nicht aus gelöschten Zeilen zurückgefordert wird.
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Hier ist das Ergebnis.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Standard | 2261 |
| MEMORYCLERK_XTP | DB_ID_5 | 7396 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Dies wird erwartet: Der Arbeitsspeicher wird freigegeben, wenn die Transaktionslast ausgeführt wird.
Wenn Sie eine zweite Ausführung der Demo-Workload starten, wird die Arbeitsspeicherauslastung anfangs verringert, da die zuvor gelöschten Zeilen bereinigt werden. Irgendwann nimmt die Arbeitsspeichergröße wieder zu, bis die Workload abgeschlossen ist. Nach dem Einfügen von 10 Millionen Zeilen nach dem Zurücksetzen der Demo ähnelt die Speicherauslastung der Auslastung nach der ersten Ausführung sehr. Zum Beispiel:
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Hier ist das Ergebnis.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Standard | 1,863 |
| MEMORYCLERK_XTP | DB_ID_5 | 7390 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Datenträgernutzung für speicheroptimierte Tabellen
Mithilfe der folgenden Abfrage können Sie ermitteln, wie viel Gesamtspeicherplatz die Prüfpunktdateien einer Datenbank zu einem bestimmten Zeitpunkt auf dem Datenträger belegen:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Anfangszustand
Wenn die Beispieldateigruppe und die speicheroptimierten Beispieltabellen anfänglich erstellt werden, werden mehrere Prüfpunktdateien erstellt, und das System beginnt mit dem Ausfüllen der Dateien – die Anzahl der vorab erstellten Prüfpunktdateien hängt von der Anzahl der logischen Prozessoren im System ab. Da das Beispiel anfangs sehr klein ist, sind die vordefinierten Dateien nach der ersten Erstellung meist leer.
Der folgende Code zeigt die anfängliche Größe des Beispiels auf dem Datenträger eines Computers mit 16 logischen Prozessoren:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Hier ist das Ergebnis.
| Größe auf dem Datenträger (MB) |
|---|
| 2312 |
Wie Sie sehen können, gibt es eine große Diskrepanz zwischen der Größe der Prüfpunktdateien, die 2,3 GB beträgt, und der tatsächlichen Datengröße, die näher an 30 MB liegt.
Mithilfe der folgenden Abfrage können Sie untersuchen, worauf die Datenträgerbelegung zurückzuführen ist. Die Größe auf dem Datenträger, die von dieser Abfrage zurückgegeben wird, stellt bei Dateien mit Status 5 (REQUIRED FOR BACKUP/HA), 6 (IN TRANSITION TO TOMBSTONE) oder 7 (TOMBSTONE) einen Schätzwert dar.
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Für den Anfangszustand des Beispiels sieht das Ergebnis ungefähr wie die folgende Tabelle für einen Server mit 16 logischen Prozessoren aus:
| state_desc | file_type_desc | count | Größe auf dem Datenträger (MB) |
|---|---|---|---|
| PRECREATED | DATEN | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| IM BAU | DATEN | 1 | 128 |
| IM BAU | DELTA | 1 | 8 |
Wie Sie sehen, wird der meiste Speicherplatz durch vorab erstellte Daten- und Änderungsdateien belegt. SQL Server hat ein Paar von Dateien (Daten, Delta) pro logischem Prozessor vorkonfiguriert. Darüber hinaus werden Datendateien bei 128 MB und Delta-Dateien mit 8 MB vorkonfiguriert, um das Einfügen von Daten in diese Dateien effizienter zu gestalten.
Die tatsächlichen Daten der speicheroptimierten Tabellen sind in einer einzelnen Datendatei gespeichert.
Nach dem Ausführen der Arbeitsauslastung
Nach einem einzelnen Testlauf, bei dem 10 Millionen Verkaufsaufträge eingefügt wurden, wird in etwa folgender Gesamtspeicherplatz auf dem Datenträger belegt (Testserver mit 16 Kernen):
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Hier ist das Ergebnis.
| Größe auf dem Datenträger (MB) |
|---|
| 8828 |
Die Größe auf dem Datenträger liegt bei etwa 9 GB. Dies entspricht weitestgehend der Größe der Daten im Arbeitsspeicher.
Im Folgenden sind die Größen der Prüfpunktdateien in den einzelnen Phasen aufgeschlüsselt:
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Hier ist das Ergebnis.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DATEN | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| IM BAU | DATEN | 1 | 128 |
| IM BAU | DELTA | 1 | 8 |
Wir verfügen weiterhin über 16 Paare vordefinierter Dateien, die einsatzbereit sind, sobald Prüfpunkte abgeschlossen werden.
Es gibt ein Paar im Bau, das genutzt wird, bis der aktuelle Checkpoint geschlossen ist. Zusammen mit den aktiven Prüfpunktdateien ergeben 6,5 GB Daten im Arbeitsspeicher eine Datenträgernutzung von ca. 6,5 GB. Denken Sie daran, dass Indizes nicht auf dem Datenträger beibehalten werden, und daher ist die Gesamtgröße auf dem Datenträger kleiner als die Größe im Arbeitsspeicher in diesem Fall.
Nach dem Zurücksetzen der exemplarischen Arbeitauslastung
Nach dem Zurücksetzen der Demo wird der Speicherplatz nicht sofort freigegeben, wenn kein Transaktionsworkload auf dem System vorhanden ist und keine Datenbankprüfpunkte gesetzt sind. Damit Prüfpunktdateien durch ihre verschiedenen Phasen bewegt werden und schließlich verworfen werden, müssen mehrere Prüfpunkte und Protokollverkürzungsereignisse geschehen, um das Zusammenführen von Prüfpunktdateien durchzuführen und die Speicherbereinigung zu starten. Dies geschieht automatisch, wenn Sie im System über eine Transaktionsworkload verfügen (und regelmäßige Protokollsicherungen durchführen, falls Sie das VOLLSTÄNDIGE Wiederherstellungsmodell verwenden), aber nicht, wenn das System im Leerlauf ist, wie in einem Demoszenario.
Im Beispiel sehen Sie nach dem Zurücksetzen der Demo möglicherweise etwas wie:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Hier ist das Ergebnis.
| Größe auf dem Datenträger (MB) |
|---|
| 11839 |
Mit fast 12 GB liegen die Ergebnisse deutlich über den 9 GB vor dem Zurücksetzen der exemplarischen Arbeitsauslastung. Dies liegt daran, dass einige Checkpoint-Dateizusammenführungen gestartet wurden, aber einige der Zusammenführungsziele noch nicht verwendet wurden und einige der Zusammenführungsquelldateien noch nicht bereinigt wurden, wie im folgenden Beispiel ersichtlich ist:
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Hier ist das Ergebnis.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DATEN | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| AKTIV | DATEN | 38 | 5152 |
| AKTIV | DELTA | 38 | 1331 |
| ZIEL ZUM ZUSAMMENFÜHREN | DATEN | 7 | 896 |
| ZIEL ZUM ZUSAMMENFÜHREN | DELTA | 7 | 56 |
| ZUSAMMENGEFÜHRTE QUELLE | DATEN | 13 | 1,772 |
| ZUSAMMENGEFÜHRTE QUELLE | DELTA | 13 | 4:55 |
Sobald Transaktionsaktivitäten im System auftreten, werden Zusammenführungsziele installiert und zusammengeführte Quelldateien bereinigt.
Nach einer zweiten Ausführung der Demo-Workload, die nach dem Zurücksetzen der Demo 10 Millionen Verkaufsaufträge einfügt, sehen Sie, dass die dateien, die während der ersten Ausführung der Workload erstellt wurden, bereinigt wurden. Wenn Sie die vorherige Abfrage mehrmals ausführen, während die Workload ausgeführt wird, können Sie sehen, dass die Prüfpunktdateien durch die verschiedenen Phasen gelangen.
Nach der zweiten Ausführung der Workload werden 10 Millionen Verkaufsaufträge eingefügt. Die Datenträgerauslastung ähnelt der nach der ersten Ausführung sehr, ist aber nicht unbedingt mit dieser identisch, da das System von Natur aus dynamisch ist. Zum Beispiel:
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Hier ist das Ergebnis.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DATEN | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| IM BAU | DATEN | 2 | 268 |
| IM BAU | DELTA | 2 | 16 |
| AKTIV | DATEN | 41 | 5608 |
| AKTIV | DELTA | 41 | 328 |
In diesem Fall gibt es zwei Prüfpunktdateipaare im UNDER CONSTRUCTION Zustand, was bedeutet, dass mehrere Dateipaare in den UNDER CONSTRUCTION Zustand verschoben wurden, wahrscheinlich aufgrund der hohen Parallelität in der Workload. Mehrere gleichzeitige Threads erfordern gleichzeitig ein neues Dateipaar und verschieben damit ein Paar von PRECREATED zu UNDER CONSTRUCTION.