Python 2 wird von der Community nicht mehr unterstützt. Wir empfehlen die Migration von Python 2-Anwendungen zu Python 3.

Datastore-Indexe

Hinweis: Entwicklern von neuen Anwendungen wird dringend empfohlen, die NDB-Clientbibliothek zu verwenden. Diese bietet im Vergleich zur vorliegenden Clientbibliothek verschiedene Vorteile, z. B. das automatische Caching von Entitäten über die Memcache API. Wenn Sie derzeit die ältere DB-Clientbibliothek verwenden, finden Sie weitere Informationen im Leitfaden zur Migration von DB- zu NDB-Clientbibliotheken.

App Engine gibt für jede Property einer Entität einen einfachen Index vor. Eine App Engine-Anwendung kann weitere benutzerdefinierte Indexe in der Indexkonfigurationsdatei index.yaml definieren. Der Entwicklungsserver fügt dieser Datei automatisch Vorschläge hinzu, wenn er Abfragen ermittelt, die mit den vorhandenen Indexen nicht ausgeführt werden können. Sie können die Indexe vor dem Hochladen der Anwendung von Hand optimieren.

Eine ausführliche Erläuterung von Indexen und Abfragen finden Sie unter Indexauswahl und erweiterte Suche.

Hinweis: Der indexbasierte Abfragemechanismus unterstützt ein breites Spektrum an Abfragen und ist für die meisten Anwendungen geeignet. Allerdings werden einige in anderen Datenbanktechnologien übliche Abfragetypen vom Abfragemodul in Datastore nicht unterstützt, insbesondere Join- und Aggregatabfragen. Weitere Informationen zu Einschränkungen bei Datastore-Abfragen finden Sie auf der Seite Datastore-Abfragen.

Indexdefinition und -struktur

Für jedes Attribut ist in einer Attributliste eines bestimmten Entitätstyps ein Index in entsprechender Reihenfolge (aufsteigend oder absteigend) definiert. Für Ancestor-Abfragen kann der Index optional auch die Ancestors einer Entität umfassen.

Eine Indextabelle beinhaltet eine Spalte für jedes Attribut, das in der Definition des Indexes genannt wird. Jede Tabellenzeile stellt eine Entität in Datastore dar, die ein potenzielles Ergebnis für Abfragen auf Grundlage des Indexes ist. Eine Entität wird nur dann in den Index eingeschlossen, wenn jedes der verwendeten Attribute über einen indexierten Wert verfügt. Wenn sich die Indexdefinition auf ein Attribut ohne Entitätswert bezieht, wird die Entität nicht im Index aufgeführt und daher niemals als Ergebnis einer indexbasierten Abfrage angezeigt.

Hinweis: Datenbanken im Datastore-Modus unterscheiden zwischen einer Entität, die kein Attribut besitzt, und einer Entität, die das Attribut mit einem Nullwert None besitzt. Wenn Sie dem Attribut einer Entität explizit einen Nullwert zuweisen, kann diese Entität in die Ergebnisse einer Abfrage aufgenommen werden, die sich auf dieses Attribut bezieht.

Hinweis: Bei Indexen, die sich aus mehreren Attributen zusammensetzen, darf keines der Attribute auf nicht indexiert gesetzt sein.

Die Zeilen einer Indextabelle werden in der definierten Reihenfolge zuerst nach Ancestor und dann nach Attributwert sortiert. Der perfekte Index für eine Abfrage, der die effizienteste Verarbeitung ermöglicht, ist nach den folgenden Attributen definiert (in dieser Reihenfolge):

  1. In Gleichheitsfiltern verwendete Attribute
  2. In Ungleichheitsfiltern verwendetes Attribut (von denen es höchstens eines geben kann)
  3. In Sortierreihenfolgen verwendete Attribute

Dadurch wird sichergestellt, dass alle Ergebnisse für jede mögliche Ausführung der Abfrage in aufeinanderfolgenden Tabellenzeilen angezeigt werden. Datastore führt eine Abfrage mit einem perfekten Index aus. Gehen Sie dazu so vor:

  1. Identifizierung des zugehörigen Indexes, der Filterattribute, der Filteroperatoren und der Sortierreihenfolge eines Abfragetyps
  2. Scan des Indexes vom Anfang bis zur ersten Entität, die allen Filterbedingungen der Abfrage entspricht
  3. Fortsetzen des Index-Scans und Ausgabe jeder Entität, bis
    • Cloud Datastore auf eine Entität trifft, die den Filterbedingungen nicht entspricht, oder
    • das Index-Ende erreicht ist, oder
    • die maximale Anzahl an Ergebnissen, die von der Abfrage angefordert wurden, erreicht ist.

Betrachten Sie beispielsweise die folgende Abfrage (in GQL):

SELECT * FROM Person WHERE last_name = "Smith"
                       AND height < 72
                  ORDER BY height DESC

Der perfekte Index für diese Abfrage ist eine Schlüsseltabelle für Entitäten vom Typ Person mit Spalten für die Werte der Attribute last_name und height. Der Index wird zuerst in aufsteigender Reihenfolge nach last_name und dann in absteigender Reihenfolge nach height sortiert:

Für zwei Abfragen, die die gleiche Form, jedoch unterschiedliche Filterwerte haben, wird derselbe Index verwendet. Die folgende Abfrage verwendet den gleichen Index wie oben:

SELECT * FROM Person WHERE last_name = "Jones"
                       AND height < 63
                     ORDER BY height DESC

Für die beiden nachfolgenden Abfragen wird trotz ihrer unterschiedlichen Form ebenfalls derselbe Index verwendet:

SELECT * FROM Person WHERE last_name = "Friedkin"
                       AND first_name = "Damian"
                     ORDER BY height ASC

und

SELECT * FROM Person WHERE last_name = "Blair"
                  ORDER BY first_name, height ASC

Indexkonfiguration

Standardmäßig definiert Datastore für jedes Attribut jedes Entitätstyps automatisch einen Index. Diese vordefinierten Indexe reichen aus, um eine Vielzahl von einfachen Abfragen wie Gleichheitsabfragen oder einfache Ungleichheitsabfragen auszuführen. Für alle anderen Abfragen muss die Anwendung die erforderlichen Indexe in einer Indexkonfigurationsdatei mit dem Namen index.yaml definieren. Wenn die Anwendung versucht, eine Abfrage auszuführen, die nicht mit den verfügbaren (d. h. vordefinierten oder in der Indexkonfigurationsdatei definierten) Indexen ausgeführt werden kann, schlägt die Abfrage mit der Ausnahme NeedIndexError fehl.

Datastore erstellt für folgende Abfragetypen automatisch Indexe:

  • Typlose Abfragen, die nur Ancestors und Schlüsselfilter benötigen
  • Abfragen, die nur Ancestors und Gleichheitsfilter benötigen
  • Abfragen, in denen nur Ungleichheitsfilter verwendet werden, die auf ein einziges Attribut beschränkt sind
  • Abfragen, in denen nur Ancestor-Filter, Gleichheitsfilter für Attribute und Ungleichheitsfilter für Schlüssel verwendet werden
  • Abfragen ohne Filter und mit nur einer Sortierung nach einem Attribut, entweder auf- oder absteigend

Bei anderen Abfragetypen müssen die Indexe in der Indexkonfigurationsdatei angegeben werden, einschließlich:

  • Abfragen mit Ancestor- und Ungleichheitsfilter
  • Abfragen mit einem oder mehreren Ungleichheitsfiltern für ein Attribut sowie einem oder mehreren Gleichheitsfiltern für andere Attribute
  • Abfragen mit einer Sortierreihenfolge nach Schlüsseln in absteigender Reihenfolge
  • Abfragen mit mehreren Sortierreihenfolgen

Indexe und Attribute

Nachfolgend sind einige bei Indexen besonders zu beachtenden Aspekte und ihre Auswirkungen auf die Attribute von Entitäten in Datastore beschrieben:

Attribute mit gemischten Werttypen

Wenn zwei Entitäten Attribute mit dem gleichen Namen, aber mit unterschiedlichen Werttypen haben, sortiert ein Index für das Attribut die Entitäten zuerst nach dem Werttyp und dann nach einer für den jeweiligen Typ geeigneten Sekundärreihenfolge. Wird beispielsweise nach dem Attribut age sortiert und zwei verschiedene Entitäten haben jeweils ein Attribut mit diesem Namen (age) – eines mit ganzzahligem Wert und eines mit Stringwert – wird die Entität mit dem ganzzahligen Wert immer vor der Entität mit dem Stringwert gelistet, unabhängig von den jeweiligen Attributwerten.

Das ist besonders bei ganzzahligen und Gleitkommazahlenwerten zu beachten, da diese von Datastore wie separate Werttypen behandelt werden. Da Ganzzahlen immer vor den Gleitkommazahlen aufgeführt werden, wird ein Attribut mit dem ganzzahligen Wert 38 vor dem Gleitkommazahlenwert 37.5 gereiht.

Nicht indexierte Attribute

Wenn Sie keinen Filter oder keine Sortierreihenfolge für ein bestimmtes Attribut benötigen, können Sie Datastore anweisen, keine Indexierung für dieses Attribut durchzuführen, indem Sie das Attribut als nicht indexiert deklarieren. Dadurch verringern sich die Kosten für die Ausführung Ihrer Anwendung, da die Anzahl der auszuführenden Datastore-Schreibvorgänge verringert wird. Eine Entität mit einem nicht indexierten Attribut verhält sich so, als sei es nicht festgelegt worden. Bei Abfragen mit einem Filter oder einer Sortierfolge für das nicht indexierte Attribut wird diese Entität niemals angezeigt.

Hinweis: Wenn ein Attribut in einem Index aufgeführt ist, der sich aus mehreren Attributen zusammensetzt, wird durch ihre Festlegung auf "nicht indexiert" verhindert, dass sie in dem zusammengesetzten Index indexiert wird.

Angenommen, eine Entität hat die Attribute a und b und Sie möchten einen Index erstellen, der Abfragen wie WHERE a ="bike" and b="red" erfüllen kann. Angenommen, Sie interessieren sich nicht für die Abfragen WHERE a="bike" und WHERE b="red". Wenn Sie a als nicht indexiert festgelegt haben und einen Index für a und b erstellt haben, erstellt Datastore keine Indexeinträge für den Index a und b und die Abfrage WHERE a="bike" and b="red" schlägt fehlt. Damit Datastore für die Indexe a und b Einträge erstellt, muss sowohl a als auch b indexiert werden.

Sie deklarieren ein nicht indexiertes Attribut, indem Sie indexed=False im Attributskonstruktor festlegen:

class Person(db.Model):
  name = db.StringProperty()
  age = db.IntegerProperty(indexed=False)

Sie können das Attribut später wieder in "indexiert" ändern, indem Sie den Konstruktor mit indexed=True noch einmal aufrufen:

class Person(db.Model):
  name = db.StringProperty()
  age = db.IntegerProperty(indexed=True)

Wenn Sie eine Property von nicht indexiert auf indexiert ändern, sind etwaige vor der Änderung erstellte Entitäten nicht von der Änderung betroffen. Abfragen, mit denen solche Attribute gefiltert werden, geben diese Entitäten nicht als Ergebnis aus, da diese bei Erstellung der Abfrage nicht im Abfrageindex enthalten waren. Um die Entitäten für zukünftige Abfragen zur Verfügung zu stellen, müssen Sie diese noch einmal in Datastore festlegen, damit sie in den entsprechenden Indexen aufgeführt werden. Für jede solche bereits bestehende Entität sind also folgende Schritte nötig:

  1. Rufen Sie die Entität in Datastore ab (get).
  2. Legen Sie die Entität noch einmal in Datastore fest (put).

Wenn Sie ein Attribut von "indexiert" in "nicht indexiert" ändern, sind entsprechend nur Entitäten betroffen, die nach der Änderung in Datastore geschrieben wurden. Die Indexeinträge für vorhandene Entitäten mit diesem Attribut bleiben bestehen, bis die Entitäten aktualisiert oder gelöscht werden. Um keine unerwünschten Ergebnisse zu erhalten, müssen Sie alle Abfragen, die nach diesem (nun nicht indexierten) Attribut filtern oder sortieren, aus Ihrem Code dauerhaft löschen.

Indexbeschränkungen

In Datastore gelten Beschränkungen für die Anzahl und Gesamtgröße der Indexeinträge, die mit einer einzelnen Entität verknüpft werden können. Diese Beschränkungen sind sehr großzügig bemessen und sind für die meisten Anwendungen nicht relevant. Unter bestimmten Umständen könnten sie jedoch für Sie relevant werden.

Wie oben beschrieben, erstellt Cloud Datastore für jedes Attribut jeder Entität einen Eintrag in einem vordefinierten Index, außer für lange Textstrings (Text), lange Bytestrings (Blob) und Attribute, die Sie ausdrücklich als nicht indexiert deklariert haben. Das Attribut kann auch in zusätzlichen benutzerdefinierten Indexen enthalten sein, die Sie in der Konfigurationsdatei index.yaml angegeben haben. Wenn eine Entität keine gelisteten Properties hat, verfügt sie maximal über einen Eintrag in einem solchen benutzerdefinierten Index (bei Nicht-Ancestor-Indexen) bzw. einen Eintrag für jeden Ancestor der Entität (bei Ancestor-Indexen). Jeder dieser Indexeinträge muss einzeln aktualisiert werden, wenn sich der Wert des Attributs ändert.

Bei einem Attribut, das für jede Entität über einen einzelnen Wert verfügt, muss jeder mögliche Wert nur einmal pro Entität im vordefinierten Index des Attributs gespeichert werden. Trotzdem ist es möglich, dass eine Entität mit einer großen Anzahl solcher Einzelwert-Attribute das Indexeintrags- oder Eintragsgrößenlimit überschreitet. Ebenso benötigt eine Entität, die mehrere Werte für dasselbe Attribut haben kann, für jeden Wert einen eigenen Indexeintrag. Wenn die Anzahl der möglichen Werte besonders groß ist, kann es sein, dass eine solche Entität das Eintragslimit überschreitet.

Bei Entitäten mit mehreren Attributen, die wiederum mehrere Werte annehmen können, ist die Situation noch schwieriger. Der Index muss einen Eintrag für jede mögliche Kombination der Attributwerte enthalten, um einer solchen Entität gerecht zu werden. Bei benutzerdefinierten Indexen, die sich auf mehrere Attribute beziehen, die wiederum über mehrere mögliche Werte verfügen, kann die Anzahl von Kombinationen "explodieren". Dadurch wird für eine Entität mit einer relativ geringen Anzahl an möglichen Attributwerten eine große Anzahl an Einträgen erforderlich. Durch diese explodierenden Indexe können die Kosten für das Schreiben einer Entität in Datastore erheblich steigen, da eine hohe Anzahl an Indexeinträgen aktualisiert werden muss. Außerdem kann das leicht dazu führen, dass die Entität das Indexeintrags- oder Eintragsgrößenlimit überschreitet.

Betrachten Sie folgende Abfrage:

SELECT * FROM Widget WHERE x=1 AND y=2 ORDER BY date

Auf dieser Grundlage schlägt das SDK den folgenden Index vor:

indexes:
- kind: Widget
  properties:
  - name: x
  - name: y
  - name: date
Dieser Index erfordert insgesamt |x| * |y| * |date| Einträge für jede Entität (wobei |x| die Anzahl der Werte angibt, die der Entität für Attribut x zugeordnet sind). Zum Beispiel der folgende Code
class Widget(db.Expando):
  pass

e2 = Widget()
e2.x = [1, 2, 3, 4]
e2.y = ['red', 'green', 'blue']
e2.date = datetime.datetime.now()
e2.put()

erstellt eine Entität mit vier Werten für das Attribut x, drei Werte für das Attribut y und dem aktuellen Datum als Wert für das Attribut date. In diesem Fall werden 12 Indexeinträge benötigt – einer für jede mögliche Kombination an Attributwerten:

(1, "red", <now>) (1, "green", <now>) (1, "blue", <now>)

(2, "red", <now>) (2, "green", <now>) (2, "blue", <now>)

(3, "red", <now>) (3, "green", <now>) (3, "blue", <now>)

(4, "red", <now>) (4, "green", <now>) (4, "blue", <now>)

Wenn dasselbe Attribut mehrere Male wiederholt wird, kann Datastore explodierende Indexe erkennen und alternative Indexe vorschlagen. In allen anderen Fällen (z. B. bei der in diesem Beispiel definierten Abfrage) erstellt Datastore jedoch einen explodierenden Index. In diesem Fall können Sie den explodierenden Index umgehen, indem Sie mithilfe Ihrer Indexkonfigurationsdatei manuell einen Index definieren:

indexes:
- kind: Widget
  properties:
  - name: x
  - name: date
- kind: Widget
  properties:
  - name: y
  - name: date
Dadurch wird die Anzahl der erforderlichen Einträge auf nur (|x| * |date| + |y| * |date|) bzw. 7 statt 12 Einträgen reduziert:

(1, <now>) (2, <now>) (3, <now>) (4, <now>)

("red", <now>) ("green", <now>) ("blue", <now>)

Jeder Put-Vorgang, der dazu führt, dass ein Index das Eintrags- oder Eintragsgrößenlimit überschreitet, schlägt mit der Ausnahme BadRequestError fehl. Der Text der Fehlermeldung beschreibt, welches Limit überschritten wurde ("Too many indexed properties" oder "Index entries too large") und welcher benutzerdefinierte Index die Ursache war. Wenn Sie einen neuen Index erstellen, der die Limits für eine Entität überschreiten würde, schlagen Abfragen des Index fehl und der Index wird in der Cloud Console mit dem Status Error angezeigt. So können Sie Indexe mit dem Status Error reparieren:

  1. Entfernen Sie den Index mit dem Status Error aus der Datei index.yaml.

  2. Führen Sie den folgenden Befehl aus dem Verzeichnis aus, in dem sich index.yaml befindet, um diesen Index aus Datastore zu entfernen:

    gcloud datastore cleanup-indexes index.yaml
    
  3. Beheben Sie die Fehlerursache. Beispiel:

    • Formulieren Sie die Indexdefinition und die entsprechenden Abfragen um.
    • Entfernen Sie die Entitäten, die dazu führen, dass ein Index explodiert.
  4. Fügen Sie den Index wieder in die Datei index.yaml ein.

  5. Führen Sie den folgenden Befehl in dem Verzeichnis aus, das index.yaml enthält, um den Index in Datastore zu erstellen:

    gcloud datastore create-indexes index.yaml
    

Das Explodieren von Indexen können Sie verhindern, indem Sie Abfragen vermeiden, für die ein benutzerdefinierter Index erforderlich ist, der ein Listenattribut verwendet. Dazu gehören wie oben beschrieben Abfragen mit mehreren Sortierreihenfolgen und Abfragen mit einer Kombination aus Gleichheits- und Ungleichheitsfiltern.