1

我想從Google數據存儲中加載大量數據。在Google數據存儲中使用遊標進行多線程處理

所以,第1步:我運行查詢(使用keysOnly = true)並遍歷遊標,以便每個指向一個包含600個對象的頁面的起始位置。我將遊標存儲在局部變量中。

第2步:我爲每個遊標分離一個線程,在每個線程中加載和處理600個對象。

這不是通常使用遊標的方式。

但是,它看起來對我來說是正確的。步驟1和步驟2中的實際查詢字符串是相同的。這類似於通常的無狀態web用例,用戶可能會要求Next,Back,然後重新載入前一頁;不需要直接從前一個光標查詢的結果中獲得遊標。

我不想按順序遍歷遊標,然後爲了並行處理在給定的遊標查詢中加載的對象而拆分線程,因爲我想從數據庫並行實現IO密集型查詢。

我得到了一些不一致的結果,似乎涉及錯過的頁面和重複加載的對象。這是多線程加載Google Datastore大量數據的正確方法嗎?或者,如果不是,那是什麼?

回答

0

Ed Davisson,Google工程師,負責Google數據存儲客戶端API,answered this。他提供了問題的根源和推薦的解決方案。

他說:

「查詢返回的遊標僅適用於在同一查詢中使用當您從純鍵查詢切換[在我的第1步,JF]對非關鍵 - 僅查詢[在我的步驟2中,JF],遊標不再適用....

「如果您的目標是將結果集拆分爲類似大小的塊,則可能需要查看QuerySplitter [現在版本爲1beta3,JF]「

2

我會推薦一種不同的方法。只運行一個遍歷所有實體的查詢。它發生得非常快(不要忘記將批量大小設置爲500,默認值僅爲10)。如果查詢很大,您仍然可能需要使用遊標。

對於每個實體使用任務API創建任務並將其添加到任務隊列中。這些任務可以並行執行。您可以設置隊列中的所有參數。

使用這種方法,您不必擔心線程,您可以將任務設置爲在失敗時自動重試等。我發現它是App Engine非常重要的部分 - 只寫您自己的邏輯,並讓App Engine擔心執行部分。

+0

謝謝,這可能會奏效,但慢速部分是實際查詢,而不是實體後處理。 ,但瓶頸是IO。 –

+1

你只需要一個鍵來創建一個任務,然後在一個任務中獲取一個實體。使用按鍵查詢,它會快得多。 –

+0

謝謝。你是否說我應該按鍵查詢順序遍歷所有對象(必要時使用遊標)?然後,並行執行獲取操作(使用任務)?這確實意味着每個實體打兩次DB,對吧? –

1

取決於你在做什麼,你有幾種選擇:

  1. 如果你有大量的數據 - 使用「扇出」模式與任務隊列。在此模型中,任務隊列作業加載一段數據,處理它並存儲結果,並可能觸發更多處理作業。任務隊列調節允許您控制吞吐量/持續時間/成本並處理失敗/重試。此模型的一個優點是您可以通過手動戳URL來測試和重新運行細分,並在管理面板中查看進度。

  2. 使用GAE的MapReduce - https://cloud.google.com/appengine/docs/java/dataprocessing/

  3. 在一個單一的過程中,如果你有一個小的數據量。缺點是請求截止日期(60s,10m或24小時 - 取決於服務器和請求的類型)。回想一下,數據存儲操作是異步的,因此您可以在單個線程中並行運行請求,這可能會簡化您的代碼。 (我相信)在你的appengine-web.xml或app.yaml中由max-concurrent-requests控制。如果您的請求可能失敗並且不能構建爲可恢復,則這可能非常昂貴。

+0

謝謝。我只是將很多對象加載到內存中,每個對象都進行最少的後處理。有很多選項可以平行加載,但真正的問題是:我該如何做光標?我嘗試遍歷所有對象以收集Cursor字符串(它劃分了1000個對象的「頁面」),然後將線程分佈到線程中,爲每個線程加載一個Cursor值對象(或者我可以使用Tasks,MapReduce,異步數據庫調用等)。但是這種設計需要兩次迭代,並且在任何情況下都會導致結果出現奇怪的不一致。 –

+0

您還沒有告訴我們您要如何處理數據,因此向您提供具體幫助非常具有挑戰性。如果您試圖使用最終一致的查詢(遊標或其他方式)獲取數據存儲的完整快照,則會出現諸如「錯過」或重複對象等症狀,除非數據非常靜態。遊標在一般意義上工作(儘管不一定在開發服務器上) – Nick

+0

是的,我試圖用最終一致的查詢將給定表的完整快照加載到內存中。有成千上萬的對象,所以我一次使用800個對象的遊標。爲什麼會導致錯過或重複的對象?我知道,如果在收集遊標後添加或刪除對象,我可能看不到這一點,但不應該一切都好嗎?在發生這種情況時,我的數據庫實際上是靜態的:我使用這種方法甚至在只讀模式下遇到了丟失和重複的對象(收集遊標併爲每個線程加載頁面的對象值)。 –