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 Indexe vor dem Hochladen der Anwendung manuell optimieren, indem Sie die Datei bearbeiten.
Hinweis: Der indexbasierte Abfragemechanismus unterstützt ein breites Spektrum an Abfragen und ist für die meisten Anwendungen geeignet. Allerdings werden einige Arten von Abfragen, die in anderen Datenbanktechnologien üblich sind, 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: Datastore unterscheidet zwischen einer Entität, die kein Attribut hat, und einer Entität, die das Attribut mit einem Nullwert None
umfasst. 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):
- In Gleichheitsfiltern verwendete Attribute
- In Ungleichheitsfiltern verwendetes Attribut (von denen es höchstens eines geben kann)
- 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 so aus:
- Identifizierung des zugehörigen Indexes, der Filterattribute, der Filteroperatoren und der Sortierreihenfolge eines Abfragetyps
- Scan des Indexes vom Anfang bis zur ersten Entität, die allen Filterbedingungen der Abfrage entspricht
- 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.
Um diese Indexe zu generieren, konfigurieren Sie diese so:
indexes:
- kind: Person
properties:
- name: last_name
direction: asc
- name: height
direction: desc
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 Gleitkommawerten zu beachten, die in 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 Gleitkommawert 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. Deklarieren Sie dazu das Attribut als nicht indexiert. 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 es in dem zusammengesetzten Index indexiert wird.
Angenommen, eine Entität hat die Attribute a und a und Sie möchten einen Index erstellen, der Abfragen wie WHERE a ="bike" and b="red"
erfüllen kann. Gehen Sie außerdem davon aus, dass die Abfragen WHERE a="bike"
und WHERE b="red"
für Sie irrelevant sind.
Wenn Sie a als nicht indexiert festgelegt haben und einen Index für a und a erstellt haben, erstellt Datastore keine Indexeinträge für den Index a und a und die Abfrage WHERE a="bike" and b="red"
schlägt fehlt. Damit Datastore für die Indexe a und a Einträge erstellt, muss sowohl a als auch a 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. Damit die Entitäten für zukünftige Abfragen zur Verfügung stehen, müssen Sie sie neu in Datastore schreiben, damit sie in die entsprechenden Indexe aufgenommen werden. Sie müssen also die folgenden Schritte für jede bestehende Entität ausführen:
- Rufen Sie die Entität aus Datastore ab (get).
- Schreiben Sie die Entität zurück in Datastore (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 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 mehrere mögliche Werte haben, kann die Anzahl von Kombinationen „explodieren“. Dadurch wird für eine Entität mit einer relativ geringen Anzahl möglicher Attributwerte eine große Anzahl Einträge erforderlich. Durch diese explodierenden Indexe können die Kosten für das Schreiben einer Entität in Datastore erheblich steigen, da eine hohe Anzahl Indexeinträge aktualisiert werden muss. Außerdem kann das leicht dazu führen, dass die Entität das Indexeintrags- oder Größ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
|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 erstellt der folgende Codeclass Widget(db.Expando):
pass
e2 = Widget()
e2.x = [1, 2, 3, 4]
e2.y = ['red', 'green', 'blue']
e2.date = datetime.datetime.now()
e2.put()
eine Entität mit vier Werten für das Attribut x
, drei Werten 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 von 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) generiert 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
(|x|
*
|date|
+
|y|
*
|date|)
oder 7 statt auf 12 Einträge 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 Indexeintrags- oder Größenlimit überschreitet, schlägt mit der Ausnahme BadRequestError
fehl. Der Text der Ausnahme 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 Google Cloud Console mit dem Status Error
angezeigt. So können Sie Indexe mit dem Status Error
reparieren:
Entfernen Sie den Index mit dem Status
Error
aus der Dateiindex.yaml
.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 indexes cleanup index.yaml
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.
Fügen Sie den Index wieder in die Datei
index.yaml
ein.Führen Sie den folgenden Befehl in dem Verzeichnis aus, das
index.yaml
enthält, um den Index in Datastore zu erstellen:gcloud datastore indexes create 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.