2013-07-23 35 views
0

我使用NDB作爲我的應用程序,並使用iter()與限制和開始遊標來遍歷任務中的20,000個查詢結果。很多時候我遇到了超時錯誤。哪個NDB查詢函數更有效地遍歷一大組查詢結果?

超時:數據存儲操作超時或數據暫時不可用。

我打這個電話是這樣的方式:

results = query.iter(limit=20000, start_cursor=cursor, produce_cursors=True) 
for item in results: 
    process(item) 
save_cursor_for_next_time(results.cursor_after().urlsafe()) 

我可以減少限制,但我想只要10分鐘一個任務可以運行。 10分鐘應該超過20000次結果。事實上,一個好的運行,任務可以在大約一分鐘內完成。

如果我切換到fetch()或fetch_page(),他們會更有效率,不太可能遇到超時錯誤?我懷疑iter()中有很多開銷會導致超時錯誤。

謝謝。

回答

0

還有其他的事情需要考慮像上下文緩存ndb,你需要禁用它。但我也使用iter方法。我還用舊的db製作了一個ndb版本的mapper api。

這是我應該解決超時問題和NDB緩存,輕鬆地創建這種東西NDB映射器API: http://blog.altlimit.com/2013/05/simple-mapper-class-for-ndb-on-app.html

這個映射器API,您可以創建它喜歡或者你可以改善這一點。

class NameYourJob(Mapper): 

    def init(self): 
     self.KIND = YourItemModel 
     self.FILTERS = [YourItemModel.send_email == True] 

    def map(self, item): 
     # here is your process(item) 
     # process here 
     item.send_email = False 
     self.update(item) 

# Then run it like this 
from google.appengine.ext import deferred 
deferred.defer(NameYourJob().run, 50, # <-- this is your batch 
    _target='backend_name_if_you_want', _name='a_name_to_avoid_dups') 
+0

我不太明白爲什麼需要在映射器中禁用上下文緩存?這真的有什麼幫助? – Sam

+2

這已經有一段時間了,但如果我沒有記錯的話,我做了它,因此它在創建實體時不會創建上下文緩存,並且可能會在運行的實例上創建不必要的開銷。 – Faisal

1

獲取是不是真的任何更有效,他們都使用相同的機制,除非你知道你有多少實體希望前期 - 然後取作爲你最終只是一個來回可以更加高效。

您可以增加iter的批量大小,這可以改善事情。請參閱https://developers.google.com/appengine/docs/python/ndb/queryclass#kwdargs_options

從文檔的缺省批量大小是20,這意味着20,000個實體會有很多批次。

其他的事情可以幫助。考慮在處理上使用map和or map_async,而不是明確調用進程(實體)讀取https://developers.google.com/appengine/docs/python/ndb/queries#map也可以在處理中引入異步可以提高併發性。

說了這麼多,你應該配置文件,這樣你就可以知道在哪裏使用時間。例如,由於您在那裏做的事情,延遲可能在您的process中。

0

對於可能長的查詢迭代,我們使用時間檢查來確保可以處理緩慢的處理。鑑於GAE基礎設施性能的差異,您可能永遠找不到最佳的處理數量。下面的代碼摘錄來自我們使用的在線維護處理程序,它通常在十秒內運行。如果沒有,我們會得到一個返回代碼,說我們需要重新運行計時器檢查。在你的情況下,你可能會在將光標傳遞給下一個隊列任務之後中斷進程。下面是一些示例代碼,希望能夠給你一個關於我們的邏輯的好主意。還有一點需要注意的是:你可以選擇把它分解成更小的叮咬,然後通過重新排列任務直到完成,扇出更小的任務。一次做20K的事情在GAE變化多端的環境中似乎非常積極。HTH -stevep

def over_dt_limit(start, milliseconds): 
    dt = datetime.datetime.now() - start 
    mt = float(dt.seconds * 1000) + (float(dt.microseconds)/float(1000)) 
    if mt > float(milliseconds): 
     return True 
    return False 


    #set a start time 
    start = datetime.datetime.now() 
    # handle a timeout issue inside your query iteration 
    for item in query.iter(): 
     # do your loop logic 
     if over_dt_limit(start, 9000): 
      # your specific time-out logic here 
      break