建立資料結構以達成強式一致性

Cloud Datastore 能夠透過許多機器發佈資料,還能針對地理範圍廣泛的區域使用免主機的同步備用資源,因此能發揮高度的可用性、擴充性以及耐用性。不過,這種設計有得有失,缺點在於任何一個實體群組的寫入總處理量受限於大約每秒認可一次,如要跨多個實體群組進行查詢或交易,也會受到限制。本頁面詳細說明這些限制,同時以不犧牲應用程式的寫入總處理量需求為前提,探討建構資料以支援同步一致性的最佳做法。

一致性等級

Datastore 查詢能夠提供的結果分為兩種一致性等級:

  • 同步一致性查詢保證能得到最新結果,但可能需要較長的時間才能完成,有時候也可能不受系統支援。
  • 最終一致性查詢的執行速度通常較快,但偶爾可能會傳回過時的結果。

在最終一致性查詢中,用於收集結果的索引也是透過最終一致性的方式來存取。因此,這類查詢有時候會傳回不再符合查詢條件的實體,也有可能會略過符合查詢條件的實體。同步一致性查詢能達到交易一致性,也就是說,其結果是以單一項具有一致性的資料快照為準。

一致性保證

根據查詢的性質,查詢會傳回一致性保證等級不同的結果:

  • 根據預設,祖系查詢 (指針對實體群組執行的查詢) 會維持同步一致性,但可以藉由設定 Datastore 讀取政策的方式改為最終一致性 (如下所述)。
  • 全域查詢 (指不針對任何實體群組執行的查詢) 一律具有最終一致性。

許多應用程式在廣泛取得不相關資料時,可以接受使用最終一致性 (亦即跨多個實體群組的全域查詢,這類查詢有時會傳回稍微過時的資料),查看或編輯一組高度相關資料時再使用同步一致性 (祖系查詢,或者是單一實體查詢作業)。在此類應用程式中,將高度相關資料列為實體群組通常會有不錯的效果。實體群組數量越多,越有助於增加總處理量,而實體群組數量越少,則能在單一祖系查詢中讀取的實體量越多。應用程式取捨總處理量和一致性時應將這一點列入考量。

Datastore 讀取政策

為改善效能,您可以設定查詢的「讀取政策」,讓結果維持最終一致性 (您也可以透過 Datastore API 明確設定一組同步一致性政策,但這樣的設定不具實質效果,因為全域查詢一律具有最終一致性,與政策無關)。

您可以透過查詢物件的讀取選項啟用最終一致性讀取:

C#

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore C# API 參考說明文件

Query query = new Query("Task")
{
    Filter = Filter.HasAncestor(_db.CreateKeyFactory("TaskList")
        .CreateKey(keyName))
};
var results = _db.RunQuery(query,
    ReadOptions.Types.ReadConsistency.Eventual);

Go

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Go API 參考說明文件

ancestor := datastore.NameKey("TaskList", "default", nil)
query := datastore.NewQuery("Task").Ancestor(ancestor).EventualConsistency()

Java

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Java API 參考說明文件

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(PropertyFilter.hasAncestor(
        datastore.newKeyFactory().setKind("TaskList").newKey("default")))
    .build();
datastore.run(query, ReadOption.eventualConsistency());

Node.js

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件

const ancestorKey = datastore.key(['TaskList', 'default']);
const query = datastore.createQuery('Task').hasAncestor(ancestorKey);

query.run({consistency: 'eventual'});

PHP

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件

$query = $datastore->query()
    ->kind('Task')
    ->hasAncestor($datastore->key('TaskList', 'default'));
$result = $datastore->runQuery($query, ['readConsistency' => 'EVENTUAL']);

Python

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Python API 參考說明文件

# Read consistency cannot be specified in google-cloud-python.

Ruby

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件

ancestor_key = datastore.key "TaskList", "default"

query = datastore.query("Task")
                 .ancestor(ancestor_key)

tasks = datastore.run query, consistency: :eventual

交易和一致性考量

Datastore 認可通常具「交易」性質,亦即其結構定義是交易,交易的變動組合只有全部適用或全部不適用兩種情況;也有可能具「非交易」性質,亦即變動組合可能不見得會全部適用,也不見得全部不適用。

單項交易可能包含任何數目的建立、更新或刪除變動。為讓資料保持一致性,交易一定會使其中包含的變動整組適用於 Datastore,其中如有任何變動失敗,則整組變動均不適用。再者,同一次交易中執行的所有同步一致性讀取 (祖系查詢或 lookup 作業),全數依賴同一個具有一致性的資料快照。同步一致性查詢必須指定一個祖系篩選條件。參與交易的查詢一律具有同步一致性。交易最多可以包含 25 個實體群組。最終一致性讀取功能不具以上限制,適用於許多情況。您可以使用最終一致性讀取將資料發佈於大量實體群組,這樣就能同步針對不同的實體群組執行認可,以利提高寫入總處理量。不過,您必須瞭解最終一致性讀取的特性,才能判斷是否適用於您的應用程式:

  • 這類讀取功能的結果不見得能夠反映出最新的交易。之所以可能發生這種情況,原因是這類讀取功能不要求必須執行於最新的備用資源,而是會在執行查詢時使用備用資源中的任何一項可用資料。
  • 跨多個實體群組的已認可交易可能看似只適用於其中一部分實體。然而請注意,絕對不會發生在單一實體中只適用部分交易的情形。
  • 查詢結果可能會包含根據篩選條件不應包含的實體,也可能會排除應包含的實體。之所以可能發生這種情況,原因在於用於讀取索引的快照版本可能不同於用於讀取實體的快照版本。

建構資料的一致性

若要瞭解如何建構資料才能達到同步一致性,請比較簡易工作清單應用程式的兩種不同方法。第一種方法所建立的每一個實體均包含在該實體本身的新實體群組中 (亦即每個實體均為根實體):

C#

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore C# API 參考說明文件

Entity task = new Entity()
{
    Key = _db.CreateKeyFactory("Task").CreateKey("sampleTask"),
    ["category"] = "Personal",
    ["done"] = false,
    ["priority"] = 4,
    ["description"] = "Learn Cloud Datastore"
};

Go

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Go API 參考說明文件

type Task struct {
	Category        string
	Done            bool
	Priority        float64
	Description     string `datastore:",noindex"`
	PercentComplete float64
	Created         time.Time
}
task := &Task{
	Category:        "Personal",
	Done:            false,
	Priority:        4,
	Description:     "Learn Cloud Datastore",
	PercentComplete: 10.0,
	Created:         time.Now(),
}

Java

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Java API 參考說明文件

Entity task = Entity.newBuilder(taskKey)
    .set("category", "Personal")
    .set("done", false)
    .set("priority", 4)
    .set("description", "Learn Cloud Datastore")
    .build();

Node.js

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件

const task = {
  category: 'Personal',
  done: false,
  priority: 4,
  description: 'Learn Cloud Datastore',
};

PHP

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件

$task = $datastore->entity('Task', [
    'category' => 'Personal',
    'done' => false,
    'priority' => 4,
    'description' => 'Learn Cloud Datastore'
]);

Python

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Python API 參考說明文件

task = datastore.Entity(client.key('Task'))
task.update({
    'category': 'Personal',
    'done': False,
    'priority': 4,
    'description': 'Learn Cloud Datastore'
})

Ruby

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件

task = datastore.entity "Task" do |t|
  t["category"] = "Personal"
  t["done"] = false
  t["priority"] = 4
  t["description"] = "Learn Cloud Datastore"
end

接下來會根據 Task 實體種類查詢尚未完成且優先順序大於或等於 4 的工作,並按優先順序遞減排序:

C#

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore C# API 參考說明文件

Query query = new Query("Task")
{
    Filter = Filter.And(Filter.Equal("done", false),
        Filter.GreaterThanOrEqual("priority", 4)),
    Order = { { "priority", PropertyOrder.Types.Direction.Descending } }
};

Go

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Go API 參考說明文件

query := datastore.NewQuery("Task").
	Filter("Done =", false).
	Filter("Priority >=", 4).
	Order("-Priority")

Java

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Java API 參考說明文件

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(CompositeFilter.and(
        PropertyFilter.eq("done", false), PropertyFilter.ge("priority", 4)))
    .setOrderBy(OrderBy.desc("priority"))
    .build();

Node.js

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件

const query = datastore
  .createQuery('Task')
  .filter('done', '=', false)
  .filter('priority', '>=', 4)
  .order('priority', {
    descending: true,
  });

PHP

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件

$query = $datastore->query()
    ->kind('Task')
    ->filter('done', '=', false)
    ->filter('priority', '>=', 4)
    ->order('priority', Query::ORDER_DESCENDING);

Python

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Python API 參考說明文件

query = client.query(kind='Task')
query.add_filter('done', '=', False)
query.add_filter('priority', '>=', 4)
query.order = ['-priority']

Ruby

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件

query = datastore.query("Task")
                 .where("done", "=", false)
                 .where("priority", ">=", 4)
                 .order("priority", :desc)

不過,由於我們是使用最終一致性查詢 (非祖系查詢),查詢結果可能不會包含最新的實體。儘管如此,認可後不久,幾乎所有寫入項目均可用於最終一致性查詢。就許多應用程式而言,只要能在目前使用者自己做出的變更結構定義中提供最終一致性查詢結果,即使出現延遲現象,通常也在可以接受的範圍內。

要達到同步一致性,更好的方法是建立包含祖系路徑的實體。祖系路徑用於識別將已建立之實體分組的共用根實體。這個範例使用 TaskList 種類的祖系路徑,名稱是 default

C#

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore C# API 參考說明文件

Key taskListKey = _db.CreateKeyFactory("TaskList").CreateKey(TestUtil.RandomName());
Key taskKey = new KeyFactory(taskListKey, "Task").CreateKey("sampleTask");
Entity task = new Entity()
{
    Key = taskKey,
    ["category"] = "Personal",
    ["done"] = false,
    ["priority"] = 4,
    ["description"] = "Learn Cloud Datastore"
};

Go

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Go API 參考說明文件

parentKey := datastore.NameKey("TaskList", "default", nil)
key := datastore.IncompleteKey("Task", parentKey)

task := Task{
	Category:    "Personal",
	Done:        false,
	Priority:    4,
	Description: "Learn Cloud Datastore",
}

// A complete key is assigned to the entity when it is Put.
var err error
key, err = client.Put(ctx, key, &task)

Java

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Java API 參考說明文件

Key taskKey = datastore.newKeyFactory()
    .addAncestors(PathElement.of("TaskList", "default"))
    .setKind("Task")
    .newKey("sampleTask");
Entity task = Entity.newBuilder(taskKey)
    .set("category", "Personal")
    .set("done", false)
    .set("priority", 4)
    .set("description", "Learn Cloud Datastore")
    .build();

Node.js

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件

const task = {
  key: taskKey,
  data: {
    category: 'Personal',
    done: false,
    priority: 4,
    description: 'Learn Cloud Datastore',
  },
};

PHP

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件

$parentKey = $datastore->key('TaskList', 'default');
$key = $datastore->key('Task')->ancestorKey($parentKey);
$task = $datastore->entity(
    $key,
    [
        'Category' => 'Personal',
        'Done' => false,
        'Priority' => 4,
        'Description' => 'Learn Cloud Datastore'
    ]
);

Python

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Python API 參考說明文件

key_with_parent = client.key(
    'TaskList', 'default', 'Task', 'sample_task')

task = datastore.Entity(key=key_with_parent)

task.update({
    'category': 'Personal',
    'done': False,
    'priority': 4,
    'description': 'Learn Cloud Datastore'
})

Ruby

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件

task_key = datastore.key [["TaskList", "default"], ["Task", "sampleTask"]]

task = datastore.entity task_key do |t|
  t["category"] = "Personal"
  t["done"] = false
  t["priority"] = 4
  t["description"] = "Learn Cloud Datastore"
end

之後,您就能夠在共同根實體所識別到的實體群組中,執行同步一致性祖系查詢:

C#

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore C# API 參考說明文件

Query query = new Query("Task")
{
    Filter = Filter.HasAncestor(_db.CreateKeyFactory("TaskList")
        .CreateKey(keyName))
};

Go

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Go API 參考說明文件

ancestor := datastore.NameKey("TaskList", "default", nil)
query := datastore.NewQuery("Task").Ancestor(ancestor)

Java

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Java API 參考說明文件

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(PropertyFilter.hasAncestor(
        datastore.newKeyFactory().setKind("TaskList").newKey("default")))
    .build();

Node.js

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件

const ancestorKey = datastore.key(['TaskList', 'default']);

const query = datastore.createQuery('Task').hasAncestor(ancestorKey);

PHP

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件

$ancestorKey = $datastore->key('TaskList', 'default');
$query = $datastore->query()
    ->kind('Task')
    ->hasAncestor($ancestorKey);

Python

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Python API 參考說明文件

# Query filters are omitted in this example as any ancestor queries with a
# non-key filter require a composite index.
ancestor = client.key('TaskList', 'default')
query = client.query(kind='Task', ancestor=ancestor)

Ruby

如要瞭解如何安裝及使用 Cloud Datastore 所需的用戶端程式庫,請參閱 Cloud Datastore 用戶端程式庫一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件

ancestor_key = datastore.key "TaskList", "default"

query = datastore.query("Task")
                 .ancestor(ancestor_key)

這種方法依工作清單寫入單一實體群組來達到同步一致性,但同時也會將變更限制在每秒最多只能寫入 1 次 (實體群組的支援上限)。若應用程式可能會出現較頻繁的寫入使用量,可能需要考慮採用其他方法。舉例來說,若是讓使用者在公開留言板張貼訊息的留言板應用程式,您可以將近期貼文放入具有效期的 memcache,並混合顯示 memcache 和 Datastore 中的近期貼文,或是可以使用 Cookie 快取這些貼文,將部分狀態或其他資訊全部放入網址中。目標在於找出一個快取解決方案,以利在目前使用者於應用程式中貼文的期間內提供資料給該使用者。請記得,如果您在交易內執行 lookup、祖系查詢 (假設未將讀取政策設為最終一致性),或者是任何作業,一定會看到最近寫入的資料。

如需關於使用交易的其他範例,請參閱此處

交易的實體群組限制

將資料歸納為實體群組,即可限制執行的交易類型:

  • 一項交易所存取的所有資料,最多只能存在於 25 個實體群組中。
  • 如果您想在交易中使用查詢,必須將資料歸納成實體群組,才能指定使用能夠比對出正確資料的祖系篩選條件。
  • 單一實體群組的寫入總處理量大約是每秒一次交易。之所以會有這項限制,原因在於 Datastore 會針對橫跨大範圍地理區域的每個實體執行免主機的同步複製作業,以利發揮高度的可靠性和容錯能力。

如要進一步瞭解更新實體和索引的方式,請參閱交易隔離一文。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Cloud Datastore 說明文件