Sekundäre Indexe

In einer Spanner-Datenbank erstellt Spanner automatisch eine Index für den Primärschlüssel jeder Tabelle. Sie müssen zum Beispiel nichts , um den Primärschlüssel von Singers zu indexieren, da er automatisch für Sie indexiert wird.

Sie können auch sekundäre Indexe für andere Spalten erstellen. Wenn Sie einen sekundären Index für eine Spalte hinzufügen, können Sie Daten in dieser Spalte effizienter nachschlagen. Für Wenn Sie z. B. schnell ein Album anhand des Titels suchen möchten, sollten Sie einen sekundären Index für AlbumTitle erstellen, damit Spanner nicht die gesamte Tabelle scannen muss.

Wenn die Suche im vorherigen Beispiel innerhalb einer Lese-Schreib-Transaktion durchgeführt wird, vermeidet die effizientere Suche außerdem Sperren der gesamten Tabelle. wodurch gleichzeitige Einfügungen und Aktualisierungen in die Tabelle für Zeilen außerhalb des Suchbereich AlbumTitle.

Neben den Vorteilen, die sie für Lookups bieten, können sekundäre Indexe auch Spanner bei der Ausführung Scans effizienter gestalten. Indexscans anstelle von vollständigen Tabellenscans.

Spanner speichert die folgenden Daten in jedem sekundären Index:

Im Laufe der Zeit analysiert Spanner Ihre Tabellen, um sicherzustellen, dass Ihre sekundären -Indexe für die entsprechenden Abfragen verwendet werden.

Sekundären Index hinzufügen

Am effizientesten ist die Erweiterung einer Tabelle um einen sekundären Index gleich bei deren Erstellung. Um eine Tabelle und ihre Indexe gleichzeitig zu erstellen, senden Sie die DDL-Anweisungen für die neue Tabelle und die neuen Indexe in einer einzigen Anfrage an Spanner.

In Spanner können Sie einen neuen sekundären Index auch einem vorhandenen während die Datenbank weiter Traffic bereitstellt. Wie jedes andere Schema wird das Hinzufügen eines Index zu einer vorhandenen Datenbank erfordert, dass die Datenbank offline geschaltet wird und nicht ganze Spalten oder Tabellen gesperrt werden.

Immer wenn einer vorhandenen Tabelle ein neuer Index hinzugefügt wird, der Index automatisch auffüllt bzw. füllt, um die aktuelle Ansicht widerzuspiegeln der indexierten Daten. Spanner verwaltet diesen Backfill-Prozess Der Prozess wird im Hintergrund mit niedrigen Knotenressourcen ausgeführt. Priorität haben. In den meisten Fällen ist es nicht möglich, den Vorgang zu beschleunigen (z.B. durch weitere Knoten hinzufügen) und Backfills wirken sich nicht wesentlich auf den der Datenbankleistung.

Die Indexerstellung kann mehrere Minuten bis hin zu vielen Stunden in Anspruch nehmen. Da die Indexerstellung eine Schemaaktualisierung ist, unterliegt sie denselben Leistungsbeschränkungen wie jede andere Schemaaktualisierung. Wie viel Zeit zum Erstellen eines sekundären Index benötigt wird, hängt von mehreren Faktoren ab:

  • Die Größe des Datasets
  • Rechenkapazität der Instanz
  • Auslastung der Instanz

Den Fortschritt eines Index-Backfill-Prozesses können Sie im Abschnitt „Fortschritt“ aufrufen.

Die Verwendung der Spalte Commit-Zeitstempel als erster Teil des sekundären Index kann Hotspots erstellen und die Schreibleistung verringern.

Mit der Anweisung CREATE INDEX einen sekundären Index definieren in Ihrem Schema. Hier einige Beispiele:

So indexieren Sie alle Singers in der Datenbank nach ihrem Vor- und Nachnamen:

GoogleSQL

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);

PostgreSQL

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);

So erstellen Sie einen Index aller Songs in der Datenbank nach dem Wert von SongName:

GoogleSQL

CREATE INDEX SongsBySongName ON Songs(SongName);

PostgreSQL

CREATE INDEX SongsBySongName ON Songs(SongName);

Wenn Sie nur die Titel eines bestimmten Interpreten indexieren möchten, verwenden Sie die Klausel INTERLEAVE IN, um den Index in der Tabelle Singers zu verschachteln:

GoogleSQL

CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName),
    INTERLEAVE IN Singers;

PostgreSQL

CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName)
    INTERLEAVE IN Singers;

So indexieren Sie nur die Titel eines bestimmten Albums:

GoogleSQL

CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName),
    INTERLEAVE IN Albums;

PostgreSQL

CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName)
    INTERLEAVE IN Albums;

So indexieren Sie nach SongName in absteigender Reihenfolge:

GoogleSQL

CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC),
    INTERLEAVE IN Albums;

PostgreSQL

CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC)
    INTERLEAVE IN Albums;

Beachten Sie, dass die vorherige DESC-Anmerkung nur für SongName gilt. Wenn Sie nach anderen Indexschlüsseln in absteigender Reihenfolge indexieren möchten, versehen Sie sie ebenfalls mit DESC: SingerId DESC, AlbumId DESC.

Beachten Sie außerdem, dass PRIMARY_KEY ein reserviertes Wort ist und nicht als Name eines Index verwendet werden kann. Dies ist der Name, der dem Pseudo-Index zugewiesen wird, der beim Erstellen einer Tabelle mit PRIMARY KEY-Spezifikation erstellt wird.

Weitere Informationen und Best Practices für die Auswahl nicht verschränkter Indexe und verschränkter Indexe finden Sie unter Indexoptionen und Verschränkte Indexe für eine Spalte verwenden, deren Wert monoton erhöht oder reduziert wird..

Fortschritt des Index-Backfills prüfen

Console

  1. Klicken Sie im Spanner-Navigationsmenü auf den Tab Vorgänge. Die Auf der Seite Vorgänge wird eine Liste der aktuell ausgeführten Vorgänge angezeigt.

  2. Suchen Sie in der Liste nach dem Backfill-Vorgang. Wenn er noch ausgeführt wird, In der Spalte Ende sehen Sie den Prozentsatz abgeschlossen ist, wie in der folgenden Abbildung dargestellt:

    Screenshot der Fortschrittsanzeige mit 98%

gcloud

gcloud spanner operations describe verwenden um den Fortschritt eines Vorgangs zu überprüfen.

  1. Rufen Sie die Vorgangs-ID ab:

    gcloud spanner operations list --instance=INSTANCE-NAME \
    --database=DATABASE-NAME --type=DATABASE_UPDATE_DDL
    

    Ersetzen Sie Folgendes:

    • INSTANCE-NAME durch die Spanner-Instanz Namen.
    • DATABASE-NAME durch den Namen der Datenbank.

    Verwendungshinweise:

    • Wenn Sie die Liste einschränken möchten, geben Sie das Flag --filter an. Beispiel:

      • --filter="metadata.name:example-db" listet nur die Vorgänge auf in einer bestimmten Datenbank.
      • --filter="error:*" listet nur die Sicherungsvorgänge auf, die fehlgeschlagen sind.

      Informationen zur Filtersyntax finden Sie unter gcloud topic filters. Informationen zum Filtern von Sicherungsvorgängen finden Sie im Feld filter in ListBackupOperationsRequest.

    • Beim Flag --type wird nicht zwischen Groß- und Kleinschreibung unterschieden.

    Die Ausgabe sieht dann ungefähr so aus:

    OPERATION_ID     STATEMENTS                                                                                          DONE   @TYPE
    _auto_op_123456  CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)                                 False  UpdateDatabaseDdlMetadata
                    CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName), INTERLEAVE IN Albums
    _auto_op_234567                                                                                                      True   CreateDatabaseMetadata
    
  2. Führen Sie gcloud spanner operations describe aus:

    gcloud spanner operations describe \
    --instance=INSTANCE-NAME \
    --database=DATABASE-NAME \
    projects/PROJECT-NAME/instances/INSTANCE-NAME/databases/DATABASE-NAME/operations/OPERATION_ID
    

    Ersetzen Sie Folgendes:

    • INSTANCE-NAME: Der Name der Spanner-Instanz.
    • DATABASE-NAME: Der Name der Spanner-Datenbank.
    • PROJECT-NAME: Der Projektname.
    • OPERATION-ID: Die Vorgangs-ID des Vorgangs, den Sie ausführen möchten. überprüfen.

    Der Abschnitt progress in der Ausgabe zeigt den Prozentsatz des Vorgangs an. vollständig sind. Die Ausgabe sieht in etwa so aus:

    done: true
    ...
      progress:
      - endTime: '2021-01-22T21:58:42.912540Z'
        progressPercent: 100
        startTime: '2021-01-22T21:58:11.053996Z'
      - progressPercent: 67
        startTime: '2021-01-22T21:58:11.053996Z'
    ...
    

REST Version 1

Rufen Sie die Vorgangs-ID ab:

  gcloud spanner operations list --instance=INSTANCE-NAME 
--database=DATABASE-NAME --type=DATABASE_UPDATE_DDL

Ersetzen Sie Folgendes:

  • INSTANCE-NAME durch die Spanner-Instanz Namen.
  • DATABASE-NAME durch den Namen der Datenbank.

Ersetzen Sie diese Werte in den folgenden Anfragedaten:

  • PROJECT-ID: Projekt-ID.
  • INSTANCE-ID: Instanz-ID.
  • DATABASE-ID: die Datenbank-ID.
  • OPERATION-ID: die Vorgangs-ID.

HTTP-Methode und URL:

GET https://spanner.googleapis.com/v1/projects/PROJECT-ID/instances/INSTANCE-ID/databases/DATABASE-ID/operations/OPERATION-ID

Wenn Sie die Anfrage senden möchten, maximieren Sie eine der folgenden Optionen:

Sie sollten in etwa folgende JSON-Antwort erhalten:

{
...
    "progress": [
      {
        "progressPercent": 100,
        "startTime": "2023-05-27T00:52:27.366688Z",
        "endTime": "2023-05-27T00:52:30.184845Z"
      },
      {
        "progressPercent": 100,
        "startTime": "2023-05-27T00:52:30.184845Z",
        "endTime": "2023-05-27T00:52:40.750959Z"
      }
    ],
...
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.protobuf.Empty"
  }
}

Für gcloud und REST können Sie den Fortschritt jedes Index-Backfills sehen im Abschnitt progress. Für jede Anweisung im Anweisungsarray gibt es ein entsprechendes Feld im Array „progress“. Diese Fortschrittsarrayreihenfolge entspricht der Reihenfolge des Arrays „Anweisungen“. Sobald die Funktion verfügbar ist, Die Felder startTime, progressPercent und endTime werden entsprechend ausgefüllt. Die Ausgabe zeigt keine geschätzte Zeit für den Zeitpunkt des Backfills an. abgeschlossen wird.

Wenn der Vorgang zu lange dauert, können Sie ihn abbrechen. Weitere Informationen finden Sie unter Brechen Sie die Indexerstellung ab.

Szenarien beim Ansehen des Index-Backfill-Fortschritts

Es gibt verschiedene Szenarien, die Sie begegnen können, wenn Sie versuchen, Fortschritts eines Index-Backfills. Anweisungen zur Indexerstellung, die einen Index-Backfill erfordern, sind Teil von Schemaaktualisierungsvorgängen. Es können mehrere Anweisungen vorhanden sein, die Teil eines Schemaaktualisierungsvorgangs sind.

Das erste Szenario ist das einfachste, nämlich wenn die Anweisung zur Indexerstellung die erste Anweisung im Schemaaktualisierungsvorgang ist. Seit der Indexerstellung Anweisung ist die erste Anweisung, die verarbeitet und ausgeführt wird, von der Ausführungsreihenfolge abhängig. Sofort wird im Feld startTime der Anweisung zur Indexerstellung die Startzeit des Schemaaktualisierungsvorgangs ein. Als Nächstes wird der Index Das Feld progressPercent der Erstellungsanweisung wird ausgefüllt, wenn der Fortschritt von der Index-Backfill über 0 % liegt. Schließlich wird das Feld endTime einmal die Anweisung übergeben wird.

Das zweite Szenario ist, wenn die Anweisung zur Indexerstellung nicht das erste ist, in der Schemaaktualisierungsvorgang ausführen. Keine mit dem Index zusammenhängenden Felder create-Anweisung wird so lange dargestellt, bis die vorherige(n) Anweisung(en) erstellt wurde(n) festgelegt ist, weil Reihenfolge der Ausführung. Ähnlich wie im vorherigen Szenario gilt: Sobald die vorherigen Anweisungen in einem Commit-Vorgang übergeben wurden, Das Feld startTime der Anweisung zur Indexerstellung wird zuerst ausgefüllt, gefolgt von Das Feld progressPercent. Das Feld endTime wird ausgefüllt, sobald das Commit beendet.

Indexerstellung abbrechen

Sie können die Google Cloud CLI verwenden, um die Indexerstellung abzubrechen. Um eine Liste mit Schema-Update-Vorgänge für eine Spanner-Datenbank verwenden, gcloud spanner operations list-Befehl und fügen Sie die Option --filter:

gcloud spanner operations list \
    --instance=INSTANCE \
    --database=DATABASE \
    --filter="@TYPE:UpdateDatabaseDdlMetadata"

Suchen Sie nach der OPERATION_ID des Vorgangs, den Sie abbrechen möchten, und brechen Sie ihn mit dem Befehl gcloud spanner operations cancel ab:

gcloud spanner operations cancel OPERATION_ID \
    --instance=INSTANCE \
    --database=DATABASE

Vorhandene Indexe ansehen

Informationen zu vorhandenen Indexen in einer Datenbank können Sie mit der Methode Google Cloud Console oder die Google Cloud CLI:

Console

  1. Rufen Sie in der Google Cloud Console die Seite Spanner-Instanzen auf.

    Zur Seite "VM-Instanzen"

  2. Klicken Sie auf den Namen der Instanz, die Sie aufrufen möchten.

  3. Klicken Sie im linken Bereich auf die Datenbank, die Sie sich ansehen möchten, und klicken Sie dann auf die Tabelle, die Sie aufrufen möchten.

  4. Klicken Sie auf den Tab Indexe. In der Google Cloud Console wird eine Liste Indexe.

  5. Optional: Klicken Sie auf den Namen eines Index, um Details zu diesem zu erhalten, z. B. die Spalten, die er enthält.

gcloud

Führen Sie den Befehl gcloud spanner databases ddl describe aus:

    gcloud spanner databases ddl describe DATABASE \
        --instance=INSTANCE

Die gcloud CLI gibt die Datendefinitionssprache (DDL) aus. Anweisungen zum Erstellen der Tabellen und Indexe der Datenbank. Die CREATE INDEX-Anweisungen beschreiben die vorhandenen Indexe. Beispiel:

    --- |-
  CREATE TABLE Singers (
    SingerId INT64 NOT NULL,
    FirstName STRING(1024),
    LastName STRING(1024),
    SingerInfo BYTES(MAX),
  ) PRIMARY KEY(SingerId)
---
  CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)

Mit einem bestimmten Index abfragen

In den folgenden Abschnitten wird erläutert, wie Sie einen Index in einer SQL-Anweisung angeben und mit der Leseschnittstelle für Spanner. In den Beispielen in diesen Abschnitten wird davon ausgegangen, dass Sie der Tabelle Albums eine Spalte MarketingBudget hinzugefügt und einen Index mit dem Namen AlbumsByAlbumTitle erstellt haben:

GoogleSQL

CREATE TABLE Albums (
  SingerId         INT64 NOT NULL,
  AlbumId          INT64 NOT NULL,
  AlbumTitle       STRING(MAX),
  MarketingBudget  INT64,
) PRIMARY KEY (SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

PostgreSQL

CREATE TABLE Albums (
  SingerId         BIGINT NOT NULL,
  AlbumId          BIGINT NOT NULL,
  AlbumTitle       VARCHAR,
  MarketingBudget  BIGINT,
  PRIMARY KEY (SingerId, AlbumId)
) INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

Index in einer SQL-Anweisung angeben

Wenn Sie SQL zum Abfragen einer Spanner-Tabelle verwenden, verwendet alle Indexe, die die Effizienz der Abfrage wahrscheinlich erhöhen. Daher müssen Sie für SQL-Abfragen keinen Index angeben. Bei Abfragen, die für Ihre Arbeitslast entscheidend sind, empfiehlt Google jedoch, FORCE_INDEX-Anweisungen in Ihren SQL-Anweisungen zu verwenden, um eine konsistentere Leistung zu erzielen.

In einigen Fällen wählt Spanner möglicherweise einen Index aus, der eine Abfrage auslöst die Latenz zu erhöhen. Wenn Sie die Schritte zur Fehlerbehebung bei Leistungsabfällen ausgeführt haben und bestätigt haben, dass es sinnvoll ist, die Abfrage mit einem anderen Index zu versuchen, können Sie den Index als Teil Ihrer Abfrage angeben:

Verwenden Sie die Methode FORCE_INDEX, um einen Index in einer SQL-Anweisung anzugeben. Hinweis zur Angabe einer Indexanweisung. Indexanweisungen verwenden die folgende Syntax:

GoogleSQL

FROM MyTable@{FORCE_INDEX=MyTableIndex}

PostgreSQL

FROM MyTable /*@ FORCE_INDEX = MyTableIndex */

Sie können auch eine Indexanweisung verwenden, um Spanner anzuweisen, die Basisdatei zu scannen anstelle eines Index verwenden:

GoogleSQL

FROM MyTable@{FORCE_INDEX=_BASE_TABLE}

PostgreSQL

FROM MyTable /*@ FORCE_INDEX = _BASE_TABLE */

Das folgende Beispiel zeigt eine SQL-Abfrage, die einen Index angibt:

GoogleSQL

SELECT AlbumId, AlbumTitle, MarketingBudget
    FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
    WHERE AlbumTitle >= "Aardvark" AND AlbumTitle < "Goo";

PostgreSQL

SELECT AlbumId, AlbumTitle, MarketingBudget
    FROM Albums /*@ FORCE_INDEX = AlbumsByAlbumTitle */
    WHERE AlbumTitle >= 'Aardvark' AND AlbumTitle < 'Goo';

Eine Indexanweisung kann den Abfrageprozessor von Spanner dazu zwingen, zusätzliche Spalten, die für die Abfrage erforderlich sind, aber nicht im Index gespeichert sind. Der Abfrageprozessor ruft diese Spalten ab, indem er den Index und die Basistabelle zusammenfügt. Um diesen zusätzlichen Join zu vermeiden, verwenden Sie einen STORING-Klausel (GoogleSQL-Dialekt-Datenbanken) oder INCLUDE-Klausel (PostgreSQL-Dialektdatenbanken) für um die zusätzlichen Spalten im Index zu speichern.

Im vorherigen Beispiel ist die Spalte MarketingBudget die im Index gespeichert sind, die SQL-Abfrage aber diese Spalte auswählt. Daher Spanner muss in der Basistabelle nach der Spalte MarketingBudget suchen. und verknüpfen es dann mit Daten aus dem Index, um die Abfrageergebnisse zurückzugeben.

Spanner gibt einen Fehler aus, wenn die Indexanweisung Folgendes enthält: Probleme:

Die folgenden Beispiele zeigen, wie Abfragen geschrieben und ausgeführt werden, die die Werte von AlbumId, AlbumTitle und MarketingBudget mithilfe des Index AlbumsByAlbumTitle abrufen:

C++

void QueryUsingIndex(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::SqlStatement select(
      "SELECT AlbumId, AlbumTitle, MarketingBudget"
      " FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}"
      " WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title",
      {{"start_title", spanner::Value("Aardvark")},
       {"end_title", spanner::Value("Goo")}});
  using RowType =
      std::tuple<std::int64_t, std::string, absl::optional<std::int64_t>>;
  auto rows = client.ExecuteQuery(std::move(select));
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\t";
    auto marketing_budget = std::get<2>(*row);
    if (marketing_budget) {
      std::cout << "MarketingBudget: " << *marketing_budget << "\n";
    } else {
      std::cout << "MarketingBudget: NULL\n";
    }
  }
  std::cout << "Read completed for [spanner_query_data_with_index]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithIndexAsyncSample
{
    public class Album
    {
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
        public long MarketingBudget { get; set; }
    }

    public async Task<List<Album>> QueryDataWithIndexAsync(string projectId, string instanceId, string databaseId,
        string startTitle, string endTitle)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand(
            "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@ "
            + "{FORCE_INDEX=AlbumsByAlbumTitle} "
            + $"WHERE AlbumTitle >= @startTitle "
            + $"AND AlbumTitle < @endTitle",
            new SpannerParameterCollection
            {
                { "startTitle", SpannerDbType.String, startTitle },
                { "endTitle", SpannerDbType.String, endTitle }
            });

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle"),
                MarketingBudget = reader.IsDBNull(reader.GetOrdinal("MarketingBudget")) ? 0 : reader.GetFieldValue<long>("MarketingBudget")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"strconv"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func queryUsingIndex(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{
		SQL: `SELECT AlbumId, AlbumTitle, MarketingBudget
			FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
			WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title`,
		Params: map[string]interface{}{
			"start_title": "Aardvark",
			"end_title":   "Goo",
		},
	}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		var albumID int64
		var marketingBudget spanner.NullInt64
		var albumTitle string
		if err := row.ColumnByName("AlbumId", &albumID); err != nil {
			return err
		}
		if err := row.ColumnByName("AlbumTitle", &albumTitle); err != nil {
			return err
		}
		if err := row.ColumnByName("MarketingBudget", &marketingBudget); err != nil {
			return err
		}
		budget := "NULL"
		if marketingBudget.Valid {
			budget = strconv.FormatInt(marketingBudget.Int64, 10)
		}
		fmt.Fprintf(w, "%d %s %s\n", albumID, albumTitle, budget)
	}
	return nil
}

Java

static void queryUsingIndex(DatabaseClient dbClient) {
  Statement statement =
      Statement
          // We use FORCE_INDEX hint to specify which index to use. For more details see
          // https://cloud.google.com/spanner/docs/query-syntax#from-clause
          .newBuilder(
              "SELECT AlbumId, AlbumTitle, MarketingBudget "
                  + "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} "
                  + "WHERE AlbumTitle >= @StartTitle AND AlbumTitle < @EndTitle")
          // We use @BoundParameters to help speed up frequently executed queries.
          //  For more details see https://cloud.google.com/spanner/docs/sql-best-practices
          .bind("StartTitle")
          .to("Aardvark")
          .bind("EndTitle")
          .to("Goo")
          .build();
  try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %s %s\n",
          resultSet.getLong("AlbumId"),
          resultSet.getString("AlbumTitle"),
          resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const projectId = 'my-project-id';
// const startTitle = 'Ardvark';
// const endTitle = 'Goo';

// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');

// Instantiates a client
const spanner = new Spanner({
  projectId: projectId,
});

async function queryDataWithIndex() {
  // Gets a reference to a Cloud Spanner instance and database
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  const query = {
    sql: `SELECT AlbumId, AlbumTitle, MarketingBudget
                FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
                WHERE AlbumTitle >= @startTitle AND AlbumTitle <= @endTitle`,
    params: {
      startTitle: startTitle,
      endTitle: endTitle,
    },
  };

  // Queries rows from the Albums table
  try {
    const [rows] = await database.run(query);

    rows.forEach(row => {
      const json = row.toJSON();
      const marketingBudget = json.MarketingBudget
        ? json.MarketingBudget
        : null; // This value is nullable
      console.log(
        `AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}, MarketingBudget: ${marketingBudget}`
      );
    });
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    database.close();
  }
}
queryDataWithIndex();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Queries sample data from the database using SQL and an index.
 *
 * The index must exist before running this sample. You can add the index
 * by running the `add_index` sample or by running this DDL statement against
 * your database:
 *
 *     CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)
 *
 * Example:
 * ```
 * query_data_with_index($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 * @param string $startTitle The start of the title index.
 * @param string $endTitle   The end of the title index.
 */
function query_data_with_index(
    string $instanceId,
    string $databaseId,
    string $startTitle = 'Aardvark',
    string $endTitle = 'Goo'
): void {
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $parameters = [
        'startTitle' => $startTitle,
        'endTitle' => $endTitle
    ];

    $results = $database->execute(
        'SELECT AlbumId, AlbumTitle, MarketingBudget ' .
        'FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} ' .
        'WHERE AlbumTitle >= @startTitle AND AlbumTitle < @endTitle',
        ['parameters' => $parameters]
    );

    foreach ($results as $row) {
        printf('AlbumId: %s, AlbumTitle: %s, MarketingBudget: %d' . PHP_EOL,
            $row['AlbumId'], $row['AlbumTitle'], $row['MarketingBudget']);
    }
}

Python

def query_data_with_index(
    instance_id, database_id, start_title="Aardvark", end_title="Goo"
):
    """Queries sample data from the database using SQL and an index.

    The index must exist before running this sample. You can add the index
    by running the `add_index` sample or by running this DDL statement against
    your database:

        CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)

    This sample also uses the `MarketingBudget` column. You can add the column
    by running the `add_column` sample or by running this DDL statement against
    your database:

        ALTER TABLE Albums ADD COLUMN MarketingBudget INT64

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    params = {"start_title": start_title, "end_title": end_title}
    param_types = {
        "start_title": spanner.param_types.STRING,
        "end_title": spanner.param_types.STRING,
    }

    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(
            "SELECT AlbumId, AlbumTitle, MarketingBudget "
            "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} "
            "WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title",
            params=params,
            param_types=param_types,
        )

        for row in results:
            print("AlbumId: {}, AlbumTitle: {}, " "MarketingBudget: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# start_title = "An album title to start with such as 'Ardvark'"
# end_title   = "An album title to end with such as 'Goo'"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

sql_query = "SELECT AlbumId, AlbumTitle, MarketingBudget
             FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
             WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title"

params      = { start_title: start_title, end_title: end_title }
param_types = { start_title: :STRING,     end_title: :STRING }

client.execute(sql_query, params: params, types: param_types).rows.each do |row|
  puts "#{row[:AlbumId]} #{row[:AlbumTitle]} #{row[:MarketingBudget]}"
end

Index in der Leseschnittstelle angeben

Wenn Sie die Leseschnittstelle für Spanner verwenden und Spanner möchten Um einen Index zu verwenden, müssen Sie den Index angeben. Die Leseschnittstelle wählt den Index nicht automatisch aus.

Darüber hinaus muss Ihr Index alle Daten enthalten, die in den Abfrageergebnissen angezeigt werden, mit Ausnahme der Spalten, die Teil des Primärschlüssels sind. Diese Einschränkung besteht, weil die Leseschnittstelle keine Joins zwischen dem Index und der Basistabelle unterstützt. Wenn Sie weitere Spalten mit in die Abfrageergebnisse einbeziehen möchten, haben Sie mehrere Möglichkeiten:

  • Verwenden Sie eine STORING- oder INCLUDE-Klausel, um die zusätzlichen Spalten in auf den Index.
  • Machen Sie Ihre Abfrage, ohne die zusätzlichen Spalten einzubeziehen, und senden Sie dann mithilfe der Primärschlüssel eine weitere Abfrage, die die zusätzlichen Spalten liest.

Spanner gibt Werte aus dem Index in aufsteigender Sortierreihenfolge nach Index zurück. . Führen Sie die folgenden Schritte aus, um Werte in absteigender Reihenfolge abzurufen:

  • Kommentieren Sie den Indexschlüssel mit DESC. Beispiel:

    CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle DESC);
    

    Die Anmerkung DESC gilt für einen einzelnen Indexschlüssel. Wenn der Index mehr als einen Schlüssel enthält und die Abfrageergebnisse auf Basis aller Schlüssel in absteigender Reihenfolge angezeigt werden sollen, fügen Sie für jeden Schlüssel eine DESC-Anmerkung ein.

  • Wenn der Lesevorgang einen Schlüsselbereich angibt, muss der Schlüsselbereich ebenfalls in absteigender Reihenfolge angegeben werden. Mit anderen Worten: Der Wert des Startschlüssels muss größer sein als der Wert des Endschlüssels.

Das folgende Beispiel veranschaulicht, wie die Werte von AlbumId und AlbumTitle mit dem Index AlbumsByAlbumTitle abgerufen werden:

C++

void ReadDataWithIndex(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  auto rows =
      client.Read("Albums", google::cloud::spanner::KeySet::All(),
                  {"AlbumId", "AlbumTitle"},
                  google::cloud::Options{}.set<spanner::ReadIndexNameOption>(
                      "AlbumsByAlbumTitle"));
  using RowType = std::tuple<std::int64_t, std::string>;
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\n";
  }
  std::cout << "Read completed for [spanner_read_data_with_index]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithIndexAsyncSample
{
    public class Album
    {
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
        public long MarketingBudget { get; set; }
    }

    public async Task<List<Album>> QueryDataWithIndexAsync(string projectId, string instanceId, string databaseId,
        string startTitle, string endTitle)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand(
            "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@ "
            + "{FORCE_INDEX=AlbumsByAlbumTitle} "
            + $"WHERE AlbumTitle >= @startTitle "
            + $"AND AlbumTitle < @endTitle",
            new SpannerParameterCollection
            {
                { "startTitle", SpannerDbType.String, startTitle },
                { "endTitle", SpannerDbType.String, endTitle }
            });

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle"),
                MarketingBudget = reader.IsDBNull(reader.GetOrdinal("MarketingBudget")) ? 0 : reader.GetFieldValue<long>("MarketingBudget")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func readUsingIndex(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	iter := client.Single().ReadUsingIndex(ctx, "Albums", "AlbumsByAlbumTitle", spanner.AllKeys(),
		[]string{"AlbumId", "AlbumTitle"})
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var albumID int64
		var albumTitle string
		if err := row.Columns(&albumID, &albumTitle); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %s\n", albumID, albumTitle)
	}
}

Java

static void readUsingIndex(DatabaseClient dbClient) {
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .readUsingIndex(
              "Albums",
              "AlbumsByAlbumTitle",
              KeySet.all(),
              Arrays.asList("AlbumId", "AlbumTitle"))) {
    while (resultSet.next()) {
      System.out.printf("%d %s\n", resultSet.getLong(0), resultSet.getString(1));
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const projectId = 'my-project-id';

// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');

// Instantiates a client
const spanner = new Spanner({
  projectId: projectId,
});

async function readDataWithIndex() {
  // Gets a reference to a Cloud Spanner instance and database
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  const albumsTable = database.table('Albums');

  const query = {
    columns: ['AlbumId', 'AlbumTitle'],
    keySet: {
      all: true,
    },
    index: 'AlbumsByAlbumTitle',
  };

  // Reads the Albums table using an index
  try {
    const [rows] = await albumsTable.read(query);

    rows.forEach(row => {
      const json = row.toJSON();
      console.log(`AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`);
    });
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    database.close();
  }
}
readDataWithIndex();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Reads sample data from the database using an index.
 *
 * The index must exist before running this sample. You can add the index
 * by running the `add_index` sample or by running this DDL statement against
 * your database:
 *
 *     CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)
 *
 * Example:
 * ```
 * read_data_with_index($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function read_data_with_index(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $keySet = $spanner->keySet(['all' => true]);
    $results = $database->read(
        'Albums',
        $keySet,
        ['AlbumId', 'AlbumTitle'],
        ['index' => 'AlbumsByAlbumTitle']
    );

    foreach ($results->rows() as $row) {
        printf('AlbumId: %s, AlbumTitle: %s' . PHP_EOL,
            $row['AlbumId'], $row['AlbumTitle']);
    }
}

Python

def read_data_with_index(instance_id, database_id):
    """Reads sample data from the database using an index.

    The index must exist before running this sample. You can add the index
    by running the `add_index` sample or by running this DDL statement against
    your database:

        CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.snapshot() as snapshot:
        keyset = spanner.KeySet(all_=True)
        results = snapshot.read(
            table="Albums",
            columns=("AlbumId", "AlbumTitle"),
            keyset=keyset,
            index="AlbumsByAlbumTitle",
        )

        for row in results:
            print("AlbumId: {}, AlbumTitle: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

result = client.read "Albums", [:AlbumId, :AlbumTitle],
                     index: "AlbumsByAlbumTitle"

result.rows.each do |row|
  puts "#{row[:AlbumId]} #{row[:AlbumTitle]}"
end

Index für reine Indexscans erstellen

Optional können Sie die STORING-Klausel (für GoogleSQL-Dialekt-Datenbanken) oder INCLUDE verwenden. (für PostgreSQL-Dialekt-Datenbanken), um eine Kopie einer Spalte im -Index. Dieser Indextyp bietet Vorteile für Abfragen und Leseaufrufe unter Verwendung des Index, allerdings beansprucht das zusätzliche Speicherkapazität:

  • SQL-Abfragen, die den Index verwenden und Spalten auswählen, die in STORING oder INCLUDE erfordern keine zusätzliche Verknüpfung mit der Basistabelle.
  • read()-Aufrufe, die den Index verwenden, können vom STORING/INCLUDE-Klausel angegeben werden.

Angenommen, Sie haben eine alternative Version von AlbumsByAlbumTitle erstellt. die eine Kopie der Spalte MarketingBudget im Index speichert (beachten Sie die STORING oder INCLUDE fett formatiert):

GoogleSQL

CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);

PostgreSQL

CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) INCLUDE (MarketingBudget);

Mit dem alten AlbumsByAlbumTitle-Index muss Spanner mit dem Index verknüpft werden mit der Basistabelle und rufen Sie dann die Spalte aus der Basistabelle ab. Mit der neuen AlbumsByAlbumTitle2-Index verwendet, liest Spanner die Spalte direkt aus dem was effizienter ist.

Wenn Sie statt SQL die Leseschnittstelle verwenden, können Sie mit dem neuen Index AlbumsByAlbumTitle2 auch direkt die Spalte MarketingBudget lesen:

C++

void ReadDataWithStoringIndex(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  auto rows =
      client.Read("Albums", google::cloud::spanner::KeySet::All(),
                  {"AlbumId", "AlbumTitle", "MarketingBudget"},
                  google::cloud::Options{}.set<spanner::ReadIndexNameOption>(
                      "AlbumsByAlbumTitle2"));
  using RowType =
      std::tuple<std::int64_t, std::string, absl::optional<std::int64_t>>;
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\t";
    auto marketing_budget = std::get<2>(*row);
    if (marketing_budget) {
      std::cout << "MarketingBudget: " << *marketing_budget << "\n";
    } else {
      std::cout << "MarketingBudget: NULL\n";
    }
  }
  std::cout << "Read completed for [spanner_read_data_with_storing_index]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithStoringIndexAsyncSample
{
    public class Album
    {
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
        public long? MarketingBudget { get; set; }
    }

    public async Task<List<Album>> QueryDataWithStoringIndexAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        var cmd = connection.CreateSelectCommand(
            "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@ "
            + "{FORCE_INDEX=AlbumsByAlbumTitle2}");

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle"),
                MarketingBudget = reader.IsDBNull(reader.GetOrdinal("MarketingBudget")) ? 0 : reader.GetFieldValue<long>("MarketingBudget")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"strconv"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func readStoringIndex(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	iter := client.Single().ReadUsingIndex(ctx, "Albums", "AlbumsByAlbumTitle2", spanner.AllKeys(),
		[]string{"AlbumId", "AlbumTitle", "MarketingBudget"})
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var albumID int64
		var marketingBudget spanner.NullInt64
		var albumTitle string
		if err := row.Columns(&albumID, &albumTitle, &marketingBudget); err != nil {
			return err
		}
		budget := "NULL"
		if marketingBudget.Valid {
			budget = strconv.FormatInt(marketingBudget.Int64, 10)
		}
		fmt.Fprintf(w, "%d %s %s\n", albumID, albumTitle, budget)
	}
}

Java

static void readStoringIndex(DatabaseClient dbClient) {
  // We can read MarketingBudget also from the index since it stores a copy of MarketingBudget.
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .readUsingIndex(
              "Albums",
              "AlbumsByAlbumTitle2",
              KeySet.all(),
              Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %s %s\n",
          resultSet.getLong(0),
          resultSet.getString(1),
          resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const projectId = 'my-project-id';

// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');

// Instantiates a client
const spanner = new Spanner({
  projectId: projectId,
});

// "Storing" indexes store copies of the columns they index
// This speeds up queries, but takes more space compared to normal indexes
// See the link below for more information:
// https://cloud.google.com/spanner/docs/secondary-indexes#storing_clause
async function readDataWithStoringIndex() {
  // Gets a reference to a Cloud Spanner instance and database
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  const albumsTable = database.table('Albums');

  const query = {
    columns: ['AlbumId', 'AlbumTitle', 'MarketingBudget'],
    keySet: {
      all: true,
    },
    index: 'AlbumsByAlbumTitle2',
  };

  // Reads the Albums table using a storing index
  try {
    const [rows] = await albumsTable.read(query);

    rows.forEach(row => {
      const json = row.toJSON();
      let rowString = `AlbumId: ${json.AlbumId}`;
      rowString += `, AlbumTitle: ${json.AlbumTitle}`;
      if (json.MarketingBudget) {
        rowString += `, MarketingBudget: ${json.MarketingBudget}`;
      }
      console.log(rowString);
    });
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    database.close();
  }
}
readDataWithStoringIndex();

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Reads sample data from the database using an index with a storing
 * clause.
 *
 * The index must exist before running this sample. You can add the index
 * by running the `add_storing_index` sample or by running this DDL statement
 * against your database:
 *
 *     CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)
 *     STORING (MarketingBudget)
 *
 * Example:
 * ```
 * read_data_with_storing_index($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function read_data_with_storing_index(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $keySet = $spanner->keySet(['all' => true]);
    $results = $database->read(
        'Albums',
        $keySet,
        ['AlbumId', 'AlbumTitle', 'MarketingBudget'],
        ['index' => 'AlbumsByAlbumTitle2']
    );

    foreach ($results->rows() as $row) {
        printf('AlbumId: %s, AlbumTitle: %s, MarketingBudget: %d' . PHP_EOL,
            $row['AlbumId'], $row['AlbumTitle'], $row['MarketingBudget']);
    }
}

Python

def read_data_with_storing_index(instance_id, database_id):
    """Reads sample data from the database using an index with a storing
    clause.

    The index must exist before running this sample. You can add the index
    by running the `add_scoring_index` sample or by running this DDL statement
    against your database:

        CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)
        STORING (MarketingBudget)

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.snapshot() as snapshot:
        keyset = spanner.KeySet(all_=True)
        results = snapshot.read(
            table="Albums",
            columns=("AlbumId", "AlbumTitle", "MarketingBudget"),
            keyset=keyset,
            index="AlbumsByAlbumTitle2",
        )

        for row in results:
            print("AlbumId: {}, AlbumTitle: {}, " "MarketingBudget: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

result = client.read "Albums", [:AlbumId, :AlbumTitle, :MarketingBudget],
                     index: "AlbumsByAlbumTitle2"

result.rows.each do |row|
  puts "#{row[:AlbumId]} #{row[:AlbumTitle]} #{row[:MarketingBudget]}"
end

Index ändern

Mit der ALTER INDEX-Anweisung können Sie zusätzliche Spalten hinzufügen in einen vorhandenen Index einfügen oder Spalten löschen. Dieses kann die von der STORING-Klausel definierte Spaltenliste aktualisieren (GoogleSQL-Dialekt-Datenbanken) oder INCLUDE (PostgreSQL-Dialektdatenbanken) an, wenn Sie die -Index. Sie können diese Anweisung nicht verwenden, um Spalten hinzuzufügen oder zu löschen Spalten aus dem Indexschlüssel. Anstatt beispielsweise eine neue Index AlbumsByAlbumTitle2 erstellt haben, können Sie mit ALTER INDEX einen in AlbumsByAlbumTitle umwandeln, wie im folgenden Beispiel gezeigt:

GoogleSQL

ALTER INDEX AlbumsByAlbumTitle ADD STORED COLUMN MarketingBudget

PostgreSQL

ALTER INDEX AlbumsByAlbumTitle ADD INCLUDE COLUMN MarketingBudget

Wenn Sie einem vorhandenen Index eine neue Spalte hinzufügen, einen Hintergrund-Backfill-Prozess. Während des Backfills Die Spalte im Index ist nicht lesbar, sodass Sie möglicherweise nicht den erwarteten Leistungssteigerung. Sie können den Befehl gcloud spanner operations verwenden, um den lang andauernden Vorgang aufzulisten und seinen Status anzusehen. Weitere Informationen finden Sie unter Vorgang beschreiben.

Sie können auch einen Vorgang abbrechen abbrechen.

Nachdem der Backfill abgeschlossen ist, fügt Spanner die Spalte dem Index hinzu. Als Index größer wird, könnte dies die Abfragen verlangsamen, die den Index verwenden.

Das folgende Beispiel zeigt, wie Sie eine Spalte aus einem Index löschen:

GoogleSQL

ALTER INDEX AlbumsByAlbumTitle DROP STORED COLUMN MarketingBudget

PostgreSQL

ALTER INDEX AlbumsByAlbumTitle DROP INCLUDE COLUMN MarketingBudget

Index der NULL-Werte

NULL-Werte werden von Spanner standardmäßig indexiert. Erinnern Sie sich beispielsweise an die Definition des Index SingersByFirstLastName in der Tabelle Singers:

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);

Alle Zeilen von Singers werden auch dann indexiert, wenn FirstName oder LastName oder beide NULL sind.

Ein Diagramm zeigt Zeilen, die in einem nach NULL-Werten gefilterten Index ausgelassen wurden.

Wenn NULL-Werte indexiert werden, können Sie auf Daten, die NULL-Werte enthalten, effiziente SQL-Abfragen und Leseaufrufe durchführen. Verwenden Sie beispielsweise diese SQL-Abfrageanweisung, um alle Singers mit einem NULL FirstName zu finden:

GoogleSQL

SELECT s.SingerId, s.FirstName, s.LastName
    FROM Singers@{FORCE_INDEX=SingersByFirstLastName} AS s
    WHERE s.FirstName IS NULL;

PostgreSQL

SELECT s.SingerId, s.FirstName, s.LastName
    FROM Singers /* @ FORCE_INDEX = SingersByFirstLastName */ AS s
    WHERE s.FirstName IS NULL;

Sortierreihenfolge für NULL-Werte

Spanner sortiert NULL als kleinsten Wert für einen bestimmten Typ. Bei einer Spalte in aufsteigender (ASC) Reihenfolge werden NULL-Werte als Erstes einsortiert. Bei einer Spalte in absteigender (DESC) Reihenfolge werden NULL-Werte als Letztes einsortiert.

Indexierung von NULL-Werten deaktivieren

GoogleSQL

Um die Indexierung von Null-Werten zu deaktivieren, fügen Sie das Schlüsselwort NULL_FILTERED zur Indexdefinition hinzu. NULL_FILTERED-Indexe sind besonders nützlich, wenn es um die Indexierung von dünnbesetzten Spalten geht, bei denen die meisten Zeilen einen NULL-Wert enthalten. In diesen Fällen kann der NULL_FILTERED-Index erheblich kleiner und effizienter zu pflegen sein als ein normaler Index, der NULL-Werte enthält.

Hier eine alternative Definition von SingersByFirstLastName, die keine NULL-Werte indexiert:

CREATE NULL_FILTERED INDEX SingersByFirstLastNameNoNulls
    ON Singers(FirstName, LastName);

Das Keyword NULL_FILTERED gilt für alle Indexschlüsselspalten. Eine NULL-Filterung kann nicht pro Spalte angegeben werden.

PostgreSQL

Um Zeilen mit Nullwerten in einer oder mehreren indexierten Spalten herauszufiltern, verwenden Sie die Methode WHERE COLUMN IS NOT NULL-Prädikat. Null-gefilterte Indexe sind besonders nützlich für die Indexierung von dünnbesetzten Indexen. , wobei die meisten Zeilen einen NULL-Wert enthalten. In diesen Fällen Ein nach Null gefilterter Index kann erheblich kleiner und effizienter zu verwalten sein als ein normaler Index, der NULL-Werte enthält.

Hier eine alternative Definition von SingersByFirstLastName, die keine NULL-Werte indexiert:

CREATE INDEX SingersByFirstLastNameNoNulls
    ON Singers(FirstName, LastName)
    WHERE FirstName IS NOT NULL
    AND LastName IS NOT NULL;

Das Herausfiltern von NULL-Werten verhindert, dass Spanner ihn für einige Abfragen. Spanner verwendet den Index z. B. nicht für diese Abfrage, da der Index alle Singers-Zeilen auslässt, für die LastName den Wert NULL hat. als würde die Verwendung des Index verhindern, dass die Abfrage die richtigen Zeilen zurückgibt:

GoogleSQL

FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
    WHERE FirstName = "John";

PostgreSQL

FROM Singers /*@ FORCE_INDEX = SingersByFirstLastNameNoNulls */
    WHERE FirstName = 'John';

Damit Spanner den Index verwenden kann, müssen Sie die Abfrage so umschreiben, schließt die Zeilen aus, die auch aus dem Index ausgeschlossen sind:

GoogleSQL

SELECT FirstName, LastName
    FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
    WHERE FirstName = 'John' AND LastName IS NOT NULL;

PostgreSQL

SELECT FirstName, LastName
    FROM Singers /*@ FORCE_INDEX = SingersByFirstLastNameNoNulls */
    WHERE FirstName = 'John' AND LastName IS NOT NULL;

Index-Proto-Felder

Generierte Spalten zum Indexieren verwenden Felder in Protokollpuffern, die in PROTO-Spalten gespeichert sind, solange die Felder für die Indexierung verwendet werden, verwenden Sie den primitiven Datentyp oder den ENUM-Datentyp.

Wenn Sie einen Index für ein Protokollnachrichtenfeld definieren, können Sie es nicht ändern oder entfernen aus dem Proto-Schema. Weitere Informationen finden Sie unter Updates von Schemas, die einen Index für Proto-Felder enthalten

Das folgende Beispiel zeigt die Tabelle Singers mit einem Proto SingerInfo. angezeigt. So definieren Sie einen Index für das Feld nationality der PROTO: müssen Sie eine gespeicherte generierte Spalte erstellen:

GoogleSQL

CREATE PROTO BUNDLE (googlesql.example.SingerInfo, googlesql.example.SingerInfo.Residence);

CREATE TABLE Singers (
  SingerId INT64 NOT NULL,
  ...
  SingerInfo googlesql.example.SingerInfo,
  SingerNationality STRING(MAX) AS (SingerInfo.nationality) STORED
) PRIMARY KEY (SingerId);

Sie hat die folgende Definition des Prototyps googlesql.example.SingerInfo:

GoogleSQL

package googlesql.example;

message SingerInfo {
optional string    nationality = 1;
repeated Residence residence   = 2;

  message Residence {
    required int64  start_year   = 1;
    optional int64  end_year     = 2;
    optional string city         = 3;
    optional string country      = 4;
  }
}

Definieren Sie dann einen Index für das Feld nationality des Protokolls:

GoogleSQL

CREATE INDEX SingersByNationality ON Singers(SingerNationality);

Die folgende SQL-Abfrage liest Daten mit dem vorherigen Index:

GoogleSQL

SELECT s.SingerId, s.FirstName
FROM Singers AS s
WHERE s.SingerNationality = "English";

Hinweise:

  • Greifen Sie mit einer Indexanweisung auf Indexe im -Feldern von Protokollpufferspalten.
  • Sie können keinen Index für wiederkehrende Protokollpufferfelder erstellen.

Aktualisierungen von Schemas, die einen Index für Proto-Felder enthalten

Wenn Sie einen Index für ein Protokollnachrichtenfeld definieren, können Sie es nicht ändern oder entfernen aus dem Proto-Schema. Das liegt daran, dass nach der Definition des Index Typprüfung wird jedes Mal durchgeführt, wenn das Schema aktualisiert wird. Spanner erfasst die Typinformationen für alle Felder im Pfad die in der Indexdefinition verwendet werden.

Eindeutige Indexe

Indexe können als UNIQUE deklariert werden. UNIQUE-Indexe fügen den indexierten Daten eine Einschränkung hinzu, die doppelte Einträge für einen bestimmten Indexschlüssel verbietet. Diese Einschränkung wird von Spanner zum Zeitpunkt des Transaktions-Commits erzwungen. Genauer gesagt kann eine Transaktion, die dazu führen würde, dass mehrere Indexeinträge für denselben Schlüssel existieren, nicht ausgeführt werden.

Wenn eine Tabelle anfänglich keine UNIQUE-Daten enthält, schlägt der Versuch fehl, einen UNIQUE-Index zu erstellen.

Ein Hinweis zu UNIQUE NULL_FILTERED-Indexen

Ein UNIQUE NULL_FILTERED-Index erzwingt keine Eindeutigkeit des Indexschlüssels, wenn mindestens eine der Schlüsselkomponenten des Index NULL ist.

Angenommen, Sie haben die folgende Tabelle und den folgenden Index erstellt:

GoogleSQL

CREATE TABLE ExampleTable (
  Key1 INT64 NOT NULL,
  Key2 INT64,
  Key3 INT64,
  Col1 INT64,
) PRIMARY KEY (Key1, Key2, Key3);

CREATE UNIQUE NULL_FILTERED INDEX ExampleIndex ON ExampleTable (Key1, Key2, Col1);

PostgreSQL

CREATE TABLE ExampleTable (
  Key1 BIGINT NOT NULL,
  Key2 BIGINT,
  Key3 BIGINT,
  Col1 BIGINT,
  PRIMARY KEY (Key1, Key2, Key3)
);

CREATE UNIQUE INDEX ExampleIndex ON ExampleTable (Key1, Key2, Col1)
    WHERE Key1 IS NOT NULL
    AND Key2 IS NOT NULL
    AND Col1 IS NOT NULL;

Die folgenden zwei Zeilen in ExampleTable weisen die gleichen Werte für die Sekundärindexschlüssel Key1, Key2 und Col1 auf:

1, NULL, 1, 1
1, NULL, 2, 1

Da Key2 den Wert NULL hat und der Index nach Nullwerten gefiltert ist, werden die Zeilen nicht im Index ExampleIndex vorhanden ist. Da sie nicht in die werden sie vom Index nicht aufgrund eines Verstoßes gegen die Eindeutigkeit von (Key1, Key2, Col1) abgelehnt.

Wenn Sie möchten, dass der Index die Eindeutigkeit der Werte des Tupels (Key1, Key2, Col1), müssen Sie Key2 in der Tabelle mit NOT NULL annotieren definieren oder den Index erstellen, ohne Nullen zu filtern.

Index löschen

Mit der Anweisung DROP INDEX löschen Sie einen sekundären Index aus Ihres Schemas.

So löschen Sie den Index mit dem Namen SingersByFirstLastName:

DROP INDEX SingersByFirstLastName;

Index für schnelleres Scannen

Wann Spanner einen Tabellenscan (anstelle eines indexierten Scans) ausführen muss Lookup), um Werte aus einer oder mehreren Spalten abzurufen, können Sie schneller wenn für diese Spalten ein Index vorhanden ist, in der durch die Abfrage vorgegebenen Reihenfolge. Wenn Sie häufig Abfragen, die Scans erfordern, sollten Sie zur Unterstützung sekundäre Indexe erstellen. und diese Scans werden effizienter.

Dies gilt insbesondere, wenn Sie Spanner zum häufigen Scannen der Primärschlüssel oder einen anderen Index in umgekehrter Reihenfolge, Effizienz durch einen sekundären Index, der die gewählte Reihenfolge explizit ist.

Die folgende Abfrage gibt beispielsweise immer ein schnelles Ergebnis zurück, Spanner muss Songs scannen, um den niedrigsten Wert von SongId:

SELECT SongId FROM Songs LIMIT 1;

SongId ist der gespeicherte Primärschlüssel der Tabelle (wie alle Primärschlüssel). in aufsteigender Reihenfolge. Spanner kann den Index dieses Schlüssels scannen schnell das erste Ergebnis erhalten.

Ohne einen sekundären Index würde die folgende Abfrage so schnell wie möglich zurückgegeben, insbesondere wenn Songs viele Daten enthält:

SELECT SongId FROM Songs ORDER BY SongId DESC LIMIT 1;

Obwohl SongId der Primärschlüssel der Tabelle ist, hat Spanner keinen um den höchsten Wert der Spalte abzurufen, ohne auf einen vollständigen Tabellenscan an.

Wenn Sie den folgenden Index hinzufügen, könnte diese Abfrage mehr schnell:

CREATE INDEX SongIdDesc On Songs(SongId DESC);

Mit diesem Index würde Spanner ihn verwenden, um eine sehr viel schneller erhalten.

Nächste Schritte