深入玩 GAE 一定要知道的資料庫底層細節

















開始用 GAE 做大型服務以來, 多數環節都很方便
但一直被 db 的寫入問題困擾著(有些情境下需要頻繁的更新/讀出)
今天看到這篇文章的某一小節(如上), 解決了我這個疑問

原來ndb (猜想 db 也是) 的寫入分三階段:

1. Commit: 在此階段, Datastore 的底層機制先將這次要做的變更紀錄下來
2. Invalidate Cache: 使受到改變的資料庫 item(entity) 的快取失效
3. Apply: 將變更實質反映到Datastore 上, 我覺得原文寫得很好 It makes the change visible to global queries and eventually-consistent reads. --> 意即這個階段去 query 才會是新(對)的值, 而且還有強調這個動作可能是數秒後才會執行

這篇文章也有說, 當執行 ndb 寫入動作時(例如呼叫 put() 這個 Datastore API), 實際上是在 2. 就返回(指呼叫API 的返回), 令新的值真正生效的3. 的執行則是非同步的(asynchronously)


基本概念懂了, 接下來講錯誤處理, 條列式講:

A. 若在 1.階段發生錯誤, 那麼系統會自動重試, 若錯誤持續發生, 你的程式會得到例外狀況(Exception)

B. 若1. 成功, 但3. 失敗, 那麼系統會在以下時機解決(正常 Apply 更新):
  a. 系統週期性自動掃描未完成的寫入作業, 再次試圖 Apply 他們
  b. 原文是這麼寫: The next write, transaction, or strongly-consistent read in the impacted entity group causes the not-yet-applied changes to be applied before the read, write, or transaction
  我這麼翻譯: 當被更新的 item 其隸屬的 entity group(有點類似資料庫 item 的樹狀結構, 藉由指標把 item 集結成一個有 root 的 tree) 要被寫入, 執行交易運算  或 高度一致性讀取  時, 會迫使這個 group 裡面還沒被 Apply 的變更被 Apply(在進行這些操作前 Apply 必會先被完成)

以上的事實會影響到在你的 GAE app 更新的資料何時會"可見"(當執行資料庫查詢時看得到更新), 官方宣稱, 在 ndb API 返回的數百毫秒內, 資料應該還沒有真實 Apply, 所以此時查到的可能還是只有局部更新的資料


就我個人以前的開發經驗, 上述官方文件印證了我觀察到的現象:
1. 有時候從紙筆推算, 這個運算後應該要更新的值, 卻怎麼也無法在下一列(或下幾列) 程式要取得時正確得到(相隔時間太短可能還沒 apply )

2. 常常我什麼也沒做, 只是加了個 logging 動作就好了, 有了這樣背景知識後就合理了, 因為可能 logging 動作執行起來要花比較久, 所以這段期間內更新已經 apply 完成


值得研究的讀取模式(Ancestor Query), 可能可以避免讀取到不一致資料的情形: https://developers.google.com/appengine/docs/python/datastore/queries#Python_Ancestor_queries

出處: https://developers.google.com/appengine/docs/python/ndb/?hl=zh-tw#writes
注意這篇是講 ndb, 查證過後, db 的機制略有不同, 如下:


但經過研讀後, 發現大概念是相同的, 基於下面這段話:
The write operation returns immediately after the Commit phase and the Apply phase then takes place asynchronously

意即當commit 階段完成, db寫入的操作就會 return 了(繼續回到原程式的中斷點繼續執行下去), apply 階段則是非同步的發生, 所以如果沒有特殊機制保護的話, 這時試圖存取該 entity 有可能會看到過時的資料

留言

熱門文章