ASP.NET Session State mit SQL Server In-Memory

7. September 2015

Auch wenn Session State nicht den besten Ruf genießt, so hat er doch – auch in ASP.NET MVC-Anwendungen – nach wie vor seine Daseinsberechtigung.

Eine wesentliche Frage beim Session State ist die nach dem Provider, also wo die Daten gespeichert werden.

 

Die schlechteste Antwort ist leider auch die Standardvorgabe: In-Proc.

In-Proc-Mode funktioniert prinzipbedingt nicht fehlerfrei. Hintergründe finden sich hier. Wer noch den In-Proc-Mode einsetzt, sollte schleunigst wechseln – aber bitte nicht kurz vor dem going-live!

Von Anfang an gab es den ASP.NET Session State Server (gut während der Entwicklung, aber für den Betrieb ungeeignet) und den SQL Server als Option. Später kamen andere Provider, insbesondere für verteilte Caches hinzu. DevOpsGuys zeigt einen Performancevergleich, bei dem Redis (auch für Azure die empfohlene Cache Implementierung) die Nase vorne hat.

Trotzdem ist die verbreitetste Variante immer noch der SQL Server, was sich auch absehbar nicht ändern dürfte. Der SQL Server ist in den meisten Unternehmen vorhanden, während Redis oder eine andere Lösung den Betrieb erst einmal vor Herausforderungen stellen würden.

Der SQL Server bietet einerseits Ausfallsicherheit und Skalierbarkeit, andererseits wirkt er sich aber bzgl. Sessionverwaltung in größeren Webfarmen negativ auf die Performance aus. Man kann dem durch Partitionierung entgegenwirken, aber das löst das eigentliche Problem nicht.

SQL Server In-Memory…

Seit dem SQL Server 2014 hat man nun eine weitere Option: Den Session State mit SQL Server In-Memory (ehem. Hekaton) zu verwalten. Microsoft hat diese Lösung in zwei Blogbeiträgen erklärt und bereitgestellt:

Die versprochenen Auswirkungen sind bemerkenswert:

Proof of the incredible magnitude of performance improvements for this provider are available in this case study detailing an ASP.NET application using Session State that handles 250,000 requests per second! (link)

Nun hat aber nicht jede Web-Seite 250.000 Requests pro Sekunde…

Kleiner Praxistest…

…ich habe daher einen kleinen Quick & Dirty Test gemacht, um eine bessere Idee zu bekommen, wie sich das bei weniger anspruchsvoller Last auswirkt. Mein Testszenario:

  • Eine simple Testseite, die einen langen String (16kB) in die Session packt.
    • “String” um keine Effekte durch Objektserialisierung zu erhalten.
    • 16kB ist genug, um die Session State Provider zu fordern, die bei SQL Server mit Chunks von 7000 Bytes arbeiten.
  • Testsystem: Webserver, Datenbanken, State Server, Lasttest-Agent; alles auf einem Laptop, 2 Kerne+Hyperthreading, SSD

Natürlich ist mein Laptop als “Testsystem” für Last-Szenarien bei Web-Anwendungen andere als geeignet, geschweige denn repräsentativ. Während der Testläufe war dann auch vor allem die CPU beschäftigt, insofern ist das alles mit Vorsicht zu genießen. Aber es ging mir ja auch nur um einen ersten Eindruck.

Ausprobiert habe ich folgende Varianten:

  1. Off: Keine Session, um die Baseline bzgl. Durchsatz zu ziehen und so den Overhead des Session-Handlings zu bestimmen.
  2. In-Proc: Auch eine Art Baseline; damit ist die reine Session-Logik vorhanden, aber keine Out-of-Process Kommunikation und keine Serialisierung.
  3. ASP.NET State Server
  4. SQL Server (klassisch)
  5. SQL Server/In-Memory/non-durable
  6. SQL Server/In-Memory/durable

Die verschiedenen Szenarien haben zu folgenden Ergebnissen geführt:

image

Im Detail:

pages/sec avg. page time
Off (baseline, 100%)

128

100%

0,14

100%

In-Proc

127

99%

0,14

100%

ASP.NET State Server

121

95%

0,15

107%

SQL Server (klassisch)

114

89%

0,19

136%

SQL Server In-Memory/non-durable

118

92%

0,16

114%

SQL Server In-Memory/durable

114

89%

0,19

136%

Ergebnisinterpretation…

Quintessenz aus dem Vergleich der In-Memory-Varianten mit der klassischen SQL-Server-Variante:

  • Die non-durable Variante ist deutlich besser als der klassische SQL Server (q.e.d.) und liegt zwischen diesem und dem ASP.NET State Server.
  • Die durable-Variante liegt hingegen gleich auf mit dem SQL Server klassisch. Allerdings ist dabei die für den SQL Server geringe Last und Datenmenge zur berücksichtigen – das dürfte bei höherer Last anders aussehen.

Die höhere Geschwindigkeit des SQL Server/In-Memory erklärt sich durch verschiedene Effekte:

  • Bei non-durable werden die Daten nicht ins Transaktionslog geschrieben (was auch der Grund ist, warum durable gleichauf mit der klassischen Variante liegt).
  • Es kommen compilierte Stored Procedures zum Einsatz.
  • INSERTs und UPDATEs können In-Memory lock-frei ausgeführt werden.

Vom ersten Punkt abgesehen gelten diese Punkte sowohl für die non-durable, als auch für die durable-Variante, auch wenn das in dieser einfachen Messreihe bei durable keine messbaren Auswirkungen hatte. Bei ausreichend Last, insbesondere wenn bei vielen parallelen Schreibzugriffen Locks zu einem Engpass werden, wird sich das aber gegenüber dem klassischen SQL Server positiv auswirken. Mehr dazu finden sich in In-Memory OLTP (In-Memory Optimization).

Fazit…

Anwendungen, die heute Probleme haben, weil sie vom Session State im SQL Server ausgebremst werden, finden mit SQL Server In-Memory einen einfachen Weg der Optimierung. Die Änderungen an bestehenden Anwendungen sind minimal. Das gilt insbesondere, wenn durability keine Anforderung ist. Robustheit, Skalierbarkeit und Verwaltung des SQL Server bleiben dabei in vollem Umfang erhalten.