Der Partitionsschlüssel ist die Entscheidung am Container, die sich später am schwersten ändern lässt. K3 hat die Grenzen genannt (20 GB und 10.000 RU/s je logische Partition) und ein Bürodokument in Elemente je Termin zerlegt, partitioniert nach Büro und Monat. Diese Lektion fragt tiefer: Wonach wählt man, was folgt aus der Wahl, und wann kippt sie? Am Ende steht eine Rechnung für drei Büros.
Vorwissen. Die Grenzen, die Reparatur nach Büro und Monat und der Überblick über hierarchische Schlüssel stehen in „K3, Lektion 3.6.4" und werden nicht wiederholt. Die Elementformen kommen aus Lektion 1.3, die Höchstwerte je Container aus Lektion 1.1 (F-05: appointments 2.000 RU/s).
Was ausgeführt wurde. Das Rechenprogramm wurde mit .NET 10 übersetzt und lief ohne Konto. Nicht gegen Azure ausgeführt ist alles, was Abfragen oder Container des Dienstes betrifft. Alle Zahlen zu Büros, Kosten je Schreibvorgang (6 RU), dem Index (Faktor 2) und der Größe der Aktivitäten (300 Byte) sind Annahmen der Aufgabe, keine Messwerte.
Was ein guter Schlüssel hat
Learn nennt für alle Container fünf Eigenschaften: Der Wert ändert sich nicht (er ist unveränderlich; ein Element „umzuziehen" heißt neu anlegen und das alte löschen, nicht in einer Transaktion über zwei Partitionen), er ist ein Text (oder eine Zahl, die in double passt), er hat viele verschiedene Werte, er verteilt RU und Speicher gleichmäßig, und er ist höchstens 2.048 Byte lang (101 Byte, solange große Schlüssel nicht eingeschaltet sind). Für Container mit vielen Abfragen kommt dazu: Der Schlüssel erscheint in den Filtern der häufigen Abfragen. Eine Abfrage mit dem Schlüssel geht an eine Partition; ohne ihn an alle, und jede physische Partition kostet laut Learn 2 bis 3 RU zusätzlich.
Wichtig für die Größe der Sorge: Kleine Container haben nur eine oder zwei physische Partitionen, und die Kosten von Abfragen über Partitionen fallen dort kaum ins Gewicht. Learn nennt als Grenze, ab der man sie vermeiden sollte, mehr als 30.000 bereitgestellte RU/s oder mehr als 100 GB.
Heiße Partitionen und die Verteilung der Schreibvorgänge
Der bereitgestellte Durchsatz wird gleichmäßig auf die physischen Partitionen verteilt (jede höchstens 10.000 RU/s und 50 GB; bei Überschreiten teilt der Dienst sie). Eine logische Partition liegt immer ganz in einer. Bekommt ein Schlüsselwert unverhältnismäßig viele Anfragen, ist er „heiß": Er wird mit 429 gebremst, obwohl der Container als Ganzes Luft hat. Der Fehler wird gern übersehen, weil Schlüssel mit scheinbar vielen Werten (userId) in der Praxis von wenigen Werten getroffen werden können.
Hierarchische Schlüssel
Bis zu drei Ebenen (SDK .NET ab 3.33.0). Abfragen mit der ersten Ebene oder einem Präfix gehen nur an die Partitionen mit diesen Daten. Für appointments (Schlüssel /officeId, /month) gilt, angewendet auf Learns Tabelle:
| Filter der Abfrage | Weg |
|---|---|
officeId und month |
eine logische Partition |
nur officeId |
gezielt an alle Partitionen dieses Büros (Präfix) |
nur month |
alle physischen Partitionen (Fan-out) |
Learn nennt Bedingungen: Jede Ebene braucht hohe Kardinalität; bei schreibstarken Lasten rät Learn zu mindestens Tausenden Werten in der ersten Ebene. Hat sie wenig Werte, bleiben die Schreibvorgänge bis zu den ersten Teilungen (bei 50 GB) auf einer physischen Partition. Hierarchische Schlüssel gibt es nur beim Anlegen des Containers, sie lassen sich nicht nachträglich einführen; der Weg dahin ist ein neuer Container und Kopieren (Container-Kopieraufträge oder Änderungsfeed). Berechtigungen der Funktion „Users and permissions" lassen sich nur auf den vollständigen Schlüssel vergeben, nicht auf ein Präfix.
Synthetisch dagegen heißt: eine eigene Eigenschaft, die aus mehreren zusammengesetzt wird.
Synthetische Schlüssel
Learn nennt drei Verfahren: Werte zusammenfügen (deviceId und Datum zu abc-123-2018), einen zufälligen Zusatz (Datum plus Zahl von 1 bis 400: gut verteilte Schreibvorgänge, aber ein Element lässt sich nicht gezielt lesen) und einen berechneten Zusatz (Hash einer anderen Eigenschaft: gleichmäßig verteilt und trotzdem wieder berechenbar). Die Stolperfalle liegt im Hash: string.GetHashCode() liefert je Prozess andere Werte, ein Zusatz damit wäre beim nächsten Start ein anderer. Ein stabiler Hash (hier FNV-1a über die UTF-8-Bytes):
public static int Bucket(string id, int buckets)
{
var hash = 2166136261u;
foreach (var b in Encoding.UTF8.GetBytes(id))
{
hash = (hash ^ b) * 16777619u;
}
return (int)(hash % (uint)buckets);
}
public static string For(string officeId, string id, int buckets) => $"{officeId}.{Bucket(id, buckets)}";
7360 ids in 10 buckets: smallest 719, largest 747
key of t-00001: buero-nord.1; again: buero-nord.1
Der Preis: Die Termine eines Büros liegen nun in zehn Partitionen, „alle Termine des Büros" ist eine Abfrage über zehn. Synthetisch lohnt, wenn die erste Ebene keine hohe Kardinalität hat und eine einzelne Partition die Schreiblast nicht trägt.
Fehlmuster
Learn nennt drei:
/idfür alles. Perfekte Verteilung und billige Punktlesevorgänge, aber jede Abfrage nach anderen Eigenschaften geht an alle Partitionen. Richtig bei fast nur Punktlesen.- Wenige Werte (
status,type,country). Ungleichmäßig und heiß. Nur bei kleinen Mengen vertretbar. - Viele Werte, die keine Abfrage nennt (eine zufällige GUID). Schreiben verteilt sich, jedes Lesen geht überall hin.
Fehlt ein einzelner Schlüssel für mehrere unabhängige Abfragen, nennt Learn globale sekundäre Indizes (zusätzliche Container mit anderem Schlüssel, aus dem Quellcontainer gespeist und laut Learn nur letztlich konsistent; der Kurs vertieft sie nicht) als Weg, statt den Schlüssel zu verbiegen.
Kosten abschätzen: drei Büros
Die Rechnung hat vier Teile: Größe eines Elements (gemessen), Mengen aus F-02, Annahmen (6 RU je Schreibvorgang, Index-Faktor 2, drei Schreibvorgänge je Termin, 2.000 RU/s Höchstwert) und die Frage, wo es kippt.
one appointment item: 221 bytes
office per year storage after 5 years largest month re-sync
buero-nord 7360 15.51 MB 0.259 MB 44160 RU = 22.1 s
buero-sued 1800 3.79 MB 0.063 MB 10800 RU = 5.4 s
buero-mitte 400 0.84 MB 0.014 MB 2400 RU = 1.2 s
Gerechnet ist: Speicher = Termine im Jahr × 5 Jahre × 221 Byte × 2 (Index-Annahme), zum Beispiel für buero-nord 7.360 × 5 × 221 × 2 = 16.265.600 Byte, rund 15,5 MB. Ein Abgleich eines Jahres kostet 7.360 × 6 = 44.160 RU, bei 2.000 RU/s sind es 22,1 Sekunden. Weiter:
share of the current writes that hit the hottest logical partition
/officeId: 77 % (buero-nord)
/officeId + /month: 77 % (current month of buero-nord)
/id: 0.0105 % (one item per partition)
one logical partition stops at 10000 RU/s = 1667 writes/s of 6 RU;
buero-nord writes on a working day: 96 = 576 RU in a day
20 GB would hold 48,585,603 such items
Die Anteile sind gerechnet, nicht gemessen: 7.360 von 9.560 Terminen im Jahr gehören zu buero-nord, und alle laufenden Schreibvorgänge eines Büros treffen den aktuellen Monat, der Monat verteilt also über die Zeit, nicht über die Büros. Für die Aktivitäten (events, Schlüssel /officeId, /day, 90 Tage Ablaufzeit, F-03) ergab die Rechnung:
events in the 90-day window per working day (largest partition)
buero-nord 5444 events 96.0 events = 56.3 KB
buero-sued 1332 events 23.5 events = 13.8 KB
buero-mitte 296 events 5.2 events = 3.1 KB
Was daraus folgt
- Mit den Zahlen aus F-02 kippt nichts. Selbst
/officeIdallein bliebe fünf Jahre bei 16 MB, die Grenze von 20 GB wäre erst bei fast 49 Millionen Elementen erreicht, und 10.000 RU/s je Partition bei 1.667 Schreibvorgängen je Sekunde. Der Schlüssel/officeId,/monthaus F-03 bleibt, weil Z1 aus Lektion 1.3 („Kalender eines Monats") dann eine Partition liest und weil er für ein größeres Büro vorsorgt. Er ist hier Vorsorge, kein Zwang; das wissen zu sagen gehört zur Begründung. - Das Risiko ist die erste Ebene. Drei Büros sind drei Werte, weit unter den „Tausenden", die Learn für schreibstarke Lasten nennt. Bei diesen Mengen wirkt sich das nicht aus. Wächst die Zahl der Büros in den Tausender-Bereich, verbessert sich die Lage von selbst; wächst ein einzelnes Büro um das Hundertfache, hilft die zweite Ebene.
- Ein Abgleich lässt sich planen. 22 Sekunden bei vollem Höchstwert zeigen: Eine nächtliche Auffrischung aller drei Büros (zusammen knapp 29 Sekunden) bleibt im Rahmen von F-05; sie als Stoß zu starten, verbraucht den Höchstwert und lässt gleichzeitige Leser 429 sehen.
Übung 1
Teil A, ohne Konto. Rechnen Sie mit dem Programm für ein viertes Büro buero-ost mit 40.000 Terminen im Jahr, sonst gleichen Annahmen.
- Speicher nach fünf Jahren, größter Monat, Dauer eines Jahresabgleichs bei 2.000 RU/s.
- Welcher Anteil der laufenden Schreibvorgänge trifft nun das größte Büro, und ändert sich Ihre Begründung für den Schlüssel?
- Welche dieser Abfragen sind für
/officeId,/montheine Partition, gezielt, oder Fan-out? (a) Büro und Monat; (b) nur das Büro; (c) nur der Monat; (d) Büro und Titel.
Teil B, mit einem Testkonto (nicht gegen Azure ausgeführt): Legen Sie appointments mit dem hierarchischen Schlüssel an (ContainerProperties mit zwei Pfaden), schreiben Sie 500 Termine und prüfen Sie die RU der Abfrage (a) und (c) mit RequestCharge. Aufräumen: Datenbank des Tests löschen.
So prüfen Sie es: Teil A mit den Zahlen der Lösung, Exit-Code 0.
Lösung 1
- 40.000 × 5 × 221 × 2 = 88.400.000 Byte, rund 84 MB; ein größter Monat von 40.000 ÷ 12 × 221 × 2 ≈ 1,4 MB; ein Jahresabgleich 40.000 × 6 = 240.000 RU, bei 2.000 RU/s 120 Sekunden.
- Gesamt nun 49.560 Termine,
buero-osthat 40.000 davon, also etwa 81 %. Die Begründung ändert sich nicht: Auch 84 MB sind weit von 20 GB, und 81 % von wenigen Schreibvorgängen sind wenige Schreibvorgänge. Neu ist nur die Dauer des Abgleichs, zwei Minuten, die man nachts einplant. - (a) eine Partition, (b) gezielt (Präfix), (c) Fan-out, (d) gezielt, solange
officeIdim Filter steht; der Filter auftitleändert den Weg nicht, nur die Kosten der Abfrage (Lektion 2.4).
Teil B bleibt ungeprüft. Offen bleibt der Index-Faktor 2: Er ist eine Annahme, bis ein Konto den Speicher meldet.
Was Sie jetzt können
- Sie bewerten einen Schlüssel nach Unveränderlichkeit, Kardinalität, Verteilung und Abfragen.
- Sie setzen hierarchische und synthetische Schlüssel ein und wissen, wann welcher passt.
- Sie erkennen die drei Fehlmuster und kennen globale sekundäre Indizes als Ausweg.
- Sie rechnen Speicher, Abgleichdauer und Kippgrenzen für drei Büros mit nachvollziehbaren Annahmen.
- Sie wissen, dass ein Schlüssel nur durch einen neuen Container geändert wird.
Weiterlesen
- Partitioning and horizontal scaling: Kriterien, Fehlmuster, Schlüsselarten im Vergleich.
- Hierarchical partition keys: Bedingungen, Routing der Abfragen, Umstieg.
- Create a synthetic partition key: Zusammenfügen, zufälliger und berechneter Zusatz.
- Service quotas and default limits: 20 GB, 10.000 RU/s, Länge der Schlüsselwerte.
- Diagnose request rate too large (429): heiße Partitionen erkennen.