Mulai menggunakan Spanner di Node.js


Tujuan

Tutorial ini memandu Anda melakukan langkah-langkah berikut menggunakan library klien Spanner untuk Node.js:

  • Membuat instance dan database Spanner.
  • Menulis, membaca, dan mengeksekusi kueri SQL pada data dalam {i>database<i}.
  • Perbarui skema database.
  • Memperbarui data menggunakan transaksi baca-tulis.
  • Tambahkan indeks sekunder ke database.
  • Menggunakan indeks untuk membaca dan mengeksekusi kueri SQL pada data.
  • Mengambil data menggunakan transaksi hanya baca.

Biaya

Tutorial ini menggunakan Spanner, yang merupakan komponen Google Cloud yang dapat ditagih. Untuk mengetahui informasi tentang biaya penggunaan Spanner, lihat Harga.

Sebelum memulai

Selesaikan langkah-langkah yang dijelaskan dalam Penyiapan, yang mencakup pembuatan dan penetapan project Google Cloud default, mengaktifkan penagihan, mengaktifkan Cloud Spanner API, dan menyiapkan OAuth 2.0 untuk mendapatkan kredensial autentikasi agar dapat menggunakan Cloud Spanner API.

Secara khusus, pastikan Anda menjalankan gcloud auth application-default login untuk menyiapkan lingkungan pengembangan lokal dengan kredensial autentikasi.

Menyiapkan lingkungan Node.js lokal

  1. Ikuti langkah-langkah untuk Menyiapkan Lingkungan Pengembangan Node.js

  2. Clone repositori aplikasi contoh ke komputer lokal Anda:

    git clone https://github.com/googleapis/nodejs-spanner
    

    Atau, Anda dapat mendownload sampel sebagai file ZIP dan mengekstraknya.

  3. Ubah ke direktori yang berisi kode contoh Spanner:

    cd samples/
    
  4. Instal dependensi menggunakan npm:

    npm install
    

Membuat instance

Saat pertama kali menggunakan Spanner, Anda harus membuat instance, yang merupakan alokasi resource yang digunakan oleh database Spanner. Saat membuat instance, Anda memilih konfigurasi instance, yang menentukan tempat data disimpan, dan juga jumlah node yang akan digunakan, yang menentukan jumlah penayangan dan resource penyimpanan dalam instance Anda.

Jalankan perintah berikut untuk membuat instance Spanner di region us-central1 dengan 1 node:

gcloud spanner instances create test-instance --config=regional-us-central1 \
    --description="Test Instance" --nodes=1

Perhatikan bahwa tindakan ini akan membuat instance dengan karakteristik berikut:

  • ID Instance test-instance
  • Nama tampilan Test Instance
  • Konfigurasi instance regional-us-central1 (Konfigurasi regional menyimpan data di satu region, sedangkan konfigurasi multi-region mendistribusikan data di beberapa region. Untuk mengetahui informasi lebih lanjut, lihat artikel Tentang instance.)
  • Jumlah node 1 (node_count sesuai dengan jumlah resource penyajian dan penyimpanan yang tersedia untuk database dalam instance. Pelajari lebih lanjut di Node dan unit pemrosesan.)

Anda akan melihat:

Creating instance...done.

Mencari contoh file

Repositori contoh berisi contoh yang menunjukkan cara menggunakan Spanner dengan Node.js.

Lihat file samples/schema.js, yang menunjukkan cara membuat database dan mengubah skema database. Datanya menggunakan skema contoh yang ditampilkan di halaman Model data dan skema.

Buat database

Buat database yang disebut example-db dalam instance bernama test-instance dengan menjalankan baris berikut di command line.

node schema.js createDatabase test-instance example-db MY_PROJECT_ID

Anda akan melihat:

Created database example-db on instance test-instance.

Kode berikut membuat database dan dua tabel dalam database.


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

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

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

const databaseAdminClient = spanner.getDatabaseAdminClient();

const createSingersTableStatement = `
CREATE TABLE Singers (
  SingerId    INT64 NOT NULL,
  FirstName   STRING(1024),
  LastName    STRING(1024),
  SingerInfo  BYTES(MAX),
  FullName    STRING(2048) AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED,
) PRIMARY KEY (SingerId)`;
const createAlbumsTableStatement = `
CREATE TABLE Albums (
  SingerId    INT64 NOT NULL,
  AlbumId     INT64 NOT NULL,
  AlbumTitle  STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE`;

// Creates a new database
try {
  const [operation] = await databaseAdminClient.createDatabase({
    createStatement: 'CREATE DATABASE `' + databaseID + '`',
    extraStatements: [
      createSingersTableStatement,
      createAlbumsTableStatement,
    ],
    parent: databaseAdminClient.instancePath(projectID, instanceID),
  });

  console.log(`Waiting for creation of ${databaseID} to complete...`);
  await operation.promise();

  console.log(`Created database ${databaseID} on instance ${instanceID}.`);
} catch (err) {
  console.error('ERROR:', err);
}

Langkah selanjutnya adalah menulis data ke database Anda.

Membuat klien database

Sebelum dapat melakukan pembacaan atau penulisan, Anda harus membuat Database:

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

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

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

// The query to execute
const query = {
  sql: 'SELECT 1',
};

// Execute a simple SQL statement
const [rows] = await database.run(query);
console.log(`Query: ${rows.length} found.`);
rows.forEach(row => console.log(row));

Anda dapat menganggap Database sebagai koneksi database: semua interaksi Anda dengan Spanner harus melalui Database. Biasanya Anda membuat Database saat aplikasi dimulai, kemudian Anda menggunakan kembali Database tersebut untuk membaca, menulis, dan mengeksekusi transaksi. Setiap klien menggunakan resource di Spanner.

Jika membuat beberapa klien di aplikasi yang sama, Anda harus memanggil Database.close() untuk menghapus resource klien, termasuk koneksi jaringan, segera setelah tidak diperlukan lagi.

Baca selengkapnya dalam referensi Database.

Menulis data dengan DML

Anda dapat menyisipkan data menggunakan Data Manipulation Language (DML) dalam transaksi baca-tulis.

Anda menggunakan metode runUpdate() untuk mengeksekusi pernyataan DML.

// 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);

database.runTransaction(async (err, transaction) => {
  if (err) {
    console.error(err);
    return;
  }
  try {
    const [rowCount] = await transaction.runUpdate({
      sql: `INSERT Singers (SingerId, FirstName, LastName) VALUES
      (12, 'Melissa', 'Garcia'),
      (13, 'Russell', 'Morales'),
      (14, 'Jacqueline', 'Long'),
      (15, 'Dylan', 'Shaw')`,
    });
    console.log(`${rowCount} records inserted.`);
    await transaction.commit();
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    database.close();
  }
});

Jalankan contoh menggunakan argumen writeUsingDml.

node dml.js writeUsingDml test-instance example-db MY_PROJECT_ID

Anda akan melihat:

4 records inserted.

Menulis data dengan mutasi

Anda juga dapat menyisipkan data menggunakan mutasi.

Anda menulis data menggunakan objek Table. Metode Table.insert() menambahkan baris baru ke tabel. Semua penyisipan dalam satu batch diterapkan secara atomik.

Kode ini menunjukkan cara menulis data menggunakan mutasi:

// 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);

// Instantiate Spanner table objects
const singersTable = database.table('Singers');
const albumsTable = database.table('Albums');

// Inserts rows into the Singers table
// Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so
// they must be converted to strings before being inserted as INT64s
try {
  await singersTable.insert([
    {SingerId: '1', FirstName: 'Marc', LastName: 'Richards'},
    {SingerId: '2', FirstName: 'Catalina', LastName: 'Smith'},
    {SingerId: '3', FirstName: 'Alice', LastName: 'Trentor'},
    {SingerId: '4', FirstName: 'Lea', LastName: 'Martin'},
    {SingerId: '5', FirstName: 'David', LastName: 'Lomond'},
  ]);

  await albumsTable.insert([
    {SingerId: '1', AlbumId: '1', AlbumTitle: 'Total Junk'},
    {SingerId: '1', AlbumId: '2', AlbumTitle: 'Go, Go, Go'},
    {SingerId: '2', AlbumId: '1', AlbumTitle: 'Green'},
    {SingerId: '2', AlbumId: '2', AlbumTitle: 'Forever Hold your Peace'},
    {SingerId: '2', AlbumId: '3', AlbumTitle: 'Terrified'},
  ]);

  console.log('Inserted data.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  await database.close();
}

Jalankan contoh menggunakan argumen insert.

node crud.js insert test-instance example-db MY_PROJECT_ID

Anda akan melihat:

Inserted data.

Membuat kueri data menggunakan SQL

Spanner mendukung antarmuka SQL untuk membaca data, yang dapat Anda akses dari command line menggunakan Google Cloud CLI atau secara terprogram menggunakan library klien Spanner untuk Node.js.

Pada command line

Jalankan pernyataan SQL berikut untuk membaca nilai semua kolom dari tabel Albums:

gcloud spanner databasesexecute-sql example-db --instance=test-instance \ --sql='SELECT SingerId, AlbumId, AlbumTitle FROM Album'

Hasilnya seharusnya:

SingerId AlbumId AlbumTitle
1        1       Total Junk
1        2       Go, Go, Go
2        1       Green
2        2       Forever Hold Your Peace
2        3       Terrified

Menggunakan library klien Spanner untuk Node.js

Selain menjalankan pernyataan SQL di command line, Anda dapat mengeluarkan pernyataan SQL yang sama secara terprogram menggunakan library klien Spanner untuk Node.js.

Gunakan Database.run() untuk menjalankan kueri SQL.

// 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();
}

Berikut cara mengeluarkan kueri dan mengakses data:

node crud.js query test-instance example-db MY_PROJECT_ID

Anda akan melihat hasil berikut:

SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified

Kueri menggunakan parameter SQL

Jika aplikasi Anda memiliki kueri yang sering dijalankan, Anda dapat meningkatkan performanya dengan membuat parameter pada kueri tersebut. Kueri parametrik yang dihasilkan dapat disimpan dalam cache dan digunakan kembali, sehingga mengurangi biaya kompilasi. Untuk informasi selengkapnya, lihat Menggunakan parameter kueri untuk mempercepat kueri yang sering dijalankan.

Berikut adalah contoh penggunaan parameter dalam klausa WHERE untuk mengueri data yang berisi nilai tertentu untuk LastName.

// 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, FirstName, LastName
        FROM Singers WHERE LastName = @lastName`,
  params: {
    lastName: 'Garcia',
  },
};

// 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}, FirstName: ${json.FirstName}, LastName: ${json.LastName}`
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

Berikut cara mengeluarkan kueri dan mengakses data:

node dml.js queryWithParameter test-instance example-db MY_PROJECT_ID

Anda akan melihat hasil berikut:

SingerId: 12, FirstName: Melissa, LastName: Garcia

Membaca data menggunakan read API

Selain antarmuka SQL Spanner, Spanner juga mendukung antarmuka baca.

Gunakan Table.read() untuk membaca baris dari database. Gunakan objek KeySet untuk menentukan kumpulan kunci dan rentang kunci yang akan dibaca.

Berikut cara membaca data:

// 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);

// Reads rows from the Albums table
const albumsTable = database.table('Albums');

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

try {
  const [rows] = await albumsTable.read(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();
}

Jalankan contoh menggunakan argumen read.

node crud.js read test-instance example-db MY_PROJECT_ID

Anda akan melihat output yang mirip dengan:

SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified

Memperbarui skema database

Asumsikan Anda perlu menambahkan kolom baru bernama MarketingBudget ke tabel Albums. Untuk menambahkan kolom baru ke tabel yang ada, skema database Anda harus diperbarui. Spanner mendukung pembaruan skema pada database sementara database terus menyalurkan traffic. Pembaruan skema tidak memerlukan pembuatan database secara offline dan pembaruan tersebut tidak mengunci seluruh tabel atau kolom. Anda dapat terus menulis data ke database selama pembaruan skema. Baca selengkapnya tentang update skema yang didukung dan performa perubahan skema di Membuat pembaruan skema.

Menambahkan kolom

Anda dapat menambahkan kolom di command line menggunakan Google Cloud CLI atau secara terprogram menggunakan library klien Spanner untuk Node.js.

Pada command line

Gunakan perintah ALTER TABLE berikut untuk menambahkan kolom baru ke tabel:

GoogleSQL

gcloud spanner databases ddl update example-db --instance=test-instance \
    --ddl='ALTER TABLE Albums ADD COLUMN MarketingBudget INT64'

PostgreSQL

gcloud spanner databases ddl update example-db --instance=test-instance \
    --ddl='ALTER TABLE Albums ADD COLUMN MarketingBudget BIGINT'

Anda akan melihat:

Schema updating...done.

Menggunakan library klien Spanner untuk Node.js

Gunakan Database.updateSchema untuk mengubah skema:


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

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

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

const databaseAdminClient = spanner.getDatabaseAdminClient();

// Creates a new index in the database
try {
  const [operation] = await databaseAdminClient.updateDatabaseDdl({
    database: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId
    ),
    statements: ['ALTER TABLE Albums ADD COLUMN MarketingBudget INT64'],
  });

  console.log('Waiting for operation to complete...');
  await operation.promise();

  console.log('Added the MarketingBudget column.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the spanner client when finished.
  // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
  spanner.close();
}

Jalankan contoh menggunakan argumen addColumn.

node schema.js addColumn test-instance example-db MY_PROJECT_ID

Anda akan melihat:

Added the MarketingBudget column.

Menulis data ke kolom baru

Kode berikut menulis data ke kolom baru. Fungsi ini menetapkan MarketingBudget ke 100000 untuk baris yang dikunci oleh Albums(1, 1) dan ke 500000 untuk baris yang dikunci oleh Albums(2, 2).

// 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);

// Update a row in the Albums table
// Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so they
// must be converted to strings before being inserted as INT64s
const albumsTable = database.table('Albums');

try {
  await albumsTable.update([
    {SingerId: '1', AlbumId: '1', MarketingBudget: '100000'},
    {SingerId: '2', AlbumId: '2', MarketingBudget: '500000'},
  ]);
  console.log('Updated data.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

Jalankan contoh menggunakan argumen update.

node crud.js update test-instance example-db MY_PROJECT_ID

Anda akan melihat:

Updated data.

Anda juga dapat menjalankan kueri SQL atau panggilan baca untuk mengambil nilai yang baru saja Anda tulis.

Berikut adalah kode untuk mengeksekusi kueri:

// This sample 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

// 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, MarketingBudget FROM Albums',
};

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

  rows.forEach(async row => {
    const json = row.toJSON();

    console.log(
      `SingerId: ${json.SingerId}, AlbumId: ${
        json.AlbumId
      }, MarketingBudget: ${
        json.MarketingBudget ? json.MarketingBudget : null
      }`
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

Untuk menjalankan kueri ini, jalankan contoh menggunakan argumen queryNewColumn.

node schema.js queryNewColumn test-instance example-db MY_PROJECT_ID

Anda akan melihat:

SingerId: 1, AlbumId: 1, MarketingBudget: 100000
SingerId: 1, AlbumId: 2, MarketingBudget: null
SingerId: 2, AlbumId: 1, MarketingBudget: null
SingerId: 2, AlbumId: 2, MarketingBudget: 500000
SingerId: 2, AlbumId: 3, MarketingBudget: null

Memperbarui data

Anda dapat memperbarui data menggunakan DML dalam transaksi baca-tulis.

Anda menggunakan metode runUpdate() untuk mengeksekusi pernyataan DML.

// This sample transfers 200,000 from the MarketingBudget field
// of the second Album to the first Album, as long as the second
// Album has enough money in its budget. Make sure to run the
// addColumn and updateData samples first (in that order).

// 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 transferAmount = 200000;

database.runTransaction((err, transaction) => {
  if (err) {
    console.error(err);
    return;
  }
  let firstBudget, secondBudget;
  const queryOne = `SELECT MarketingBudget FROM Albums
    WHERE SingerId = 2 AND AlbumId = 2`;

  const queryTwo = `SELECT MarketingBudget FROM Albums
  WHERE SingerId = 1 AND AlbumId = 1`;

  Promise.all([
    // Reads the second album's budget
    transaction.run(queryOne).then(results => {
      // Gets second album's budget
      const rows = results[0].map(row => row.toJSON());
      secondBudget = rows[0].MarketingBudget;
      console.log(`The second album's marketing budget: ${secondBudget}`);

      // Makes sure the second album's budget is large enough
      if (secondBudget < transferAmount) {
        throw new Error(
          `The second album's budget (${secondBudget}) is less than the transfer amount (${transferAmount}).`
        );
      }
    }),

    // Reads the first album's budget
    transaction.run(queryTwo).then(results => {
      // Gets first album's budget
      const rows = results[0].map(row => row.toJSON());
      firstBudget = rows[0].MarketingBudget;
      console.log(`The first album's marketing budget: ${firstBudget}`);
    }),
  ])
    .then(() => {
      // Transfers the budgets between the albums
      console.log(firstBudget, secondBudget);
      firstBudget += transferAmount;
      secondBudget -= transferAmount;

      console.log(firstBudget, secondBudget);

      // Updates the database
      // Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so they
      // must be converted (back) to strings before being inserted as INT64s.

      return transaction
        .runUpdate({
          sql: `UPDATE Albums SET MarketingBudget = @Budget
              WHERE SingerId = 1 and AlbumId = 1`,
          params: {
            Budget: firstBudget,
          },
        })
        .then(() =>
          transaction.runUpdate({
            sql: `UPDATE Albums SET MarketingBudget = @Budget
                  WHERE SingerId = 2 and AlbumId = 2`,
            params: {
              Budget: secondBudget,
            },
          })
        );
    })
    .then(() => {
      // Commits the transaction and send the changes to the database
      return transaction.commit();
    })
    .then(() => {
      console.log(
        `Successfully executed read-write transaction using DML to transfer ${transferAmount} from Album 2 to Album 1.`
      );
    })
    .then(() => {
      // Closes the database when finished
      database.close();
    });
});

Jalankan contoh menggunakan argumen writeWithTransactionUsingDml.

node dml.js writeWithTransactionUsingDml test-instance example-db MY_PROJECT_ID

Anda akan melihat:

Successfully executed read-write transaction using DML to transfer $200000 from Album 2 to Album 1.

Menggunakan indeks sekunder

Misalkan Anda ingin mengambil semua baris Albums yang memiliki nilai AlbumTitle dalam rentang tertentu. Anda dapat membaca semua nilai dari kolom AlbumTitle menggunakan pernyataan SQL atau panggilan baca, lalu menghapus baris yang tidak memenuhi kriteria, tetapi melakukan pemindaian tabel lengkap ini mahal, terutama untuk tabel dengan banyak baris. Namun, Anda dapat mempercepat pengambilan baris saat melakukan penelusuran berdasarkan kolom kunci non-utama dengan membuat indeks sekunder pada tabel.

Menambahkan indeks sekunder ke tabel yang ada memerlukan update skema. Seperti update skema lainnya, Spanner mendukung penambahan indeks saat database terus menyalurkan traffic. Spanner secara otomatis mengisi ulang indeks dengan data yang ada. Pengisian ulang mungkin memerlukan waktu beberapa menit untuk diselesaikan, tetapi Anda tidak perlu membuat database offline atau menghindari penulisan ke tabel yang diindeks selama proses ini. Untuk mengetahui detail selengkapnya, lihat Menambahkan indeks sekunder.

Setelah Anda menambahkan indeks sekunder, Spanner akan otomatis menggunakannya untuk kueri SQL yang cenderung berjalan lebih cepat dengan indeks tersebut. Jika menggunakan antarmuka baca, Anda harus menentukan indeks yang ingin digunakan.

Menambahkan indeks sekunder

Anda dapat menambahkan indeks di command line menggunakan gcloud CLI atau secara terprogram menggunakan library klien Spanner untuk Node.js.

Pada command line

Gunakan perintah CREATE INDEX berikut untuk menambahkan indeks ke database:

gcloud spanner databases ddl update example-db --instance=test-instance \
    --ddl='CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)'

Anda akan melihat:

Schema updating...done.

Menggunakan library klien Spanner untuk Node.js

Gunakan Database.updateSchema() untuk menambahkan indeks:

// 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,
});

const databaseAdminClient = spanner.getDatabaseAdminClient();

const request = ['CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)'];

// Creates a new index in the database
try {
  const [operation] = await databaseAdminClient.updateDatabaseDdl({
    database: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId
    ),
    statements: request,
  });

  console.log('Waiting for operation to complete...');
  await operation.promise();

  console.log('Added the AlbumsByAlbumTitle index.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the spanner client when finished.
  // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
  spanner.close();
}

Jalankan contoh menggunakan argumen createIndex.

node indexing.js createIndex test-instance example-db MY_PROJECT_ID

Menambahkan indeks dapat memerlukan waktu beberapa menit. Setelah indeks ditambahkan, Anda akan melihat:

Added the AlbumsByAlbumTitle index.

Membaca menggunakan indeks

Untuk kueri SQL, Spanner secara otomatis menggunakan indeks yang sesuai. Di antarmuka baca, Anda harus menentukan indeks dalam permintaan.

Untuk menggunakan indeks di antarmuka baca, gunakan metode Table.read().

// 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 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();
}

Jalankan contoh menggunakan argumen readIndex.

node indexing.js readIndex test-instance example-db MY_PROJECT_ID

Anda akan melihat:

AlbumId: 2, AlbumTitle: Forever Hold your Peace
AlbumId: 2, AlbumTitle: Go, Go, Go
AlbumId: 1, AlbumTitle: Green
AlbumId: 3, AlbumTitle: Terrified
AlbumId: 1, AlbumTitle: Total Junk

Menambahkan indeks untuk pembacaan khusus indeks

Anda mungkin telah memperhatikan bahwa contoh pembacaan sebelumnya tidak menyertakan pembacaan kolom MarketingBudget. Hal ini karena antarmuka baca Spanner tidak mendukung kemampuan untuk menggabungkan indeks dengan tabel data guna mencari nilai yang tidak disimpan dalam indeks.

Buat definisi alternatif AlbumsByAlbumTitle yang menyimpan salinan MarketingBudget dalam indeks.

Pada command line

GoogleSQL

gcloud spanner databases ddl update example-db --instance=test-instance \
    --ddl='CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)

PostgreSQL

gcloud spanner databases ddl update example-db --instance=test-instance \
    --ddl='CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) INCLUDE (MarketingBudget)

Menambahkan indeks dapat memerlukan waktu beberapa menit. Setelah indeks ditambahkan, Anda akan melihat:

Schema updating...done.

Menggunakan library klien Spanner untuk Node.js

Gunakan Database.updateSchema() untuk menambahkan indeks dengan klausa STORING:

// "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

// 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,
});

const databaseAdminClient = spanner.getDatabaseAdminClient();

const request = [
  'CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)',
];

// Creates a new index in the database
try {
  const [operation] = await databaseAdminClient.updateDatabaseDdl({
    database: databaseAdminClient.databasePath(
      projectId,
      instanceId,
      databaseId
    ),
    statements: request,
  });

  console.log('Waiting for operation to complete...');
  await operation.promise();

  console.log('Added the AlbumsByAlbumTitle2 index.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the spanner client when finished.
  // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
  spanner.close();
}

Jalankan contoh menggunakan argumen createStoringIndex.

node indexing.js createStoringIndex test-instance example-db MY_PROJECT_ID

Anda akan melihat:

Added the AlbumsByAlbumTitle2 index.

Sekarang Anda dapat menjalankan operasi baca yang mengambil semua kolom AlbumId, AlbumTitle, dan MarketingBudget dari indeks AlbumsByAlbumTitle2:

// "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

// 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 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();
}

Jalankan contoh menggunakan argumen readStoringIndex.

node indexing.js readStoringIndex test-instance example-db MY_PROJECT_ID

Anda akan melihat output yang mirip dengan:

AlbumId: 2, AlbumTitle: Forever Hold your Peace, MarketingBudget: 300000
AlbumId: 2, AlbumTitle: Go, Go, Go, MarketingBudget: null
AlbumId: 1, AlbumTitle: Green, MarketingBudget: null
AlbumId: 3, AlbumTitle: Terrified, MarketingBudget: null
AlbumId: 1, AlbumTitle: Total Junk, MarketingBudget: 300000

Mengambil data menggunakan transaksi hanya baca

Misalnya Anda ingin mengeksekusi lebih dari satu pembacaan pada stempel waktu yang sama. Transaksi hanya baca mengamati awalan yang konsisten dari histori commit transaksi, sehingga aplikasi Anda selalu mendapatkan data yang konsisten. Gunakan Database.runTransaction() untuk menjalankan transaksi hanya baca.

Berikut ini cara menjalankan kueri dan melakukan pembacaan dalam transaksi hanya baca yang sama:

// 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);

// Gets a transaction object that captures the database state
// at a specific point in time
database.getSnapshot(async (err, transaction) => {
  if (err) {
    console.error(err);
    return;
  }
  const queryOne = 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums';

  try {
    // Read #1, using SQL
    const [qOneRows] = await transaction.run(queryOne);

    qOneRows.forEach(row => {
      const json = row.toJSON();
      console.log(
        `SingerId: ${json.SingerId}, AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`
      );
    });

    const queryTwo = {
      columns: ['SingerId', 'AlbumId', 'AlbumTitle'],
    };

    // Read #2, using the `read` method. Even if changes occur
    // in-between the reads, the transaction ensures that both
    // return the same data.
    const [qTwoRows] = await transaction.read('Albums', queryTwo);

    qTwoRows.forEach(row => {
      const json = row.toJSON();
      console.log(
        `SingerId: ${json.SingerId}, AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`
      );
    });

    console.log('Successfully executed read-only transaction.');
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    transaction.end();
    // Close the database when finished.
    await database.close();
  }
});

Jalankan contoh menggunakan argumen readOnly.

node transaction.js readOnly test-instance example-db MY_PROJECT_ID

Anda akan melihat output yang mirip dengan:

SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified
SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
SingerId: 1, AlbumId: 2, AlbumTitle: Go, Go, Go
SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk
SingerId: 2, AlbumId: 1, AlbumTitle: Green
SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace
SingerId: 2, AlbumId: 3, AlbumTitle: Terrified
Successfully executed read-only transaction.

Pembersihan

Agar tidak menimbulkan biaya tambahan pada akun Penagihan Cloud Anda untuk resource yang digunakan dalam tutorial ini, hapus database dan hapus instance yang Anda buat.

Menghapus database

Jika instance dihapus, semua database di dalamnya akan dihapus secara otomatis. Langkah ini menunjukkan cara menghapus database tanpa menghapus instance (Anda masih akan dikenai biaya untuk instance tersebut).

Pada command line

gcloud spanner databases delete example-db --instance=test-instance

Menggunakan Konsol Google Cloud

  1. Buka halaman Spanner Instances di konsol Google Cloud.

    Buka halaman Instances

  2. Klik instance.

  3. Klik database yang ingin dihapus.

  4. Di halaman Database details, klik Delete.

  5. Konfirmasi bahwa Anda ingin menghapus database, lalu klik Delete.

Menghapus instance

Jika sebuah instance dihapus, semua database yang dibuat dalam instance tersebut akan otomatis dihapus.

Pada command line

gcloud spanner instances delete test-instance

Menggunakan Konsol Google Cloud

  1. Buka halaman Spanner Instances di konsol Google Cloud.

    Buka halaman Instances

  2. Klik instance Anda.

  3. Klik Delete.

  4. Konfirmasi bahwa Anda ingin menghapus instance, lalu klik Hapus.

Langkah selanjutnya