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.
Überblick
In diesem Beispiel wird das neue In-Memory OLTP-Feature vorgestellt, das Teil von SQL Server 2014 ist. Es zeigt die neuen speicheroptimierten Tabellen und nativ kompilierten gespeicherten Prozeduren und kann verwendet werden, um die Leistungsvorteile von In-Memory OLTP zu veranschaulichen.
Hinweis
Um dieses Thema für SQL Server 2016 anzusehen, siehe Erweiterungen von AdventureWorks zur Demonstration von In-Memory OLTP
Im Beispiel werden 5 Tabellen der AdventureWorks-Datenbank zu speicheroptimierten Tabellen migriert, und es enthält eine Demo-Workload für die Auftragsverarbeitung. Sie können diese Demo-Workload verwenden, um den Leistungsvorteil der Verwendung von In-Memory OLTP auf Ihrem Server zu sehen.
In der Beschreibung des Beispiels erläutern wir die Kompromisse, die bei der Migration der Tabellen zu In-Memory OLTP vorgenommen wurden, um die Features zu berücksichtigen, die für speicheroptimierte Tabellen in SQL Server 2014 noch nicht unterstützt werden.
Die Dokumentation dieses Beispiels ist wie folgt strukturiert:
Voraussetzungen für die Installation des Beispiels und Ausführen der Demo-Workload
Anweisungen zum Installieren des In-Memory OLTP-Beispiels basierend auf AdventureWorks
Beschreibung der Beispieltabellen und -prozeduren – dies umfasst Beschreibungen der Tabellen und Prozeduren, die AdventureWorks vom OLTP-Beispiel In-Memory hinzugefügt wurden, sowie Überlegungen zum Migrieren einiger der ursprünglichen AdventureWorks-Tabellen zu speicheroptimierten Tabellen.
Anweisungen zum Ausführen von Leistungsmessungen mithilfe der Demo-Workload – dies umfasst Anweisungen zum Installieren und Ausführen von ostress, einem Tool zur Steuerung der Workload, sowie zur Ausführung der Demo-Workload selbst.
Arbeitsspeicher- und Festplattenspeicherauslastung im Beispiel
Voraussetzungen
SQL Server 2014 RTM – Auswertung, Entwickler oder Enterprise Edition
Für Leistungstests ist ein Server mit Spezifikationen erforderlich, die den Spezifikationen Ihrer Produktionsumgebung ähneln. Für dieses bestimmte Beispiel sollten Sie mindestens 16 GB Arbeitsspeicher für SQL Server verfügbar haben. Allgemeine Richtlinien für Hardware für In-Memory OLTP finden Sie im folgenden Blogbeitrag:https://cloudblogs.microsoft.com/sqlserver/2013/08/01/hardware-considerations-for-in-memory-oltp-in-sql-server-2014/
Installieren des In-Memory OLTP-Beispiels basierend auf AdventureWorks
Führen Sie die folgenden Schritte aus, um das Beispiel zu installieren:
Laden Sie das Archiv für die vollständige Sicherung der AdventureWorks2014-Datenbank herunter:
Öffnen Sie Folgendes: https://msftdbprodsamples.codeplex.com/downloads/get/880661.
Wenn Sie aufgefordert werden, die Datei in einem lokalen Ordner zu speichern.
Extrahieren Sie die AdventureWorks2014.bak Datei in einen lokalen Ordner, z. B. "c:\temp".
Wiederherstellen der Datenbanksicherung mit Transact-SQL oder SQL Server Management Studio:
Identifizieren Des Zielordners und Dateinamens für die Datendatei, z. B.
"h:\DATA\AdventureWorks2014_Data.mdf"
Identifizieren Des Zielordners und Dateinamens für die Protokolldatei, z. B.
'i:\DATA\AdventureWorks2014_log.ldf'
- Die Protokolldatei sollte auf einem anderen Laufwerk als die Datendatei platziert werden, idealerweise ein Laufwerk mit geringer Latenz, z. B. ein SSD- oder PCIe-Speicher, um maximale Leistung zu erzielen.
Beispiel-T-SQL-Skript:
RESTORE DATABASE [AdventureWorks2014] FROM DISK = N'C:\temp\AdventureWorks2014.bak' WITH FILE = 1, MOVE N'AdventureWorks2014_Data' TO N'h:\DATA\AdventureWorks2014_Data.mdf', MOVE N'AdventureWorks2014_Log' TO N'i:\DATA\AdventureWorks2014_log.ldf' GOÄndern Sie den Datenbankbesitzer in eine Anmeldung auf Ihrem Server, indem Sie den folgenden Befehl im Abfragefenster von SQL Server Management Studio ausführen:
ALTER AUTHORIZATION ON DATABASE::AdventureWorks2014 TO [<NewLogin>]Laden Sie das Beispielskript "SQL Server 2014 RTM In-Memory OLTP-Sample.sql" aus SQL Server 2014 RTM In-Memory OLTP-Beispiel in einen lokalen Ordner herunter.
Aktualisieren Sie den Wert für die Variable "checkpoint_files_location" im Skript "SQL Server 2014 RTM In-Memory OLTP-Sample.sql", um auf den Zielspeicherort für die In-Memory OLTP-Prüfpunktdateien zu verweisen. Die Prüfpunktdateien sollten auf einem Laufwerk mit guter sequenzieller E/A-Leistung platziert werden.
Aktualisieren Sie den Wert für die Variable "database_name", um auf die AdventureWorks2014-Datenbank zu verweisen.
Achten Sie darauf, den Backslash "\" als Teil des Pfadnamens einzuschließen.
Beispiel:
:setvar checkpoint_files_location "d:\DBData\" ... :setvar database_name "AdventureWorks2014"
Führen Sie das Beispielskript auf eine von zwei Arten aus:
Verwenden des Befehlszeilenprogramms "sqlcmd". Zum Beispiel, indem Sie den folgenden Befehl über die Eingabeaufforderung im Ordner ausführen, der das Skript enthält:
sqlcmd -S . -E -i "ssSQL14 RTM hek_2 Sample.sql"Verwenden von Management Studio:
Öffnen des Skripts "SQL Server 2014 RTM In-Memory OLTP-Sample.sql" in einem Abfragefenster
Herstellen einer Verbindung mit dem Zielserver, der die Datenbank AdventureWorks2014 enthält
Aktivieren Sie den SQLCMD-Modus, indem Sie auf "Abfrage -> SQLCMD-Modus" klicken.
Klicken Sie auf die Schaltfläche "Ausführen", um das Skript auszuführen.
Beschreibung der Beispieltabellen und -verfahren
Im Beispiel werden neue Tabellen für Produkte und Verkaufsaufträge erstellt, die auf vorhandenen Tabellen in AdventureWorks basieren. Das Schema der neuen Tabellen ähnelt den vorhandenen Tabellen mit einigen Unterschieden, wie unten 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.
Beachten Sie, dass die speicheroptimierten Tabellen, die in der Workload für den Leistungsvergleich verwendet werden, vollständig dauerhaft und vollständig protokolliert sind. Sie opfern keine Haltbarkeit oder Zuverlässigkeit, um den Leistungsgewinn zu erzielen.
Die Zielarbeitsauslastung für dieses Beispiel ist die Auftragsverarbeitung, bei der wir auch Informationen zu Produkten und Rabatten berücksichtigen. Zu diesem Zweck werden die Tabellen "SalesOrderHeader", "SalesOrderDetail", "Product", "SpecialOffer" und "SpecialOfferProduct" verwendet.
Zwei neue gespeicherte Prozeduren, Sales.usp_InsertSalesOrder_inmem und Sales.usp_UpdateSalesOrderShipInfo_inmem, werden verwendet, um Verkaufsaufträge einzufügen und die Versandinformationen eines bestimmten Verkaufsauftrags zu aktualisieren.
Das neue Schema "Demo" enthält Hilfstabellen und gespeicherte Prozeduren zum Ausführen einer Demo-Workload.
Konkret fügt das In-Memory OLTP-Beispiel die folgenden Objekte zu AdventureWorks hinzu:
Vom Beispiel hinzugefügte Tabellen
Die neuen Tabellen
Sales.SalesOrderHeader_inmem
- Kopfzeileninformationen zu Verkaufsaufträgen. Jeder Verkaufsauftrag hat eine Zeile in dieser Tabelle.
Sales.SalesOrderDetail_inmem
- Details zu Verkaufsaufträgen. Jede Position eines Verkaufsauftrags hat eine Zeile in dieser Tabelle.
Sales.SpecialOffer_inmem
- Informationen zu Sonderangeboten, einschließlich des Rabattprozentsatzes, der jedem Sonderangebot zugeordnet ist.
Sales.SpecialOfferProduct_inmem
- Referenztabelle 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 des Listenpreises.
Demo.DemoSalesOrderDetailSeed
- Wird in der Demo-Workload zum Erstellen von Beispielverkaufsaufträgen verwendet.
Datenträgerbasierte Variationen der Tabellen:
Sales.SalesOrderHeader_ondisk
Sales.SalesOrderDetail_ondisk
Sales.Sonderangebot_ondisk
Sales.SpecialOfferProduct_ondisk
Production.Product_ondisk
Unterschiede zwischen ursprünglichen datenträgerbasierten und neuen speicheroptimierten Tabellen
In den meisten Fällen verwenden die in diesem Beispiel eingeführten neuen Tabellen dieselben Spalten und dieselben Datentypen wie die ursprünglichen Tabellen. Es gibt jedoch einige Unterschiede. Wir führen die folgenden Unterschiede zusammen mit einer Begründung für die Änderungen auf.
Sales.SalesOrderHeader_inmem
Standardeinschränkungen werden für speicheroptimierte Tabellen unterstützt, und die meisten Standardeinschränkungen haben wir unverändert migriert. Die ursprüngliche Tabelle "Sales.SalesOrderHeader" enthält jedoch zwei Standardeinschränkungen, die das aktuelle Datum für die Spalten "OrderDate" und "ModifiedDate" abrufen. Bei einer hohen Durchsatz-Auftragsverarbeitung mit viel Parallelität kann jede globale Ressource zu einem Konfliktpunkt werden. 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 Bestellungen einfügt, insbesondere dann, wenn die Systemzeit für mehrere Spalten im Kopfteil der Bestellung sowie in den Bestelldetails abgerufen werden muss. Das Problem wird in diesem Beispiel behoben, indem die Systemzeit nur einmal für jeden eingefügten Verkaufsauftrag abgerufen und dieser Wert für die Datetime-Spalten in SalesOrderHeader_inmem und SalesOrderDetail_inmem in der gespeicherten Prozedur Sales.usp_InsertSalesOrder_inmem verwendet wird.
Alias-UDTs – Die ursprüngliche Tabelle verwendet zwei benutzerdefinierte Alias-Datentypen (UDTs) dbo. OrderNumber und dbo. AccountNumber, für die Spalten PurchaseOrderNumber bzw. AccountNumber. SQL Server 2014 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 "SalesPersonID" nullfähig, während in den neuen Tabellen die Spalte nicht nullfähig ist und eine Standardeinschränkung mit dem Wert (-1) aufweist. Dies 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 "SalesOrderNumber" und "TotalDue" werden weggelassen, da sql Server 2014 berechnete Spalten in speicheroptimierten Tabellen nicht unterstützt. Die neue Ansicht Sales.vSalesOrderHeader_extended_inmem reflektiert die Spalten "SalesOrderNumber" und "TotalDue". Daher können Sie diese Ansicht verwenden, wenn diese Spalten erforderlich sind.
Fremdschlüsseleinschränkungen werden für speicheroptimierte Tabellen in SQL Server 2014 nicht unterstützt. Darüber hinaus ist SalesOrderHeader_inmem eine Hot-Tabelle im Beispielworkload, und Fremdschlüsseleinschränkungen erfordern zusätzliche Verarbeitung für alle DML-Vorgänge, da es Nachschlagevorgänge in allen anderen Tabellen erfordert, auf die in diesen Einschränkungen verwiesen wird. Daher wird davon ausgegangen, dass die App die referenzielle Integrität sicherstellt und die referenzielle Integrität beim Einfügen von Zeilen nicht überprüft wird. Referenzielle Integrität für die Daten in dieser Tabelle kann mithilfe der gespeicherten Prozedur dbo.usp_ValidateIntegrity mithilfe des folgenden Skripts überprüft werden:
DECLARE @o int = object_id(N'Sales.SalesOrderHeader_inmem') EXEC dbo.usp_ValidateIntegrity @oCheck constraints werden für speicheroptimierte Tabellen in SQL Server 2014 nicht unterstützt. Die Domänenintegrität wird zusammen mit der referenziellen Integrität mithilfe dieses Skripts überprüft:
DECLARE @o int = object_id(N'Sales.SalesOrderHeader_inmem') EXEC dbo.usp_ValidateIntegrity @oRowguid - Die Rowguid-Spalte wird weggelassen. Während eindeutiger Bezeichner für speicheroptimierte Tabellen unterstützt wird, wird die Option ROWGUIDCOL in SQL Server 2014 nicht unterstützt. Spalten dieser Art werden in der Regel für die Zusammenführungsreplikation oder Tabellen mit Filestream-Spalten verwendet. Dieses Beispiel enthält keines.
Verkauf.Auftragsdetails
Standardeinschränkungen – ähnlich wie SalesOrderHeader – die Standardeinschränkung, die das Systemdatum/die Systemzeit erfordert, wird nicht migriert, sondern die gespeicherte Prozedur, die Verkaufsaufträge einfügt, übernimmt das Einfügen des aktuellen Systemdatums/der aktuellen Systemzeit beim ersten Einfügen.
Berechnete Spalten – Die berechnete Spalte "LineTotal" wurde nicht migriert, da berechnete Spalten mit speicheroptimierten Tabellen in SQL Server 2014 nicht unterstützt werden. Um auf diese Spalte zuzugreifen, verwenden Sie die Ansicht Sales.vSalesOrderDetail_extended_inmem.
Rowguid - Die Rowguid-Spalte wird weggelassen. Ausführliche Informationen finden Sie in der Beschreibung der Tabelle "SalesOrderHeader".
Informationen zu Check- und Fremdschlüsselbeschränkungen finden Sie in der Beschreibung von SalesOrderHeader. Das folgende Skript kann verwendet werden, um die Domänen- und referenzielle Integrität für diese Tabelle zu überprüfen:
DECLARE @o int = object_id(N'Sales.SalesOrderHeader_inmem') EXEC dbo.usp_ValidateIntegrity @o
Produktion.Produkt
Alias-UDTs – die ursprüngliche Tabelle verwendet den benutzerdefinierten Datentyp dbo. Flag, das dem Bit des Systemdatentyps entspricht. Die migrierte Tabelle verwendet stattdessen den Bitdatentyp.
BIN2-Sortierung – Der Spaltenname und "ProductNumber" sind in Indexschlüsseln enthalten und müssen daher BIN2-Sortierungen in SQL Server 2014 aufweisen. Hier wird davon ausgegangen, dass die App keine Sortierungsdetails benötigt, wie zum Beispiel Insensitivität gegenüber Groß-/Kleinschreibung.
Rowguid - Die Rowguid-Spalte wird weggelassen. Ausführliche Informationen finden Sie in der Beschreibung der Tabelle "SalesOrderHeader".
Eindeutige, Check - und Foreign Key-Einschränkungen werden auf zwei Arten berücksichtigt: die gespeicherten Prozeduren Product.usp_InsertProduct_inmem und Product.usp_DeleteProduct_inmem können verwendet werden, um Produkte einzufügen und zu löschen; Diese Verfahren überprüfen die Domänen- und referenzielle Integrität und schlagen fehl, wenn die Integrität verletzt wird. Darüber hinaus kann das folgende Skript verwendet werden, um die Domänen- und referenzielle Integrität wie folgt zu überprüfen:
DECLARE @o int = object_id(N'Production.Product') EXEC dbo.usp_ValidateIntegrity @o- Beachten Sie, dass die Speicherprozeduren usp_InsertProduct_inmem und usp_DeleteProduct_inmem nur Fremdschlüssel zwischen den migrierten Tabellen in Betracht ziehen. Verweise auf andere Tabellen "ProductModel", "ProductSubcategory" und "UnitMeasure" werden nicht berücksichtigt.
Verkaufs.Sonderangebot
Check- und Foreign Key-Einschränkungen werden auf zwei Arten berücksichtigt: Die gespeicherten Prozeduren Sales.usp_InsertSpecialOffer_inmem und Sales.usp_DeleteSpecialOffer_inmem können verwendet werden, um Sonderangebote einzufügen und zu löschen. Diese gespeicherten Prozeduren überprüfen die Domänen- und referenzielle Integrität und schlagen fehl, wenn die Integrität verletzt wird. Darüber hinaus kann das folgende Skript verwendet werden, um die Domänen- und referenzielle Integrität wie folgt zu überprüfen:
DECLARE @o int = object_id(N'Sales.SpecialOffer_inmem') EXEC dbo.usp_ValidateIntegrity @oRowguid - Die Rowguid-Spalte wird weggelassen. Ausführliche Informationen finden Sie in der Beschreibung der Tabelle "SalesOrderHeader".
Verkauf.Sonderangebotsprodukt
Fremdschlüsseleinschränkungen werden auf zwei Arten berücksichtigt: Die gespeicherte Prozedur Sales.usp_InsertSpecialOfferProduct_inmem kann verwendet werden, um Beziehungen zwischen Sonderangeboten und Produkten einzufügen; Diese Verfahren überprüfen die referenzielle Integrität und schlagen fehl, wenn die Integrität verletzt wird. Darüber hinaus kann das folgende Skript verwendet werden, um die referenzielle Integrität wie folgt zu überprüfen:
DECLARE @o int = object_id(N'Sales.SpecialOfferProduct_inmem') EXEC dbo.usp_ValidateIntegrity @oRowguid - Die Rowguid-Spalte wird weggelassen. Ausführliche Informationen finden Sie in der Beschreibung der Tabelle "SalesOrderHeader".
Überlegungen zu Indizes für speicheroptimierte Tabellen
Der Basisindex für speicheroptimierte Tabellen ist der NONCLUSTERED-Index, der Punktsuchvorgänge (Indexsuche für Gleichheitsprädikat), Bereichsscans (Indexsuche im Ungleichheitsprädikat), vollständige Indexscans und sortierte Scans unterstützt. Darüber hinaus unterstützen NONCLUSTERED-Indizes die Suche nach führenden Spalten des Indexschlüssels. Tatsächlich unterstützen speicheroptimierte NONCLUSTERED-Indizes alle Vorgänge, die von datenträgerbasierten NONCLUSTERED-Indizes unterstützt werden, außer bei Rückwärtsscans. Daher ist die Verwendung von NONCLUSTERED-Indizes eine sichere Wahl für Ihre Indizes.
Hashindizes können verwendet werden, um die Workload weiter zu optimieren. Sie sind besonders für Punktsuchvorgänge und Zeileneinfügungen optimiert. Man muss jedoch berücksichtigen, dass Bereichsscans, sortierte Scans oder die Suche in den führenden Indexschlüsselspalten nicht unterstützt werden. Daher muss bei der Verwendung dieser Indizes vorsichtig sein. Darüber hinaus ist es erforderlich, den „bucket_count“ bei der Erstellung anzugeben. Es sollte in der Regel auf das Ein- bis Zweifache der Anzahl von Indexschlüsselwerten eingestellt werden, jedoch ist eine Überschätzung normalerweise kein Problem.
Weitere Informationen zu Indexrichtlinien und Richtlinien für die Auswahl des richtigen bucket_count finden Sie unter "Onlinebücher".
Die Indizes der migrierten Tabellen wurden für die Demo-Arbeitsauslastung für die Auftragsverarbeitung optimiert. Die Arbeitsauslastung basiert auf Einfügungen und Punktsuchvorgängen in den Tabellen Sales.SalesOrderHeader_inmem und Sales.SalesOrderDetail_inmem, und sie basiert auch auf Punktsuchvorgängen in den Primärschlüsselspalten in den Tabellen Production.Product_inmem und Sales.SpecialOffer_inmem.
Aus Leistungsgründen hat Sales.SalesOrderHeader_inmem drei Indizes, die alle HASH-Indizes sind, da für die Workload keine sortierten Scans oder Bereichsscans erforderlich sind.
HASH-Index für (SalesOrderID): bucket_count beträgt 10 Millionen (aufgerundet auf 16 Millionen), da die erwartete Anzahl von Verkaufsaufträgen 10 Millionen beträgt.
HASH-Index für (SalesPersonID): bucket_count beträgt 1 Million. Das bereitgestellte Dataset verfügt nicht über viele Vertriebsmitarbeiter, aber dies ermöglicht zukünftiges Wachstum, und Sie zahlen keine Leistungseinbußen für Punktabfragen, wenn die *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 hat drei Indizes, die alle aus Gründen der Leistung als HASH-Indizes gestaltet sind, und weil für die Aufgaben keine sortierten Scans oder Bereichsscans erforderlich sind.
HASH-Index für (SalesOrderID, SalesOrderDetailID): Dies ist der Primärschlüsselindex, und auch wenn Abfragen nach (SalesOrderID, SalesOrderDetailID) selten sind, verbessert die Verwendung eines Hash-Index für den Schlüssel die Geschwindigkeit der Zeileneinfügungen. Die bucket_count beträgt 50 Millionen (aufgerundet auf 67 Millionen): Die erwartete Anzahl von Verkaufsaufträgen beträgt 10 Millionen, und die Größe beträgt durchschnittlich 5 Artikel pro Bestellung.
HASH-Index on (SalesOrderID): Nachschlagevorgänge nach Verkaufsauftrag sind häufig: Sie möchten alle Positionen finden, die einer einzelnen Bestellung entsprechen. bucket_count beträgt 10 Millionen (aufgerundet auf 16 Millionen), da die erwartete Anzahl der Bestellungen 10 Millionen beträgt.
HASH-Index auf (ProductID): bucket_count beträgt 1 Million. Der bereitgestellte Datensatz enthält nicht viele Produkte, aber dies ermöglicht zukünftiges Wachstum.
Production.Product_inmem weist drei Indizes auf
HASH-Index auf (ProductID): Abfragen auf ProductID liegen im kritischen Pfad für die Demo-Workload, daher handelt es sich um einen Hash-Index.
Nicht gruppierter Index auf (Name): Dies ermöglicht geordnete Scans der Produktnamen.
NONCLUSTERED-Index über (ProductNumber): Dies ermöglicht geordnete Scans von Produktnummern.
Sales.SpecialOffer_inmem hat einen HASH-Index für (SpecialOfferID): Punkt-Nachschlagevorgänge von Sonderangeboten befinden sich im kritischen Teil der Demo-Workload. Die bucket_count beträgt 1 Millionen, um zukünftiges Wachstum zu ermöglichen.
Sales.SpecialOfferProduct_inmem wird in der Demo-Workload nicht referenziert, daher besteht keine Notwendigkeit, Hash-Indizes für diese Tabelle zu verwenden, um die Workload zu optimieren – die Indizes für (SpecialOfferID, ProductID) und (ProductID) sind nicht geclustert.
Beachten Sie, dass einige der bucket_counts übergroß sind, jedoch nicht die bucket_counts für die Indizes auf SalesOrderHeader_inmem und SalesOrderDetail_inmem. Diese wurden nur für 10 Millionen Verkaufsaufträge dimensioniert. 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 nicht genügend Arbeitsspeicher fehlschlägt. Wenn Sie wirklich weit über 10 Millionen Verkaufsaufträge hinaus skalieren möchten, können Sie die Anzahl der Buckets entsprechend erhöhen.
Überlegungen zur Speicherauslastung
Die Speicherauslastung in der Beispieldatenbank wird sowohl vor als auch nach dem Ausführen der Demo-Workload in der Abschnittsspeicherauslastung für die speicheroptimierten Tabellen erläutert.
Gespeicherte Prozeduren, die vom Beispiel hinzugefügt wurden
Die beiden wichtigsten gespeicherten Verfahren zum Einfügen von Verkaufsauftrags- und Aktualisierungsversanddetails sind wie folgt:
Sales.usp_InsertSalesOrder_inmem
Fügt eine neue Verkaufsbestellung in die Datenbank ein und gibt die SalesOrderID für diesen Verkaufsauftrag aus. Als Eingabeparameter werden Details für den Verkaufsauftragskopf sowie die Positionen im Verkaufsauftrag benötigt.
Ausgabeparameter:
- @SalesOrderID int - die SalesOrderID für den soeben eingefügten Verkaufsauftrag
Eingabeparameter (erforderlich):
@DueDate datetime2
@CustomerID Int
@BillToAddressID [int]
@ShipToAddressID [int]
@ShipMethodID [int]
@SalesOrderDetails Sales.SalesOrderDetailType_inmem - TVP, das die Positionen der Bestellung enthält
Eingabeparameter (optional):
@Status [tinyint]
@OnlineOrderFlag [bit]
@PurchaseOrderNumber [nvarchar](25)
@AccountNumber [nvarchar](15)
@SalesPersonID [int]
@TerritoryID [int]
@CreditCardID [int]
@CreditCardApprovalCode [varchar](15)
@CurrencyRateID [int]
@Comment nvarchar(128)
Sales.usp_UpdateSalesOrderShipInfo_inmem
Aktualisieren Sie 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 nativ kompilierte gespeicherte Prozedur Sales.usp_UpdateSalesOrderShipInfo_native mit Wiederholungslogik, um (unerwartete) potenzielle Konflikte mit gleichzeitigen Transaktionen zu behandeln, die dieselbe Bestellung aktualisieren. Weitere Informationen zur Wiederholungslogik finden Sie im Thema "Onlinebücher" hier.
Sales.usp_UpdateSalesOrderShipInfo_native
- Dies ist die nativkompilierte gespeicherte Prozedur, die die Aktualisierung der Versandinformationen tatsächlich verarbeitet. Es soll aus der gespeicherten Wrapper-Prozedur Sales.usp_UpdateSalesOrderShipInfo_inmem aufgerufen werden. Wenn der Client Fehler behandeln kann und eine Wiederholungslogik implementiert, können Sie diese Prozedur direkt aufrufen, anstatt die gespeicherte Wrapper-Prozedur zu verwenden.
Die folgende gespeicherte Prozedur wird für die Demoworkload verwendet.
Demo.usp_DemoReset
- Setzt die Demo zurück, indem die Tabellen "SalesOrderHeader" und "SalesOrderDetail" geleert und neu befüllt werden.
Die folgenden gespeicherten Prozeduren werden zum Einfügen und Löschen aus speicheroptimierten Tabellen verwendet, wobei die Domänen- und referenzielle Integrität gewährleistet wird.
Production.usp_InsertProduct_inmem
Production.usp_DeleteProduct_inmem
Sales.usp_InsertSpecialOffer_inmem
Sales.usp_DeleteSpecialOffer_inmem
Sales.usp_InsertSpecialOfferProduct_inmem
Schließlich wird die folgende gespeicherte Prozedur verwendet, um die Domänen- und referenzielle Integrität zu überprüfen.
dbo.usp_ValidateIntegrity
Optionaler Parameter: @object_id - ID des Objekts zur Überprüfung der Integrität für
Dieses Verfahren basiert auf den Tabellen dbo. DomainIntegrity, dbo. ReferentialIntegrity und dbo. UniqueIntegrity für die Integritätsregeln, die überprüft werden müssen – im Beispiel werden diese Tabellen basierend auf der Überprüfung, dem Fremdschlüssel und eindeutigen Einschränkungen aufgefüllt, die für die ursprünglichen Tabellen in der AdventureWorks-Datenbank vorhanden sind.
Es basiert auf den Hilfsprozeduren dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKCheck und dbo.GenerateUQCheck, um das benötigte T-SQL zur Durchführung der Integritätsprüfungen zu erzeugen.
Leistungsmessungen unter Verwendung der Demonstrationsarbeitslast
Ostress ist ein Befehlszeilentool, das vom Microsoft CSS SQL Server-Supportteam entwickelt wurde. Dieses Tool kann verwendet werden, um Abfragen auszuführen oder gespeicherte Prozeduren parallel auszuführen. 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 führt die Threads aus und führt die Anweisung für alle Threads parallel aus. Nachdem die Ausführung für alle Threads abgeschlossen ist, meldet ostress die Zeit, die für alle Threads benötigt wird, um die Ausführung abzuschließen.
Installieren von ostress
Ostress wird als Teil der RML Utilities installiert; es gibt keine eigenständige Installation für Ostress.
Installationsschritte:
Laden Sie das x64-Installationspaket für die RML-Dienstprogramme von der folgenden Seite herunter, und führen Sie es aus: https://blogs.msdn.com/b/psssql/archive/2013/10/29/cumulative-update-2-to-the-rml-utilities-for-microsoft-sql-server-released.aspx
Wenn ein Dialogfeld angezeigt wird, in dem bestimmte Dateien verwendet werden, klicken Sie auf 'Weiter'.
Ausführen von Stress
Ostress wird über die Befehlszeilenaufforderung ausgeführt. Es ist am bequemsten, das Tool aus der "RML Cmd Prompt" auszuführen, die als Teil der RML Utilities installiert ist.
Gehen Sie wie folgt vor, um die RML Cmd-Eingabeaufforderung zu öffnen:
Öffnen Sie in Windows Server 2012 [R2] und in Windows 8 und 8.1 das Startmenü, indem Sie auf die Windows-Taste klicken und "rml" eingeben. Klicken Sie auf "RML Cmd Prompt", die sich in der Liste der Suchergebnisse befindet.
Stellen Sie sicher, dass sich die Eingabeaufforderung im Installationsordner "RML Utilities" befindet. Beispiel:

Die Befehlszeilenoptionen für Ostress können angezeigt werden, wenn Sie einfach ostress.exe ohne Befehlszeilenoptionen ausführen. Die wichtigsten Optionen für die Ausführung von ostress mit diesem Beispiel sind:
-S Name der MicrosoftSQL Server-Instanz, mit der eine Verbindung hergestellt werden soll
-E die Windows-Authentifizierung verwenden, um eine Verbindung herzustellen (Standard); Wenn Sie die SQL Server-Authentifizierung verwenden, verwenden Sie die Optionen -U und -P, um den Benutzernamen bzw. das Kennwort anzugeben.
-d Name der Datenbank, für dieses Beispiel AdventureWorks2014
-Q die auszuführende T-SQL-Anweisung
-n Anzahl der Verbindungen, die jede Eingabedatei/Abfrage verarbeiten
-ist die Anzahl der Iterationen pro Verbindung zur Ausführung jeder Eingabedatei/Abfrage.
Demoworkload
Die wichtigste Speicherprozedur, die in der Demolast verwendet wird, ist Sales.usp_InsertSalesOrder_inmem/ondisk. Das Skript unten erstellt einen Tabellenwertparameter (TVP) mit Beispieldaten und ruft die Prozedur auf, um einen Verkaufsauftrag mit 5 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 Demo nach jeder Ausführung des Stresslaufs Demo.usp_DemoReset zurück. Mit diesem Verfahren 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 für die Auftragsverarbeitung zu simulieren:
DECLARE
@i int = 0,
@od Sales.SalesOrderDetailType_inmem,
@SalesOrderID int,
@DueDate datetime2 = sysdatetime(),
@CustomerID int = rand() * 8000,
@BillToAddressID int = rand() * 10000,
@ShipToAddressID int = rand() * 10000,
@ShipMethodID 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;
EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od;
SET @i += 1
END
Mit diesem Skript wird jeder erstellte Beispielauftrag 20 Mal eingefügt, wobei 20 gespeicherte Prozeduren in einer WHILE-Schleife ausgeführt werden. Die Schleife wird verwendet, um die Tatsache zu berücksichtigen, dass die Datenbank zum Erstellen der Beispielreihenfolge verwendet wird. Die mittelstufige Anwendung erstellt den Kundenauftrag zur Einfügung in typischen Produktionsumgebungen.
Das obige 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 zu steuern, und der Parameter "r", um zu steuern, wie oft das Skript für jede Verbindung ausgeführt wird.
Funktionale Validierung der Arbeitslast
Um zu überprüfen, ob alles funktioniert, beginnen wir mit einem Beispieltest, bei dem 10 gleichzeitige Verbindungen und 5 Iterationen verwendet werden, um insgesamt 10 * 5 * 20 = 1000 Verkaufsaufträge einzufügen.
Mit dem folgenden Befehl wird davon ausgegangen, dass die Standardinstanz auf dem lokalen Computer verwendet wird. Wenn Sie eine benannte Instanz oder einen Remoteserver verwenden, ändern Sie den Servernamen entsprechend, indem Sie den Parameter -S verwenden.
Fügen Sie 1000 Verkaufsaufträge in speicheroptimierte Tabellen ein, und verwenden Sie den folgenden Befehl in der RML Cmd-Eingabeaufforderung:
Klicken Sie auf die Schaltfläche "Kopieren", um den Befehl zu kopieren, und fügen Sie ihn in die Befehlszeile für RML-Hilfsprogramme ein.
ostress.exe -n10 -r5 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID 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; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"
Wenn alles wie erwartet funktioniert, sieht Ihr Befehlsfenster ähnlich wie folgt aus. Fehlermeldungen werden nicht erwartet.

Überprüfen Sie, ob auch die Arbeitsauslastung für datenträgerbasierte Tabellen wie erwartet funktioniert, indem Sie den folgenden Befehl in der RML Cmd-Eingabeaufforderung ausführen:
Klicken Sie auf die Schaltfläche "Kopieren", um den Befehl zu kopieren, und fügen Sie ihn in die Befehlszeile für RML-Hilfsprogramme ein.
ostress.exe -n10 -r5 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID 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; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"
Ausführen der Arbeitslast
Um in großem Maßstab zu testen, fügen wir 10 Millionen Verkaufsaufträge mit 100 Verbindungen ein. Dieser Test führt auf einem bescheidenen Server (z. B. 8 physische, 16 logische Kerne) und einfachem SSD-Speicher für das Protokoll vernünftig aus. 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 Arbeitslast verwenden wir die verstrichene Zeit, wie von ostress.exe nach deren Ausführung gemeldet.
Speicheroptimierte Tabellen
Wir beginnen damit, die Workload auf speicheroptimierten Tabellen auszuführen. Der folgende Befehl öffnet 100 Threads, die jeweils für 5.000 Iterationen ausgeführt werden. Jede Iteration fügt 20 Verkaufsaufträge in separate Transaktionen ein. Es gibt 20 Einfügungen pro Iteration, um die Tatsache zu kompensieren, dass die Datenbank verwendet wird, um die einzufügenden Daten zu generieren. Dies ergibt insgesamt 20 * 5.000 * 100 = 10.000.000 Auftragseinfügungen.
Öffnen Sie die RML Cmd-Eingabeaufforderung, und führen Sie den folgenden Befehl aus:
Klicken Sie auf die Schaltfläche "Kopieren", um den Befehl zu kopieren, und fügen Sie ihn in die Befehlszeile für RML-Hilfsprogramme ein.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID 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; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"
Auf einem Testserver mit einer Gesamtanzahl von 8 physischen (16 logischen) Kernen dauerte dies 2 Minuten und 5 Sekunden. Auf einem zweiten Testserver mit 24 physischen (48 logischen) Kernen dauerte dies 1 Minute und 0 Sekunden.
Beobachten Sie die CPU-Auslastung, während die Workload ausgeführt wird, z. B. mithilfe des Task-Managers. Sie werden sehen, dass die CPU-Auslastung bei fast 100%liegt. 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. Beachten Sie, dass diese Arbeitslast eine Weile dauern kann, was größtenteils auf Latch-Contention im System zurückzuführen ist. Speicheroptimierte Tabellen sind riegelfrei und leiden daher nicht an diesem Problem.
Öffnen Sie die RML Cmd-Eingabeaufforderung, und führen Sie den folgenden Befehl aus:
Klicken Sie auf die Schaltfläche "Kopieren", um den Befehl zu kopieren, und fügen Sie ihn in die Befehlszeile für RML-Hilfsprogramme ein.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID 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; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"
Auf einem Testserver mit einer Gesamtanzahl von 8 physischen (16 logischen) Kernen dauerte dies 41 Minuten und 25 Sekunden. Auf einem zweiten Testserver mit 24 physischen (48 logischen) Kernen dauerte dies 52 Minuten und 16 Sekunden.
Der Hauptfaktor des Leistungsunterschieds zwischen speicheroptimierten Tabellen und datenträgerbasierten Tabellen in diesem Test ist die Tatsache, dass SQL Server bei Verwendung datenträgerbasierter Tabellen die CPU nicht vollständig nutzen kann. Der Grund dafür ist eine Überlastung der Sperren: Gleichzeitige Transaktionen versuchen, auf dieselbe Datenseite zu schreiben; Sperren werden verwendet, um sicherzustellen, dass jeweils nur eine Transaktion zeitgleich auf eine Seite schreiben kann. Das In-Memory OLTP-Modul ist riegelfrei, und Datenzeilen werden nicht auf Seiten organisiert. Daher blockieren gleichzeitige Transaktionen die Einfügungen der anderen nicht, sodass SQL Server die CPU vollständig nutzen kann.
Sie können die CPU-Auslastung beobachten, während die Workload ausgeführt wird, z. B. mithilfe des Task-Managers. Sie werden mit datenträgerbasierten Tabellen sehen, dass die CPU-Auslastung weit von 100%ist. Bei einer Testkonfiguration mit 16 logischen Prozessoren würde die Auslastung etwa 24%zeigen.
Optional können Sie die Anzahl der Latch-Wartezeiten pro Sekunde mithilfe des Leistungsmonitors mit dem Leistungsindikator "\SQL Server:Latches\Latch Waits/sec" anzeigen.
Zurücksetzen der Demo
Um die Demo zurückzusetzen, öffnen Sie die RML Cmd-Eingabeaufforderung, und führen Sie den folgenden Befehl aus:
ostress.exe -S. -E -dAdventureWorks2014 -Q"EXEC Demo.usp_DemoReset"
Je nach Hardware kann dies einige Minuten dauern.
Wir empfehlen eine Zurücksetzung nach jeder Demoausführung. Da diese Workload nur eingefügt wird, verbraucht jede Ausführung mehr Arbeitsspeicher, und daher ist eine Zurücksetzung erforderlich, um zu verhindern, dass arbeitsspeicherfrei ist. Die Menge des Arbeitsspeichers, die nach einer Ausführung verbraucht wird, wird im Abschnitt Speichernutzung nach Ausführung der Workload erläutert.
Problembehandlung bei langsam ausgeführten Tests
Die Testergebnisse variieren in der Regel mit der Hardware und der Parallelitätsstufe, die in der Testausführung 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 beträgt die Leistungssteigerung mit In-Memory OLTP wahrscheinlich weniger als 2X. Latch Contention ist nur dann ein großes Problem, wenn eine hohe Parallelität vorliegt.
Geringe Anzahl von Kernen, die sql Server zur Verfügung stehen: Dies bedeutet, dass es eine geringe Parallelität im System gibt, 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 Ausführung der Workload auf datenträgerbasierten Tabellen hoch ist, bedeutet dies, dass es nicht viele Konflikte gibt, was auf einen Mangel an Nebenläufigkeit hindeutet.
Geschwindigkeit des Loglaufwerks: Wenn das Loglaufwerk nicht mit dem Transaktionsdurchsatz im System Schritt halten kann, entsteht ein Engpass bei der Protokoll-E/A. Obwohl die Protokollierung mit In-Memory OLTP effizienter ist, ist ein möglicher Leistungsgewinn begrenzt, wenn die Protokoll-E/A ein Engpass ist.
- Symptom: Wenn die CPU-Auslastung nicht nahe 100% oder sehr spiky ist, wenn die Workload auf speicheroptimierten Tabellen ausgeführt wird, ist es möglich, dass es einen Protokoll-E/A-Engpass gibt. Dies kann bestätigt werden, indem sie den Ressourcenmonitor öffnen und die Warteschlangenlänge für das Protokolllaufwerk betrachten.
Arbeitsspeicher- und Speicherplatzauslastung im Beispiel
In der folgenden Abbildung wird beschrieben, was sie in Bezug auf Arbeitsspeicher und Speicherplatznutzung für die Beispieldatenbank erwarten. Außerdem zeigen wir die Ergebnisse, die wir auf einem Testserver mit 16 logischen Kernen gesehen haben.
Speicherauslastung für die speicheroptimierten Tabellen
Gesamtnutzung der Datenbank
Die folgende Abfrage kann verwendet werden, um die Gesamtspeicherauslastung für In-Memory OLTP im System abzurufen.
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'
Momentaufnahme, nachdem die Datenbank soeben erstellt wurde:
| Typ | Name | pages_MB |
| MEMORYCLERK_XTP | Standard | 94 |
| MEMORYCLERK_XTP | DB_ID_5 | 877 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Die Standardspeicherbearbeiter enthalten systemweite Speicherstrukturen und sind relativ klein. Der Speichermanager für die Benutzerdatenbank, in diesem Fall die Datenbank mit der ID 5, beträgt etwa 900 MB.
Arbeitsspeicherauslastung pro Tabelle
Die folgende Abfrage kann verwendet werden, um einen Drilldown zur Speicherauslastung der einzelnen Tabellen und deren Indizes auszuführen:
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 dms JOIN sys.tables t
ON dms.object_id=t.object_id
WHERE t.type='U'
Im Folgenden werden die Ergebnisse dieser Abfrage für eine Neuinstallation des Beispiels angezeigt:
| Tabellenname | Speicher_allokiert_für_Tabelle_kb | Speicher_für_Indizes_zugewiesen_kb |
| SpecialangebotProdukt_inmem | 64 | 3840 |
| DemoVerkaufsauftragskopfSeed | 1984 | 5504 |
| 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 ist etwa 7 MB, und SalesOrderDetail_inmem ist etwa 15 MB groß.
Auffällig ist hier die Größe des für Indizes zugeordneten Speichers im Vergleich zur Größe der Tabellendaten. Das liegt daran, dass die Hashindizes im Beispiel für eine größere Datengröße vorab angepasst werden. Beachten Sie, dass Hashindizes eine feste Größe aufweisen und damit ihre Größe nicht mit der Größe von Daten in der Tabelle vergrößert wird.
Arbeitsspeicherauslastung nach Ausführung der Arbeitslast
Nach dem Einfügen von 10 Millionen Verkaufsaufträgen sieht die gesamte Speicherauslastung ähnlich wie folgt aus:
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'
| Typ | Name | pages_MB |
| MEMORYCLERK_XTP | Standard | 146 |
| MEMORYCLERK_XTP | DB_ID_5 | 7374 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Wie Sie sehen können, verwendet SQL Server etwas unter 8 GB für die speicheroptimierten Tabellen und Indizes in der Beispieldatenbank.
Sehen Sie sich die detaillierte Speicherauslastung pro Tabelle nach einem Beispiel an:
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 dms JOIN sys.tables t
ON dms.object_id=t.object_id
WHERE t.type='U'
| Tabellenname | memory_allocated_for_table_kb | Speicher_für_Indizes_kb_zugewiesen |
| 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 | 5504 |
Wir können insgesamt ca. 6,5 GB Daten sehen. Beachten Sie, dass die Größe der Indizes in der Tabelle SalesOrderHeader_inmem und SalesOrderDetail_inmem mit der Größe der Indizes übereinstimmt, bevor Sie die Verkaufsaufträge einfügen. Die Indexgröße wurde nicht geändert, da beide Tabellen Hashindizes verwenden, und Hashindizes sind statisch.
Nach dem Zurücksetzen der Demo
Die gespeicherte Prozedur Demo.usp_DemoReset kann verwendet werden, um die Demo zurückzusetzen. Es löscht die Daten in den Tabellen SalesOrderHeader_inmem und SalesOrderDetail_inmem und besetzt die Daten neu 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 nach Bedarf Speicher aus gelöschten Zeilen in speicheroptimierten Tabellen im Hintergrund zurück. Unmittelbar nach dem Zurücksetzen der Demo, ohne dass ein Transaktionsworkload auf dem System vorhanden ist, werden Sie sehen, dass der Speicher aus gelöschten Zeilen noch nicht zurückgefordert wird.
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'
| Typ | Name | pages_MB |
| MEMORYCLERK_XTP | Standard | 2261 |
| MEMORYCLERK_XTP | DB_ID_5 | 7396 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Dies wird erwartet: Arbeitsspeicher wird erneut beansprucht, wenn die Transaktionsworkload 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. Beispiel:
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'
| Typ | Name | pages_MB |
| MEMORYCLERK_XTP | Standard | 1863 |
| MEMORYCLERK_XTP | DB_ID_5 | 7390 |
| MEMORYCLERK_XTP | Standard | 0 |
| MEMORYCLERK_XTP | Standard | 0 |
Datenträgerauslastung für speicheroptimierte Tabellen
Die Gesamtgröße auf dem Datenträger für die Prüfpunktdateien einer Datenbank zu einem bestimmten Zeitpunkt kann mithilfe der Abfrage gefunden werden:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX'
Anfänglicher Status
Wenn die Beispieldateigruppe und die speicheroptimierten Beispieltabellen anfänglich erstellt werden, werden eine Reihe von Prüfpunktdateien vorab 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 anfänglich sehr klein ist, sind die bereits erstellten Dateien nach der ersten Erstellung meist leer.
Im Folgenden wird die anfängliche Größe des Datenträgers für das Beispiel auf einem Computer mit 16 logischen Prozessoren gezeigt:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX'
| Größe auf dem Datenträger in MB |
| 2312 |
Wie Sie sehen können, gibt es eine große Diskrepanz zwischen der Größe der Prüfpunktdateien( 2,3 GB) und der tatsächlichen Datengröße, die näher an 30 MB liegt.
Wenn Sie sich genauer ansehen, wo sich die Speicherplatznutzung ergibt, können Sie die folgende Abfrage verwenden. Die auf dem Datenträger angegebene Größe, die von dieser Abfrage zurückgegeben wird, ist eine Näherung für Dateien mit dem Status 5 (ERFORDERLICH FÜR SICHERUNG/HA), 6 (IM ÜBERGANG ZUM TOMBSTONE) oder 7 (TOMBSTONE).
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 für einen Server mit 16 logischen Prozessoren ungefähr so aus:
| Statusbeschreibung | Dateityp_Beschreibung | anzahl | Größe auf dem Datenträger MB |
| VORKONFIGURIERT | DATEN | 16 | 2048 |
| VORKONFIGURIERT | DELTA | 16 | 128 |
| IM BAU | DATEN | 1 | 128 |
| IM BAU | DELTA | 1 | 8 |
Wie Sie sehen können, wird der großteil des Speicherplatzes von vordefinierten Daten und Delta-Dateien verwendet. SQL Server hat vorab ein Paar von Dateien (Daten, Delta) pro logischem Prozessor erstellt. Darüber hinaus sind Datendateien bei 128 MB vorgroß, und Delta-Dateien bei 8 MB, um das Einfügen von Daten in diese Dateien effizienter zu gestalten.
Die tatsächlichen Daten in den speicheroptimierten Tabellen befinden sich in der einzelnen Datendatei.
Nach dem Ausführen der Workload
Nach einem einzigen Testlauf, der 10 Millionen Verkaufsbestellungen einschließt, sieht die gesamte Datenträgergröße ungefähr so aus (für einen 16-Kern-Testserver):
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX'
| Größe auf dem Datenträger in MB |
| 8828 |
Die Größe auf dem Datenträger liegt nahe bei 9 GB, was der Größe der Daten im Arbeitsspeicher nahe kommt.
Genauere Betrachtung der Größen der Prüfpunktdateien in den verschiedenen Zuständen:
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
| state_desc | file_type_desc | anzahl | Größe auf dem Datenträger MB |
| VORKONFIGURIERT | DATEN | 16 | 2048 |
| VORKONFIGURIERT | DELTA | 16 | 128 |
| IM BAU | DATEN | 1 | 128 |
| IM BAU | DELTA | 1 | 8 |
Wir haben noch 16 Paare von vordefinierten Dateien, die bereitgestellt werden können, wenn die Prüfpunkte abgeschlossen sind.
Es gibt ein Paar im Bau, das verwendet wird, bis der aktuelle Prüfpunkt geschlossen ist. Zusammen mit den aktiven Prüfpunktdateien gibt dies ca. 6,5 GB Datenträgerauslastung für 6,5 GB Daten im Arbeitsspeicher. 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 Demo
Nach dem Zurücksetzen der Demo wird Speicherplatz nicht sofort zurückgefordert, wenn keine Transaktionsworkloads auf dem System vorhanden sind und keine Datenbankprüfpunkte vorhanden sind. Damit Prüfpunktdateien in ihre verschiedenen Phasen verschoben und schließlich verworfen werden, müssen eine Reihe von Prüfpunkten und Protokollabkürzungsereignissen geschehen, um die Zusammenführung von Prüfpunktdateien zu initiieren und die Garbage Collection zu initiieren. Dies geschieht automatisch, wenn Sie im System über eine Transaktionsauslastung verfügen [und regelmäßige Protokollsicherungen ausfü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 f JOIN sys.database_files df
ON f.data_space_id=df.data_space_id
WHERE f.type=N'FX'
| Größe auf dem Datenträger in MB |
| 11839 |
Bei fast 12 GB ist dies deutlich mehr als die 9 GB, die wir vor dem Demo-Reset hatten. Dies liegt daran, dass einige Checkpoint-Dateizusammenführungen gestartet wurden, aber einige der Zusammenführungsziele noch nicht installiert sind und einige der Zusammenführungsquelldateien noch nicht bereinigt wurden, wie aus dem Folgenden hervorgeht:
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
| state_desc | Dateityp_Beschreibung | anzahl | Größe auf dem Datenträger MB |
| VORKONFIGURIERT | DATEN | 16 | 2048 |
| VORKONFIGURIERT | DELTA | 16 | 128 |
| Aktiv | DATEN | 38 | 5152 |
| Aktiv | DELTA | 38 | 1331 |
| ZUSAMMENFÜHREN DES ZIELS | DATEN | 7 | 896 |
| ZUSAMMENFÜHREN DES ZIELS | DELTA | 7 | 56 |
| ZUSAMMENGEFÜHRTE QUELLE | DATEN | 13 | 1772 |
| ZUSAMMENGEFÜHRTE QUELLE | DELTA | 13 | 4:55 |
Zusammengeführte Ziele werden installiert und zusammengeführte Quellen werden bereinigt, wenn transaktionale Aktivitäten im System auftreten.
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 obige Abfrage mehrmals ausführen, während die Workload ausgeführt wird, können Sie sehen, dass die Prüfpunktdateien ihren Weg durch die verschiedenen Phasen machen.
Wenn Sie nach der zweiten Ausführung der Workload 10 Millionen Verkaufsaufträge einfügen, sehen Sie eine sehr ähnliche, aber nicht unbedingt identische Datenträgerauslastung wie nach der ersten Ausführung, da das System dynamisch ist. 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
| state_desc | Dateityp-Beschreibung | anzahl | Größe auf dem Datenträger MB |
| VORKONFIGURIERT | DATEN | 16 | 2048 |
| VORKONFIGURIERT | 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 Zustand "Unterbau", was bedeutet, dass mehrere Dateipaare in den Zustand "Unterbau" verschoben wurden, wahrscheinlich aufgrund der hohen Parallelität in der Workload. Mehrere gleichzeitige Threads erfordern gleichzeitig ein neues Dateipaar und verschoben somit ein Paar von "vorkonfiguriert" in "unter Bau".