Hierarchische Partitionsschlüssel in Azure Cosmos DB

GILT FÜR: NoSQL

Azure Cosmos DB verteilt Ihre Daten basierend auf Ihren Partitionsschlüsseln auf logische und physische Partitionen, um die horizontale Skalierung zu ermöglichen. Mit hierarchischen Partitionsschlüsseln (auch als Unterpartitionierung bekannt) können Sie jetzt eine Hierarchie mit bis zu drei Ebenen für Ihre Partitionsschlüssel konfigurieren, um die Datenverteilung weiter zu optimieren und ein höheres Maß an Skalierung zu ermöglichen.

Wenn Sie gegenwärtig synthetische Schlüssel verwenden oder Szenarien haben, in denen Partitionsschlüssel 20 GB an Daten überschreiten können, kann die Unterpartitionierung hilfreich sein. Wenn Sie diese Funktion verwenden, können Präfixe für logische Partitionsschlüssel 20 GB und 10.000 Anforderungseinheiten pro Sekunde (RU/Sek.) überschreiten. Abfragen nach Präfix werden effizient an die Teilmenge der Partitionen weitergeleitet, die die Daten enthalten.

Auswählen der hierarchischen Partitionsschlüssel

Wenn Sie über mehrmandantenfähige Anwendungen verfügen, wird empfohlen, hierarchische Partitionsschlüssel zu verwenden. Mit hierarchischen Partitionen können Sie über den Grenzwert für logische Partitionsschlüssel von 20 GB hinaus skalieren. Wenn Ihr aktueller Partitionsschlüssel oder ein einzelner Partitionsschlüssel häufig 20 GB erreicht, sind hierarchische Partitionen eine gute Wahl für Ihre Workload.

Bei der Auswahl Ihrer hierarchischen Partitionsschlüssel ist es wichtig, je nach Ihrer Workload die folgenden allgemeinen Partitionierungskonzepte zu berücksichtigen:

  • Für alle Container sollte jede Ebene des vollständigen Pfads (beginnend mit der ersten Ebene) Ihres hierarchischen Partitionsschlüssels:

    • eine hohe Kardinalität aufweisen. Der erste, zweite und dritte Schlüssel (falls zutreffend) der hierarchischen Partition sollten alle einen großen Bereich möglicher Werte aufweisen.
    • die zu verbrauchenden Anforderungseinheiten (Request Units, RUs) und die Datenspeicherung gleichmäßig auf alle logischen Partitionen verteilen. Durch diese Verteilung wird ein gleichmäßiger RU-Verbrauch und eine gleichmäßige Speicherverteilung auf Ihren physischen Partitionen sichergestellt.
  • Bei großen Workloads mit hohem Lesezugriff empfehlen wir, hierarchische Partitionsschlüssel auszuwählen, die in Ihren Abfragen häufig vorkommen. Beispielsweise kann eine Workload, die häufig Abfragen ausführt, um bestimmte Benutzersitzungen in einer mehrmandantenfähigen Anwendung herauszufiltern, von den hierarchischen Partitionsschlüsseln TenantId, UserId und SessionId (in dieser Reihenfolge) profitieren. Abfragen können effizient nur an die relevanten physischen Partitionen weitergeleitet werden, indem der Partitionsschlüssel in das Filterprädikat eingeschlossen wird. Weitere Informationen zu Partitionsschlüsseln für Workloads mit hohem Lesezugriff finden Sie in der Übersicht über die Partitionierung.

Beispiel eines Anwendungsfalls

Angenommen, Sie haben ein Szenario mit mehreren Mandanten, in dem Sie Ereignisinformationen für Benutzende in jedem Mandanten speichern. Diese Ereignisinformationen können Ereignisvorkommen wie u. a. Anmelde-, Clickstream- oder Zahlungsereignisse umfassen.

In einem realen Szenario können einige Mandanten groß werden und Tausende von Benutzenden enthalten, während die meisten anderen Mandanten kleiner sind und nur wenige Benutzende umfassen. Die Partitionierung nach /TenantId kann dazu führen, dass der Speichergrenzwert von Azure Cosmos DB von 20 GB für eine einzelne logische Partition überschritten wird. Die Partitionierung nach /UserId führt dazu, dass alle Abfragen auf einem Mandanten partitionsübergreifend durchgeführt werden. Beide Ansätze haben erhebliche Nachteile.

Die Verwendung eines synthetischen Partitionsschlüssels, der TenantId und UserId kombiniert, macht die Anwendung komplexer. Darüber hinaus sind Abfragen mit einem synthetischen Partitionsschlüssel für einen Mandanten weiterhin partitionsübergreifend – außer wenn alle Benutzer im Voraus bekannt sind und angegeben werden.

Bei hierarchischen Partitionsschlüsseln können Sie zuerst nach TenantId und dann nach UserId partitionieren. Wenn Sie erwarten, dass durch die Kombination von TenantId und UserId Partitionen mit mehr als 20 GB erzeugt werden, können Sie sogar weiter herunter auf einer anderen Ebene partitionieren, z. B. nach SessionId (Sitzungs-ID). Die Gesamttiefe darf nicht größer als drei Ebenen sein. Wenn eine physische Partition 50 GB Speicher überschreitet, wird sie von Azure Cosmos DB automatisch so aufgeteilt, dass ungefähr die Hälfte der Daten in der einen physischen Partition gespeichert wird und die andere Hälfte in der anderen Partition. Im Grunde bedeutet die Unterpartitionierung, dass eine einzelne TenantId 20 GB Daten überschreiten kann und sich die Daten einer TenantId über mehrere physische Partitionen erstrecken können.

Abfragen, in denen entweder die TenantId oder sowohl die TenantId als auch die UserId angegeben sind, werden effizient nur an die Teilmenge von physischen Partitionen weitergeleitet, in denen die relevanten Daten enthalten sind. Durch Angabe des vollständigen Pfads oder Präfixpfads des unterpartitionierten Partitionsschlüssels wird eine vollständige Auffächerungsabfrage effektiv vermieden. Wenn der Container beispielsweise 1,000 physische Partitionen enthält, der Wert einer bestimmten TenantId aber nur in fünf dieser Partitionen vorkommt, würde die Abfrage nur an die kleinere Anzahl von relevanten physischen Partitionen weitergeleitet.

Verwenden der Element-ID in der Hierarchie

Wenn Ihr Container eine Eigenschaft mit einer Vielzahl von möglichen Werten hat, ist dies wahrscheinlich eine gute Wahl für den Partitionsschlüssel für die letzte Ebene Ihrer Hierarchie. Ein mögliches Beispiel für einen solchen Eigenschaftstyp ist die Element-ID. Die Systemeigenschaft Element-ID ist in jedem in Ihrem Container enthaltenen Element vorhanden. Das Hinzufügen der Element-ID als eine weitere Ebene garantiert, dass Sie über den Grenzwert für logische Partitionsschlüssel von 20 GB hinaus skalieren können. Sie können für die erste oder erste und zweite Schlüsselebene über diesen Grenzwert hinaus skalieren.

Beispielsweise können Sie über einen Container für eine mehrmandantenfähige Workload verfügen, die nach TenantId und UserIdpartitioniert ist. Wenn es möglich ist, dass eine einzelne Kombination aus TenantId und UserId 20 GB überschreitet, wird die Partitionierung mit drei Schlüsselebenen empfohlen, bei denen der Schlüssel der dritten Ebene eine hohe Kardinalität hat. Ein Beispiel für dieses Szenario ist, wenn der Schlüssel der dritten Ebene eine GUID mit natürlich hoher Kardinalität ist. Da es unwahrscheinlich ist, dass die Kombination aus TenantId, UserId und einer GUID 20 GB überschreitet, kann die Kombination aus TenantId und UserId effektiv über 20 GB hinaus skaliert werden.

Weitere Informationen zur Verwendung der Element-ID als Partitionsschlüssel finden Sie in der Übersicht über die Partitionierung.

Erste Schritte

Wichtig

Das Arbeiten mit Containern, die hierarchische Partitionsschlüssel verwenden, wird nur in den folgenden SDK-Versionen unterstützt. Sie müssen ein unterstütztes SDK verwenden, um neue Container mit hierarchischen Partitionsschlüsseln zu erstellen und CRUD (Erstellungs-, Lese-, Aktualisierungs- und Lösch-) oder Abfragevorgänge für die Daten auszuführen. Wenn Sie ein SDK oder einen Connector verwenden möchten, das bzw. der derzeit nicht unterstützt wird, können Sie eine Anfrage in unserem Communityforum erstellen.

Hier finden Sie die aktuelle Vorschauversion der unterstützten SDKs:

SDK Unterstützte Versionen Link zum Paket-Manager
.NET SDK v3 >= 3.33.0 https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.33.0/
Java SDK v4 >= 4.42.0 https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/cosmos/azure-cosmos/CHANGELOG.md#4420-2023-03-17/
JavaScript SDK v4 4.0.0 https://www.npmjs.com/package/@azure/cosmos/
Python SDK >= 4.6.0 https://pypi.org/project/azure-cosmos/4.6.0/

Erstellen eines Containers unter Verwendung von hierarchischen Partitionsschlüsseln

Erstellen Sie zuerst einen neuen Container anhand einer vordefinierten Liste der Unterpartitionsschlüsselpfade mit einer Tiefe von bis zu drei Ebenen.

Sie können einen neuen Container erstellen, indem Sie eine der folgenden Optionen verwenden:

  • Azure-Portal
  • SDK
  • Azure Resource Manager-Vorlage
  • Azure Cosmos DB-Emulator

Azure-Portal

Die einfachste Möglichkeit zum Erstellen eines Containers und Angeben von hierarchischen Partitionsschlüsseln ist das Verwenden des Azure-Portals.

  1. Melden Sie sich beim Azure-Portal an.

  2. Navigieren Sie zur Seite des vorhandenen „Azure Cosmos DB for NoSQL“-Kontos.

  3. Wählen Sie im Menü auf der linken Seite die Option Daten-Explorer aus.

    Screenshot der Seite für ein neues „Azure Cosmos DB for NoSQL“-Konto mit hervorgehobener Option „Daten-Explorer“

  4. Wählen Sie im Daten-Explorer die Option Neuer Container aus.

    Screenshot der Option „Neuer Container“ im Daten-Explorer

  5. Geben Sie unter Neuer Container unter Partitionsschlüssel/TenantId ein. Geben Sie bei den verbleibenden Feldern einen beliebigen Wert ein, der Ihrem Szenario entspricht.

    Hinweis

    Wir verwenden hier /TenantId als Beispiel. Sie können für die erste Ebene einen beliebigen Schlüssel angeben, wenn Sie hierarchische Partitionsschlüssel für Ihre eigenen Container implementieren.

  6. Wählen Sie Hierarchischen Partitionsschlüssel hinzufügen zweimal aus.

    Screenshot der Schaltfläche zum Hinzufügen eines neuen hierarchischen Partitionsschlüssels

  7. Geben Sie für die zweite und dritte Ebene der Unterpartitionierung /UserId bzw. /SessionId ein.

    Screenshot einer Liste von drei hierarchischen Partitionsschlüsseln

  8. Wählen Sie OK aus, um den Container zu erstellen.

SDK

Definieren Sie beim Erstellen eines neuen Containers mithilfe des SDK eine Liste der Unterpartitionsschlüsselpfade mit einer Tiefe von bis zu drei Ebenen. Verwenden Sie beim Konfigurieren der Eigenschaften des neuen Containers die Liste der Unterpartitionsschlüssel.

// List of partition keys, in hierarchical order. You can have up to three levels of keys.
List<string> subpartitionKeyPaths = new List<string> { 
    "/TenantId",
    "/UserId",
    "/SessionId"
};

// Create a container properties object
ContainerProperties containerProperties = new ContainerProperties(
    id: "<container-name>",
    partitionKeyPaths: subpartitionKeyPaths
);

// Create a container that's subpartitioned by TenantId > UserId > SessionId
Container container = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: 400);

Azure-Ressourcen-Manager-Vorlagen

Die Azure Resource Manager-Vorlage für einen unterpartitionierten Container ist fast identisch mit einem Standardcontainer. Der einzige wichtige Unterschied ist der Wert des Pfads properties/partitionKey. Weitere Informationen zum Erstellen einer Azure Resource Manager-Vorlage für eine Azure Cosmos DB-Ressource finden Sie in der Referenz zu Azure Resource Manager-Vorlagen für Azure Cosmos DB.

Konfigurieren Sie das Objekt partitionKey mithilfe der Werte in der folgenden Tabelle, um einen unterpartitionierten Container zu erstellen:

Pfad Wert
paths Liste der hierarchischen Partitionsschlüssel (maximal drei Ebenen)
kind MultiHash
version 2

Beispiel für die Partitionsschlüsseldefinition

Angenommen, Sie verfügen über einen hierarchischen Partitionsschlüssel, der aus TenantId>UserId>SessionId besteht. Dann würde das Objekt partitionKey so konfiguriert, dass es alle drei Werte in der paths-Eigenschaft, den kind-Wert MultiHash und den version-Wert 2 enthält.

partitionKey: {
  paths: [
    '/TenantId'
    '/UserId'
    '/SessionId'
  ]
  kind: 'MultiHash'
  version: 2
}

Weitere Informationen zum Objekt partitionKey finden Sie im Artikel ContainerPartitionKey-Spezifikation.

Azure Cosmos DB-Emulator

Sie können die Unterpartitionierungsfunktion mit der aktuellen Version des lokalen Emulators für Azure Cosmos DB testen. Starten Sie den Emulator im Installationsverzeichnis mit dem /EnablePreview-Flag, um die Unterpartitionierung im Emulator zu aktivieren:

.\CosmosDB.Emulator.exe /EnablePreview

Warnung

Der Emulator unterstützt derzeit nicht alle Features hierarchischer Partitionsschlüssel wie das Portal. Der Emulator unterstützt derzeit Folgendes nicht:

  • Verwenden von Data Explorer zum Erstellen von Containern mit hierarchischen Partitionsschlüsseln
  • Verwenden von Data Explorer zum Navigieren zu Elementen und Interagieren mit Elementen mithilfe hierarchischer Partitionsschlüssel

Weitere Informationen finden Sie unter Azure Cosmos DB-Emulator.

Verwenden der SDKs zum Arbeiten mit Containern mit hierarchischen Partitionsschlüsseln

Wenn Sie einen Container mit hierarchischen Partitionsschlüsseln haben, verwenden Sie die zuvor angegebenen Versionen der .NET- oder Java-SDKs zum Durchführen von Vorgängen und Ausführen von Abfragen für diesen Container.

Hinzufügen eines Elements zu einem Container

Zum Hinzufügen eines neuen Elements zu einem Container, für den hierarchische Partitionsschlüssel aktiviert sind, stehen Ihnen zwei Optionen zur Auswahl:

  • Automatische Extraktion
  • Manuelle Angabe des Pfads

Automatische Extraktion

Wenn Sie ein Objekt mit festgelegtem Partitionsschlüsselwert übergeben, kann das SDK automatisch den vollständigen Partitionsschlüsselpfad extrahieren.

// Create a new item
UserSession item = new UserSession()
{
    id = "f7da01b0-090b-41d2-8416-dacae09fbb4a",
    TenantId = "Microsoft",
    UserId = "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b",
    SessionId = "0000-11-0000-1111"
};

// Pass in the object, and the SDK automatically extracts the full partition key path
ItemResponse<UserSession> createResponse = await container.CreateItemAsync(item);

Manuelle Angabe des Pfads

Die PartitionKeyBuilder-Klasse im SDK kann einen Wert für einen zuvor definierten hierarchischen Partitionsschlüsselpfad erstellen. Verwenden Sie diese Klasse, wenn Sie einem Container mit aktivierter Unterpartitionierung ein neues Element hinzufügen.

Tipp

Bei der Verwendung im großen Stil erzielen Sie möglicherweise eine höhere Leistung, wenn Sie den vollständigen Partitionsschlüsselpfad angeben, auch wenn das SDK den Pfad aus dem Objekt extrahieren kann.

// Create a new item object
PaymentEvent item = new PaymentEvent()
{
    id = Guid.NewGuid().ToString(),
    TenantId = "Microsoft",
    UserId = "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b",
    SessionId = "0000-11-0000-1111"
};

// Specify the full partition key path when creating the item
PartitionKey partitionKey = new PartitionKeyBuilder()
            .Add(item.TenantId)
            .Add(item.UserId)
            .Add(item.SessionId)
            .Build();

// Create the item in the container
ItemResponse<PaymentEvent> createResponse = await container.CreateItemAsync(item, partitionKey);

Ausführen einer Schlüssel-Wert-Suche (Punktlesevorgang) für ein Element

Schlüssel-Wert-Suchen (Punktlesevorgänge) werden ähnlich wie bei einem nicht unterpartitionierten Container ausgeführt. Nehmen wir beispielsweise an, Sie verfügen über einen hierarchischen Partitionsschlüssel, der aus TenantId>UserId>SessionId besteht. Der eindeutige Bezeichner für das Element ist eine GUID. Er ist eine als Zeichenfolge dargestellte GUID, die als eindeutiger Dokumenttransaktionsbezeichner dient. Wenn Sie einen Punktlesevorgang für ein einzelnes Element durchführen möchten, übergeben Sie die id-Eigenschaft des Elements und den vollständigen Wert für den Partitionsschlüssel, einschließlich aller drei Komponenten des Pfads.

// Store the unique identifier
string id = "f7da01b0-090b-41d2-8416-dacae09fbb4a";

// Build the full partition key path
PartitionKey partitionKey = new PartitionKeyBuilder()
    .Add("Microsoft") //TenantId
    .Add("8411f20f-be3e-416a-a3e7-dcd5a3c1f28b") //UserId
    .Add("0000-11-0000-1111") //SessionId
    .Build();

// Perform a point read
ItemResponse<UserSession> readResponse = await container.ReadItemAsync<UserSession>(
    id,
    partitionKey
);

Ausführen einer Abfrage

Der SDK-Code, der zum Ausführen einer Abfrage für einen unterpartitionierten Container verwendet wird, ist identisch mit dem Code einer Abfrage für einen nicht unterpartitionierten Container.

Wenn in der Abfrage alle Werte der Partitionsschlüssel im WHERE-Filter oder ein Präfix der Schlüsselhierarchie angegeben sind, leitet das SDK die Abfrage automatisch an die entsprechenden physischen Partitionen weiter. Abfragen, die nur die „Mitte“ der Hierarchie angeben, sind partitionsübergreifende Abfragen.

Betrachten Sie beispielsweise einen hierarchischen Partitionsschlüssel, der aus TenantId>UserId>SessionId besteht. Die Komponenten des Abfragefilters bestimmen, ob es sich bei der Abfrage um eine Abfrage für eine einzelne Partition, eine partitionsübergreifende Zielabfrage oder eine Auffächerungsabfrage handelt.

Abfrage Routing
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' AND c.SessionId = '0000-11-0000-1111' Die Abfrage wird an die einzige logische und physische Partition weitergeleitet, die die Daten für die angegebenen Werte von TenantId, UserId und SessionId enthält.
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' Die Abfrage wird an die als Ziel angegebene Teilmenge von logischen und physischen Partitionen weitergeleitet, die Daten für die angegebenen Werte von TenantId und UserId enthalten. Diese Abfrage ist eine gezielte partitionsübergreifende Abfrage, die Daten für einen bestimmten Benutzer im Mandanten zurückgibt.
SELECT * FROM c WHERE c.TenantId = 'Microsoft' Die Abfrage wird an die als Ziel angegebene Teilmenge von logischen und physischen Partitionen weitergeleitet, die Daten für den angegebenen Wert von TenantId enthalten. Diese Abfrage ist eine gezielte partitionsübergreifende Abfrage, die Daten für alle Benutzer im Mandanten zurückgibt.
SELECT * FROM c WHERE c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' Die Abfrage wird an alle physischen Partitionen weitergeleitet, was zu einer partitionsübergreifenden Auffächerungsabfrage führt.
SELECT * FROM c WHERE c.SessionId = '0000-11-0000-1111' Die Abfrage wird an alle physischen Partitionen weitergeleitet, was zu einer partitionsübergreifenden Auffächerungsabfrage führt.

Abfrage für eine einzelne Partition für einen unterpartitionierten Container

Hier ist ein Beispiel für die Ausführung einer Abfrage, die alle Unterpartitionierungsebenen enthält, wodurch sie effektiv zu einer Abfrage für eine einzelne Partition wird.

// Define a single-partition query that specifies the full partition key path
QueryDefinition query = new QueryDefinition(
    "SELECT * FROM c WHERE c.TenantId = @tenant-id AND c.UserId = @user-id AND c.SessionId = @session-id")
    .WithParameter("@tenant-id", "Microsoft")
    .WithParameter("@user-id", "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b")
    .WithParameter("@session-id", "0000-11-0000-1111");

// Retrieve an iterator for the result set
using FeedIterator<PaymentEvent> results = container.GetItemQueryIterator<PaymentEvent>(query);

while (results.HasMoreResults)
{
    FeedResponse<UserSession> resultsPage = await resultSet.ReadNextAsync();
    foreach(UserSession result in resultsPage)
    {
        // Process result
    }
}

Gezielte Abfrage für mehrere Partitionen für einen unterpartitionierten Container

Hier ist ein Beispiel für eine Abfrage, die eine Teilmenge der Unterpartitionierungsebenen enthält, wodurch diese Abfrage effektiv zu einer Zielabfrage für mehrere Partitionen wird.

// Define a targeted cross-partition query specifying prefix path[s]
QueryDefinition query = new QueryDefinition(
    "SELECT * FROM c WHERE c.TenantId = @tenant-id")
    .WithParameter("@tenant-id", "Microsoft")

// Retrieve an iterator for the result set
using FeedIterator<PaymentEvent> results = container.GetItemQueryIterator<PaymentEvent>(query);

while (results.HasMoreResults)
{
    FeedResponse<UserSession> resultsPage = await resultSet.ReadNextAsync();
    foreach(UserSession result in resultsPage)
    {
        // Process result
    }
}

Einschränkungen und bekannte Probleme

  • Die Verwendung von Containern mit hierarchischen Partitionsschlüsseln wird nur in .NET v3- und Java v4-SDKs sowie in der Vorschauversion des JavaScript-SDKs unterstützt. Sie müssen ein unterstütztes SDK verwenden, um neue Container mit hierarchischen Partitionsschlüsseln zu erstellen und CRUD- bzw. Abfragevorgänge für die Daten auszuführen. Unterstützung für andere SDKs, einschließlich Python, ist derzeit nicht verfügbar.
  • Bei verschiedenen Azure Cosmos DB-Connectoren (z. B. Azure Data Factory) gibt es Einschränkungen.
  • Sie können hierarchische Partitionsschlüssel nur mit einer Tiefe von bis zu drei Ebenen angeben.
  • Hierarchische Partitionsschlüssel können derzeit nur für neue Container aktiviert werden. Sie müssen Partitionsschlüsselpfade zum Zeitpunkt der Containererstellung festlegen, und Sie können sie später nicht mehr ändern. Um hierarchische Partitionen in vorhandenen Containern zu verwenden, erstellen Sie einen neuen Container mit den hierarchischen Partitionsschlüsseln und verschieben die Daten mithilfe von Containerkopieraufträgen.
  • Hierarchische Partitionsschlüssel werden derzeit nur für die „API for NoSQL“-Konten unterstützt. Die APIs für MongoDB und Cassandra werden derzeit nicht unterstützt.
  • Hierarchische Partitionsschlüssel werden derzeit nicht mit der Berechtigungsfunktion unterstützt. Sie können einem Teilpräfix des hierarchischen Partitionsschlüsselpfads keine Berechtigung zuweisen. Berechtigungen können nur dem gesamten logischen Partitionsschlüsselpfad zugewiesen werden. Wenn Sie beispielsweise nach TenantId - >UserId partitioniert haben, können Sie keine Berechtigung zuweisen, die für einen bestimmten Wert von TenantId gilt. Sie können jedoch eine Berechtigung für einen Partitionsschlüssel zuweisen, wenn Sie sowohl den Wert für TenantId als auch den Wert für „UserId“ angeben.

Nächste Schritte