索引和地理區域分區

Spanner 提供多種索引,可提升查詢效能。 為架構和查詢模式選擇正確的索引類型至關重要,尤其是使用地理分區的資料庫。本頁說明不同索引類型的優點,以及選取和使用 Spanner 索引 (含地理位置分區) 的最佳做法。

索引類型

Spanner 支援全域、本機和遠端索引。每種類型都有不同的效能特性和用途。對於地理位置分割資料庫,瞭解這些索引類型非常重要。選擇合適的索引有助於最佳化資料庫結構定義和查詢,進而大幅縮短地理區域分割資料庫的延遲時間。如果資料庫未使用地理位置分割,瞭解這些索引類型就比較不重要,因為這些索引都會儲存在預設位置,且效能特徵相似。

全域索引

全域索引是 Spanner 中的預設索引類型。索引資料會儲存在預設分區中,可能不會與資料表資料共置。在地理位置分區資料表上建立全域索引,可能會導致涉及索引資料欄的寫入作業延遲時間大幅增加,尤其是當索引的預設分區「寫入仲裁」,與要寫入的資料表資料列寫入仲裁相距甚遠時。您可以搭配使用附近的唯讀副本讀取租約區域或過時讀取,減輕全域索引的讀取延遲。

全域索引具有下列特性:

  • 如果沒有索引,查詢作業就必須掃描整個資料表,而索引可加快這類查詢的速度,並強制所有資料表資料列 (不論位置) 保持唯一性。
  • 適合用於資料庫中需要不重複值的資料欄。
  • 索引可加快查詢速度,篩選或排序索引資料欄。

以下是全域專屬索引的範例:

CREATE UNIQUE INDEX idx_customer_email ON customer(email);

本機索引

本機索引會交錯於索引資料表的父項階層中。主鍵資料欄名稱和類型必須與建立索引的資料表相符。

本機索引具有下列特性:

  • 索引資料會儲存在與索引資料相同的分割區中。寫入延遲時間取決於特定放置位置的寫入仲裁,而非預設放置位置的寫入仲裁。

  • 因為索引和資料表資料都位於相同位置,因此針對特定鍵前置字串的查詢延遲時間最短。

如要建立本機索引,請在父項資料表中交錯索引。如果您使用UNIQUE本機索引,系統只會在特定父項資料列中強制執行唯一性,不會在整個資料表中執行。

以下範例會在父項資料表 locations 中,為交錯的資料表 customer 建立本機索引:

GoogleSQL

-- Create locations placement table
CREATE TABLE locations (
  location STRING(MAX) NOT NULL PLACEMENT KEY,
) PRIMARY KEY(location);

-- Create customer table interleaved in the locations table
CREATE TABLE customer (
  location STRING(MAX) NOT NULL,
  customerId  INT64 NOT NULL,
  email STRING(MAX),
  webcookie STRING(64),
) PRIMARY KEY(location, customerId), INTERLEAVE IN PARENT locations;

-- Create a local index on the interleaved customer table
CREATE INDEX idx_customer_email_local ON customer(location, email),
INTERLEAVE IN locations;

PostgreSQL

-- Create locations placement table
CREATE TABLE locations (
  location varchar NOT NULL PLACEMENT KEY PRIMARY KEY
);

-- Create customer table interleaved in the locations table
CREATE TABLE customer (
  location varchar NOT NULL,
  customerId  BIGINT NOT NULL,
  email varchar(1024),
  webcookie varchar(64),
  PRIMARY KEY(location, customerId)
) INTERLEAVE IN PARENT locations;

-- Create a local index on the interleaved customer table
CREATE INDEX idx_customer_email_local ON customer(location, email)
INTERLEAVE IN locations;

在讀寫交易中查詢資料時,您必須在查詢中指定索引資料表的主鍵前置字元。否則查詢可能需要完整掃描資料表。例如:

GoogleSQL

-- The location (the index key prefix) must be specified
SELECT *
FROM customer
WHERE location= @location AND email= @email;

PostgreSQL

-- The location (the index key prefix) must be specified
SELECT *
FROM customer
WHERE location= @location AND email= @email;

如要瞭解不需要指定位置的最佳化作業,請參閱「使用全域不重複索引搭配本機或遠端索引」。

如果刊登位置資料表是以實體為基礎,且您想為特定實體或子實體內的資料建立索引,本機索引也很有用。例如:

GoogleSQL

-- Create entity based customer placement table
CREATE TABLE customer (
  customerId INT64 NOT NULL,
  email STRING(MAX),
  webcookie STRING(64),
  location STRING(MAX) NOT NULL PLACEMENT KEY
) PRIMARY KEY(customerId);

-- Create customerOrders child table
CREATE TABLE customerOrders (
  customerId INT64 NOT NULL,
  orderId INT64 NOT NULL,
  orderName STRING(MAX) NOT NULL
) PRIMARY KEY(customerId, orderId), INTERLEAVE IN PARENT customer;

-- Create a local index on the interleaved child table
CREATE INDEX idx_order_local ON customerOrders(customerId, orderName),
INTERLEAVE IN customer;

PostgreSQL

-- Create entity based customer placement table
CREATE TABLE customer (
  customerId BIGINT NOT NULL PRIMARY KEY,
  email varchar(1024),
  webcookie varchar(64),
  location varchar NOT NULL PLACEMENT KEY
);

-- Create customerOrders child table
CREATE TABLE customerOrders (
  customerId BIGINT NOT NULL,
  orderId BIGINT NOT NULL,
  orderName varchar(1024) NOT NULL,
  PRIMARY KEY(customerId, orderId)
) INTERLEAVE IN PARENT customer;

-- Create a local index on the interleaved child table
CREATE INDEX idx_order_local ON customerOrders(customerId, orderName)
INTERLEAVE IN customer;

遠端索引

遠端索引會將索引資料交錯儲存在資料表底下,但該資料表並非索引資料表交錯階層中的祖系。主鍵類型必須與對應索引資料欄的類型相符,但名稱可以不同。

使用地理位置分區時,您只能在自動管理的根放置資料表下交錯處理遠端索引。資料庫中的每個刊登位置都會對應到這個資料表的一列。

遠端索引具有下列特性:

  • 如果資料表的主鍵不是以刊登位置鍵做為前置字元,但您想根據刊登位置鍵,在地理位置上共置索引的分割區,這時就特別實用。
  • 這些索引只支援位置資料表中的索引資料欄,不支援交錯式子資料表中的任何資料欄。

如要搭配地理區域分區使用遠端索引,請在 ALTER DATABASE DDL 陳述式中設定 auto_managed_root_placement_table_name 選項,建立根存放位置資料表。

  1. 使用 ALTER DATABASE DDL 陳述式建立根刊登位置資料表。

    GoogleSQL

    ALTER DATABASE DATABASE_NAME SET OPTIONS
      (auto_managed_root_placement_table_name="TABLE_NAME");
    

    更改下列內容:

    • DATABASE_NAME:資料庫名稱。
    • TABLE_NAME:要建立的資料表名稱。建議使用 root_placement_table 這個名稱。

    舉例來說,下列指令會建立名為 root_placement_table 的資料表。

    ALTER DATABASE example_db SET OPTIONS
      (auto_managed_root_placement_table_name='root_placement_table');
    

    建立根位置資訊表後,Spanner 會建立內部資料表,並在您建立或捨棄位置資訊時,自動插入及刪除資料列。以下是 Spanner 建立的系統定義放置位置資料表範例,其中範例資料表名稱設為 root_placement_table。(請勿執行這個範例)。

    // Automatically generated after you run the previous example.
    // Don't put this in your schema explicitly.
    CREATE TABLE root_placement_table (
    location STRING(MAX) NOT NULL PLACEMENT KEY
    ) PRIMARY KEY(location);
    

    PostgreSQL

    ALTER DATABASE DATABASE_NAME SET
      spanner.auto_managed_root_placement_table_name='TABLE_NAME';
    

    更改下列內容:

    • DATABASE_NAME:資料庫名稱。
    • TABLE_NAME:要建立的資料表名稱。

    舉例來說,如要建立做為交錯根的 root_placement_table 資料表,請執行下列指令:

    ALTER DATABASE example_db SET
      spanner.auto_managed_root_placement_table_name='root_placement_table';
    

    建立根位置資訊表後,Spanner 會建立內部資料表,並在您建立或捨棄位置資訊時,自動插入及刪除資料列。以下是 Spanner 建立的系統定義放置位置資料表範例,其中範例資料表名稱設為 root_placement_table。(請勿執行這個範例)。

    // Automatically generated after you run the previous example.
    // Don't put this in your schema explicitly.
    CREATE TABLE root_placement_table (
      location varchar NOT NULL PLACEMENT KEY,
      PRIMARY KEY (location)
    );
    
  2. 在自動管理的root_placement_table資料表下,建立交錯的遠端索引。

    GoogleSQL

    -- Create a customer table with a primary key that is not the location
    CREATE TABLE customer (
      customerId INT64 NOT NULL ,
      email STRING(MAX),
      webcookie STRING(64),
      location STRING(MAX) NOT NULL PLACEMENT KEY,
    ) PRIMARY KEY(customerId);
    
    -- Create a remote index on the customer table
    CREATE INDEX idx_customer_email_remote ON customer(location, email),
    INTERLEAVE IN root_placement_table;
    

    PostgreSQL

    -- Create a customer table with a primary key that is not the location
    CREATE TABLE customer (
      customerId BIGINT NOT NULL PRIMARY KEY,
      email varchar(1024),
      webcookie varchar(64),
      location varchar NOT NULL PLACEMENT KEY
    );
    
    -- Creates a remote index on the customer table
    CREATE INDEX idx_customer_email_remote ON customer(location, email)
    INTERLEAVE IN root_placement_table;
    
  3. 在讀寫交易中查詢資料時,請在查詢述詞中指定索引的索引鍵前置字串,這樣就不需要完整掃描資料表。例如:

    GoogleSQL

    -- Specify the location (the index key prefix) in query
    SELECT *
    FROM customer
    WHERE location= @location AND email= @email;
    

    如要瞭解不需指定位置的最佳化方式,請參閱「具有本機或遠端索引的全域不重複索引」一節。

    PostgreSQL

    -- Specify the location (the index key prefix) in query
    SELECT *
    FROM customer
    WHERE location= @location AND email= @email;
    

    如要瞭解不需指定位置的最佳化作業,請參閱「具有本機或遠端索引的全域不重複索引」一節。

全域不重複索引的最佳化

使用全域不重複索引時,在下列使用情況中,Spanner 可能會透過啟發式最佳化,觸發查詢延遲改善:

  • 使用全域不重複索引搭配本機或遠端索引
  • 使用具有部分主鍵的全域不重複索引

以下各節說明 Spanner 在各個用途中可能採用的最佳化措施。

具有本機或遠端索引的全域專屬索引

為改善本機查詢延遲時間,當全域不重複索引與本機或遠端索引合併時,Spanner 可能會啟動以啟發式為基礎的最佳化程序。

這項最佳化功能會猜測放置位置與用戶端所在位置相同,並略過全域索引的預設分區,或免除篩選完整資料表掃描的必要性,藉此盡量縮短區域內查詢的延遲時間,即使未指定地理分區資料的位置也適用。如果用戶端主要存取儲存在自己區域內的資料,這種做法特別有益。

如果主要考量是區域內查詢延遲時間,且可容許寫入延遲時間稍微增加,則混合使用不同索引類型會很有幫助。即使您未在查詢中指定位置,結合不同索引類型也能提升區域內查詢的效能。

如要進行這項最佳化作業,您必須在同一資料欄上建立全域不重複索引,以及對應的本機或遠端索引。索引資料必須是全域唯一值。如果符合下列條件,Spanner 會對查詢套用這項最佳化措施:

  • 您不知道主鍵前置字串,且未指定資料位置。
  • 您的要求來自與資料放置位置的預設主要地區相同的地區,其中包含本機或遠端索引分片。

Spanner 會透過下列方式套用最佳化:

  • 如果系統觸發最佳化作業,且在本地位置找到資料列:由於有全域不重複索引,Spanner 無須搜尋其他位置。您的查詢有區域內延遲。
  • 如果初始位置搜尋未傳回資料列:這表示查詢並非區域內查詢。Spanner 會改用全域索引。

以下範例會建立全域不重複索引和本機索引:

GoogleSQL

CREATE UNIQUE INDEX idx_customer_email ON customer(email);
CREATE INDEX idx_customer_email_local ON customer(location, email), INTERLEAVE IN locations;

PostgreSQL

CREATE UNIQUE INDEX idx_customer_email ON customer(email);
CREATE INDEX idx_customer_email_local ON customer(location, email) INTERLEAVE IN locations;

下列範例會建立全域不重複索引和遠端索引:

GoogleSQL

CREATE UNIQUE INDEX idx_customer_email ON customer(email);
CREATE INDEX idx_customer_email_remote ON customer(location, email), INTERLEAVE IN root_placement_table;

PostgreSQL

CREATE UNIQUE INDEX idx_customer_email ON customer(email);
CREATE INDEX idx_customer_email_remote ON customer(location, email) INTERLEAVE IN root_placement_table;

根據先前的索引範例,下列查詢範例具有區域內延遲:

GoogleSQL

SELECT *
FROM customer
WHERE email= @email;

PostgreSQL

SELECT *
FROM customer
WHERE email= @email;

部分主鍵的全域不重複索引

在部分主鍵上使用全域不重複索引時,Spanner 可以套用類似於「使用全域不重複索引搭配本機或遠端索引」一文詳述的最佳化。

下列範例會在父項資料表 locations 中建立交錯的 customer,然後在 customerId 資料欄上建立全域唯一索引。

GoogleSQL

-- Create locations placement table
CREATE TABLE locations (
location STRING(MAX) NOT NULL PLACEMENT KEY,
) PRIMARY KEY(location);

-- Create customer table interleaved in the locations table
CREATE TABLE customer (
  location STRING(MAX) NOT NULL,
  customerId  INT64 NOT NULL,
  email STRING(MAX),
  webcookie STRING(64),
) PRIMARY KEY(location, customerId), INTERLEAVE IN PARENT locations;

-- Create global unique index on customerId column
CREATE UNIQUE INDEX idx_customer_customerid ON customer(customerId);

PostgreSQL

-- Create locations placement table
CREATE TABLE locations (
  location varchar NOT NULL PLACEMENT KEY PRIMARY KEY
);

-- Create customer table interleaved in the locations table
CREATE TABLE customer (
  location varchar NOT NULL,
  customerId  BIGINT NOT NULL,
  email varchar(1024),
  webcookie varchar(64),
  PRIMARY KEY(location, customerId)
) INTERLEAVE IN PARENT locations;

-- Create global unique index on customerId column
CREATE UNIQUE INDEX idx_customer_customerid ON customer(customerId);

最佳化適用於下列查詢:

GoogleSQL

SELECT * FROM customer WHERE customerId= @customerId;

PostgreSQL

SELECT * FROM customer WHERE customerId= @customerId;

如果您未建立全域專屬索引,這項查詢可能需要完整掃描資料表。如果未使用全域不重複索引,則需要在查詢中加入位置篩選器,才能獲得良好的查詢延遲時間:

GoogleSQL

SELECT * FROM customer WHERE location = @location AND customerId= @customerId;

PostgreSQL

SELECT * FROM customer WHERE location = @location AND customerId= @customerId;

選擇索引類型的一般指南,以獲得最佳延遲

您選擇的索引類型會直接影響查詢延遲時間。索引資料相對於資料表資料的位置,是地理分區工作負載效能的主要因素。

本節說明如何選擇全域、本機和遠端索引。

選擇全域索引的時機

如果工作負載可容許相關的讀取和寫入延遲,或您需要在索引資料欄上強制執行全域唯一性,請使用全域索引。

選擇全域索引時,請考量下列事項:

  • 用戶端與預設寫入仲裁領導者之間的距離,以及仲裁的預設延遲時間,會決定寫入延遲時間的增加幅度。這項影響僅限於涉及已建立索引資料欄的作業,例如插入資料列或更新已建立索引的資料欄。
  • 新增唯讀副本或使用讀取租約,可減輕讀取延遲時間增加的問題:
    • 在地理位置相近的區域新增唯讀副本,可縮短過時讀取作業的延遲時間。
    • 新增唯讀副本並使用讀取租約區域,可減少強烈讀取延遲。如果新增唯讀備用資源時未使用讀取租約區域,強讀取延遲時間不會縮短,但讀取總處理量可能會增加。
    • Spanner 一律會從領導者提供悲觀交易讀取作業。在預設位置新增副本,無法協助預設位置的資料進行悲觀交易讀取。
  • 全域索引 (包括鍵和儲存值) 會放在預設位置,但這個位置不會提供位置層級的資料駐留位置。詳情請參閱 Spanner 的資料落地總覽

選擇本機和遠端索引的時機

選擇本機和遠端索引時,請考量下列事項:

  • 本機和遠端索引可提供放置位置的讀寫效能,但會犧牲唯一索引資料欄的全域唯一性和排序屬性。而是提供索引資料欄的排序和唯一性,這些資料欄位於交錯的父項資料列中。
  • 使用本機或遠端索引時,您必須在查詢述詞中加入放置位置,除非也有全域不重複索引,可讓 Spanner 猜測本機放置位置。否則,查詢計畫和效能就不會是決定性因素。 根據查詢統計資料,Spanner 可能會執行基礎資料表掃描,或從各放置位置的索引分散及收集資料,進而增加延遲時間。

何時選擇具有本機或遠端索引的全域不重複索引

選擇全域不重複索引與本機或遠端索引的組合時,請考量下列事項:

  • 如果不知道特定刊登位置,請使用全域不重複索引與本機或遠端索引的組合。如果大多數查詢都來自與所要求資料位置相同的區域,這個方法就非常適合。
  • 寫入全域索引時,寫入延遲時間會受到額外的預設寫入仲裁延遲時間影響。
  • 透過以啟發式為基礎的最佳化,查詢會由本機索引分片提供服務,且大部分時間都會顯示區域內延遲。

針對特定結構定義設計選擇索引的詳細指南

最佳索引策略取決於資料表的主鍵結構和應用程式的查詢模式。本節將針對三種常見的結構定義設計,提供選取適當索引類型的指南:

  • 使用實體做為主鍵的結構定義
  • 使用位置資訊做為主鍵的結構定義
  • 使用位置相關值做為主鍵的結構定義

結構定義設計:以實體做為主鍵

如果結構定義使用實體做為主鍵,請根據查詢中是否指定位置,選擇索引策略。

如果實體 (例如 customerID) 是主鍵,而另一個非鍵資料欄 (例如 location) 是刊登位置鍵,請根據查詢模式決定刊登位置資料表的索引策略。(如果實體的插入延遲時間是您關心的問題,請勿使用實體做為刊登位置資料表的主鍵)。

如要為特定實體 (例如 customerID) 下的資料建立索引,請使用本機索引。資料會在實體內建立索引並排序,但不會跨實體建立索引。舉例來說,如果您想依日期為每位顧客的訂單建立索引,可以在 customerID 身分底下建立交錯的本機索引。

如果查詢中一律會提供位置資訊,請採用下列其中一種策略:

  • 如果查詢中一律會提供位置資訊,且不需要強制執行全域唯一性,請使用遠端索引。這些指標提供讀取和寫入作業的區域內延遲時間。

    遠端索引僅支援在放置資料表中建立索引資料欄,不支援在交錯式資料表中建立索引資料欄。遠端索引必須交錯於根放置資料表下。遠端索引會為刊登位置的所有刊登位置列建立索引。

如果查詢中不一定會提供位置資訊,請採用下列其中一種策略:

  • 如果索引資料欄是全域不重複,請建立全域不重複索引,強制執行該不重複性。

    如要實現低延遲的強讀取作業,請建立遠端索引,以及全域不重複索引。

    使用這種組合時,寫入作業可能會產生跨區域延遲,但指定位置的查詢 (使用 WHERE location= @location) 則可透過遠端索引,享有區域內延遲的優勢。如果查詢未指定位置,Spanner 會先在本地搜尋,然後根據啟發式方法進行最佳化。如果找不到資料,系統會改用全域索引。

    如果您使用讀取租約區域,且在與資料相同的區域中,有預設分割區的唯讀副本,則不需要遠端索引,因為讀取租約區域已為全域索引的讀取作業提供低延遲的強一致性讀取。

  • 如果查詢未指定位置,且編入索引的資料欄並非全域不重複,請只建立全域 (非不重複) 索引。在這種情況下,新增本機或遠端索引不會改善讀取延遲,因為即使 Spanner 在本機位置找到資料,也無法判斷其他位置是否有相符資料。

結構定義設計:將位置設為主鍵

如果 location 欄同時做為主鍵和位置鍵,系統會根據跨延遲問題,以及查詢是否一律指定位置,決定要選取哪個索引。

  • 如果不在意跨區域延遲時間,或需要全域唯一性,請使用全域索引。
  • 如果擔心跨區域延遲問題,且查詢一律會包含位置,而且您不需要 Spanner 強制執行全域唯一性,請只使用本機索引。確保讀取和寫入作業的本機延遲時間。
  • 如果擔心跨區域查詢延遲時間,但可接受跨區域寫入延遲時間,且位置不一定已知,則適用下列策略:

    條件 建議
    依全域專屬的部分主鍵查詢

    建立專屬的全域索引,強制執行唯一性。由於主鍵的功能類似,因此不需要本機索引。系統會套用以啟發式為基礎的最佳化功能。首先,Spanner 會檢查完整的主鍵和本機位置,然後再回溯至全域索引。

    如需範例,請參閱部分主鍵的全球唯一索引

    依非索引鍵全域專屬資料欄查詢

    建立專屬的全域索引,強制執行唯一性。

    對於區域內延遲,可能發生下列情況:

    • 在與全域索引相同的資料欄上建立本機索引。系統會套用啟發式最佳化。結合全域和本機索引,可為區域內強烈和過時的查詢提供低延遲時間,而寫入作業和跨區域查詢與寫入作業則有跨區域延遲時間。
    • 如果預設分區的讀寫或唯讀副本與資料位於相同區域,則:
      • 如果您只需要過時讀取的區域內延遲,但不需要強式讀取的區域內延遲,則不需要本機索引。本機副本可提供區域內延遲時間。
      • 如果需要區域內延遲時間,才能進行強讀取,您可以在與全域索引相同的資料欄上建立本機索引,或是使用讀取租約區域。讀取釋出期區域會以寫入延遲時間為代價,提供低強式讀取延遲時間。
    索引資料欄並非全域唯一 只建立全域索引。本機索引不會縮短讀取延遲時間,因為 Spanner 可能需要檢查所有位置。

如果這三種情況都不適用於您的用途,您可能必須犧牲應用程式的簡潔性或寫入延遲時間,才能持續提供位置資訊。

如果資料表的主鍵是以位置相關值為依據,但並非直接是刊登位置鍵欄 (例如,當刊登位置少於國家/地區時,使用 country 做為主鍵),您可以選擇使用全域索引或本機索引 (交錯於 country 欄下方)。不過,對於此類位置資訊資料表下交錯的任何資料表,系統都不支援遠端索引。

在這種情況下,本機索引不支援以啟發式為基礎的最佳化。因此,只有在查詢明確指定主鍵前置字元時,才能達到本機延遲。