קריאות מחוץ לעסקאות

בדף הזה מוסבר איך לבצע קריאות ב-Spanner מחוץ להקשר של טרנזקציות לקריאה בלבד ולקריאה וכתיבה. אם אחד מהמקרים הבאים מתקיים, כדאי לעיין בדף עסקאות:

  • אם אתם צריכים לכתוב, בהתאם לערך של קריאה אחת או יותר, אתם צריכים לבצע את הקריאה כחלק מעסקת קריאה-כתיבה. מידע נוסף זמין במאמר בנושא עסקאות קריאה-כתיבה.

  • אם אתם מבצעים כמה קריאות שדורשות תצוגה עקבית של הנתונים, כדאי לבצע את הקריאות כחלק מעסקה לקריאה בלבד. מידע נוסף זמין במאמר בנושא עסקאות לקריאה בלבד.

סוגי קריאה

‫Spanner מאפשר לכם לקבוע את רמת העדכניות של הנתונים כשאתם קוראים נתונים, באמצעות שני סוגים של קריאות:

  • קריאה חזקה היא קריאה בסימן זמן נוכחי, ויש הבטחה שהיא תראה את כל הנתונים שנשמרו עד תחילת הקריאה. ב-Spanner, ברירת המחדל היא שימוש בקריאות חזקות כדי להציג בקשות קריאה.
  • קריאה לא עדכנית היא קריאה שמתבצעת בחותמת זמן שכבר חלפה. אם האפליקציה שלכם רגישה לזמן האחזור אבל יכולה להתמודד עם נתונים לא עדכניים, קריאות לא עדכניות יכולות לשפר את הביצועים.

כדי לבחור את סוג הקריאה הרצוי, מגדירים חסימה של חותמת הזמן בבקשת הקריאה. כדאי לפעול לפי השיטות המומלצות הבאות כשבוחרים חותמת זמן:

  • כדאי לבחור קריאות חזקות כשהדבר אפשרי. זהו חותם הזמן שמוגדר כברירת מחדל לקריאות ב-Spanner, כולל עסקאות לקריאה בלבד. קריאות חזקות מבטיחות את ההשפעות של כל העסקאות שבוצעו לפני תחילת הפעולה, ללא קשר לשכפול שמקבל את הקריאה. לכן, קריאות חזקות מפשטות את קוד האפליקציה והופכות את האפליקציות לאמינות יותר. מידע נוסף על מאפייני העקביות של Spanner זמין במאמר TrueTime and External Consistency.

    אופציונלית, כדי לצמצם את זמן האחזור של קריאה לעסקאות לקריאה בלבד שדורשות מודל עקביות חזק, אפשר להעניק לאזורים שאינם אזורים ראשיים, לאזורים עם הרשאת קריאה וכתיבה או לאזורים עם הרשאת קריאה בלבד את הסטטוס read lease region. כדי לעשות את זה, משתמשים בהסכמי שכירות לקריאה ב-Spanner. אזורים להקצאת זמן קצר לקריאה עוזרים למסד הנתונים שלכם להפחית את זמן האחזור של קריאה חזקה במופעים של שני אזורים או של מספר אזורים. עם זאת, פעולות כתיבה יחוו השהיה גבוהה יותר כשמשתמשים בהרשאת קריאה.

  • אם השהייה הופכת קריאות חזקות ללא מעשיות במצבים מסוימים, כדאי להשתמש בקריאות לא עדכניות (bounded-staleness או exact-staleness) כדי לשפר את הביצועים במקומות שבהם לא צריך שהקריאות יהיו עדכניות ככל האפשר. כפי שמתואר בדף שכפול, 15 שניות הוא ערך סביר של נתונים לא עדכניים לשימוש כדי להשיג ביצועים טובים.

קריאת נתונים באמצעות תפקיד במסד נתונים

אם אתם משתמשים בבקרת גישה ברמת דיוק גבוהה, אתם צריכים לבחור תפקיד במסד הנתונים כדי להריץ שאילתות והצהרות SQL, וכדי לבצע פעולות על שורות במסד הנתונים. התפקיד שבחרתם יישאר לאורך כל הסשן, עד שתשנו אותו.

במאמר גישה למסד נתונים עם בקרת גישה ברמת גרנולריות גבוהה מוסבר איך לבצע קריאה באמצעות תפקיד במסד נתונים.

שיטות קריאה יחידות

‫Spanner תומך בשיטות קריאה יחידות (כלומר, קריאה מחוץ להקשר של טרנזקציה) במסד נתונים עבור:

  • ביצוע הקריאה כהצהרת שאילתת SQL או באמצעות API לקריאה של Spanner.
  • ביצוע קריאה חזקה משורה אחת או מכמה שורות בטבלה.
  • ביצוע קריאה לא עדכנית משורה אחת או מכמה שורות בטבלה.
  • קריאה משורה אחת או מכמה שורות באינדקס משני.

אם אתם רוצים להפנות קריאות בודדות לרפליקה או לאזור ספציפיים במסגרת הגדרת מופע מרובה אזורים או הגדרה אזורית בהתאמה אישית עם אזורים אופציונליים לקריאה בלבד, כדאי לעיין במאמר בנושא קריאות מופנות.

בקטעים הבאים מוסבר איך להשתמש בשיטות קריאה באמצעות ספריות לקוח של Spanner.

הפעלת שאילתה

בדוגמה הבאה מוצג איך להריץ הצהרת שאילתת SQL על מסד נתונים.

GoogleSQL

C++‎

משתמשים ב-ExecuteQuery() כדי להריץ הצהרת שאילתת SQL על מסד נתונים.

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

  spanner::SqlStatement select("SELECT SingerId, LastName FROM Singers");
  using RowType = std::tuple<std::int64_t, std::string>;
  auto rows = client.ExecuteQuery(std::move(select));
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << "SingerId: " << std::get<0>(*row) << "\t";
    std::cout << "LastName: " << std::get<1>(*row) << "\n";
  }

  std::cout << "Query completed for [spanner_query_data]\n";
}

C#‎

אפשר להשתמש ב-ExecuteReaderAsync() כדי להריץ שאילתות במסד הנתונים.


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

public class QuerySampleDataAsyncSample
{
    public class Album
    {
        public int SingerId { get; set; }
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
    }

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

        var albums = new List<Album>();
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");

        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                SingerId = reader.GetFieldValue<int>("SingerId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle")
            });
        }
        return albums;
    }
}

המשך

אפשר להשתמש ב-Client.Single().Query כדי להריץ שאילתות במסד הנתונים.


import (
	"context"
	"fmt"
	"io"

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

func query(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 SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %d %s\n", singerID, albumID, albumTitle)
	}
}

Java

אפשר להשתמש ב-ReadContext.executeQuery כדי להריץ שאילתות במסד הנתונים.

static void query(DatabaseClient dbClient) {
  try (ResultSet resultSet =
      dbClient
          .singleUse() // Execute a single read or query against Cloud Spanner.
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Node.js

אפשר להשתמש ב-Database.run כדי להריץ שאילתות במסד הנתונים.

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

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

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

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

const query = {
  sql: 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
};

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

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