我的 Google App Engine 應用程式(Python3,標準環境)處理來自用戶的請求:如果資料庫中沒有想要的記錄,則創建它。
這是關于資料庫覆寫的問題:
當一個用戶(通過瀏覽器)向資料庫發送請求時,正在運行的 GAE 實體可能會暫時無法回應該請求,然后它會創建一個新行程來回應該請求。這導致兩個實體回應相同的請求。兩個實體幾乎同時查詢資料庫,每個實體都發現沒有想要的記錄,因此創建了一條新記錄。結果是兩個重復的記錄。
另一個風景是,由于某種原因,用戶的瀏覽器發送了兩次時間差小于0.01秒的請求,由服務器端的兩個實體處理,從而創建了重復記錄。
我想知道如何通過一個實體臨時鎖定資料庫以防止資料庫被另一個實體覆寫。
我考慮過以下方案,但不知道它是否有效。
對于python 2,Google App Engine 提供了“memcache”,可以用來標記查詢的狀態,以達到資料庫鎖定的目的。但是對于python3,似乎必須設定一個Redis服務器才能在不同實體之間快速交換資料庫狀態。那么,使用 Redis 進行資料庫鎖定的效率如何呢?
Flask 的 session 模塊的使用。session 模塊可用于在不同的請求之間共享資料(在大多數情況下,用戶的登錄狀態),從而在不同的實體之間共享。我想知道在不同實體之間交換資料的速度。
附加資訊 (1)
我按照建議使用事務,但它不起作用。下面是我用來驗證交易的代碼。
失敗的原因可能是事務僅適用于當前客戶端。同時對于多個請求,GAE的服務器端會創建不同的行程或實體來回應請求,每個行程或實體都會有自己獨立的客戶端。
@staticmethod
def get_test(test_key_id, unique_user_id, course_key_id, make_new=False):
client = ndb.Client()
with client.context():
from google.cloud import datastore
from datetime import datetime
client2 = datastore.Client()
print("transaction started at: ", datetime.utcnow())
with client2.transaction():
print("query started at: ", datetime.utcnow())
my_test = MyTest.query(MyTest.test_key_id==test_key_id, MyTest.unique_user_id==unique_user_id).get()
import time
time.sleep(5)
if make_new and not my_test:
print("data to create started at: ", datetime.utcnow())
my_test = MyTest(test_key_id=test_key_id, unique_user_id=unique_user_id, course_key_id=course_key_id, status="")
my_test.put()
print("data to created at: ", datetime.utcnow())
print("transaction ended at: ", datetime.utcnow())
return my_test
附加資訊 (2)
Here is new information about usage of memcache (Python 3) I have tried the follow code to lock the database by using memcache, but it still failed to avoid overwriting.
@user_student.route("/run_test/<test_key_id>/<user_key_id>/")
def run_test(test_key_id, user_key_id=0):
from google.appengine.api import memcache
import time
cache_key_id = test_key_id "_" user_key_id
print("cache_key_id", cache_key_id)
counter = 0
client = memcache.Client()
while True: # Retry loop
result = client.gets(cache_key_id)
if result is None or result == "":
client.cas(cache_key_id, "LOCKED")
print("memcache added new value: counter = ", counter)
break
time.sleep(0.01)
counter =1
if counter>500:
print("failed after 500 tries.")
break
my_test = MyTest.get_test(int(test_key_id), current_user.unique_user_id, current_user.course_key_id, make_new=True)
client.cas(cache_key_id, "")
memcache.delete(cache_key_id)
uj5u.com熱心網友回復:
如果問題是重復而不是覆寫,也許你應該在創建新條目時指定資料 id,但不要讓 GAE 為你生成一個隨機的。然后應用程式將兩次寫入同一個條目,而不是創建兩個條目。資料 ID 可以是任何唯一的,例如會話 ID、時間戳等。
事務的問題是,它會阻止您并行修改同一個條目,但不會阻止您并行創建兩個新條目。
uj5u.com熱心網友回復:
我以以下方式使用 memcache(使用 get/set )并成功鎖定了資料庫寫入。
似乎gets/cas 不能很好地作業。在測驗中,我通過 cas() 設定了閥門,但后來它無法通過 gets() 讀取值。
記憶體快取 API:https ://cloud.google.com/appengine/docs/standard/python3/reference/services/bundled/google/appengine/api/memcache
@user_student.route("/run_test/<test_key_id>/<user_key_id>/")
def run_test(test_key_id, user_key_id=0):
from google.appengine.api import memcache
import time
cache_key_id = test_key_id "_" user_key_id
print("cache_key_id", cache_key_id)
counter = 0
client = memcache.Client()
while True: # Retry loop
result = client.get(cache_key_id)
if result is None or result == "":
client.set(cache_key_id, "LOCKED")
print("memcache added new value: counter = ", counter)
break
time.sleep(0.01)
counter =1
if counter>500:
return "failed after 500 tries of memcache checking."
my_test = MyTest.get_test(int(test_key_id), current_user.unique_user_id, current_user.course_key_id, make_new=True)
client.delete(cache_key_id)
...
uj5u.com熱心網友回復:
交易: https ://developers.google.com/appengine/docs/python/datastore/transactions
當兩個或多個事務同時嘗試修改一個或多個公共物體組中的物體時,只有第一個提交其更改的事務才能成功;所有其他人都將在提交時失敗。
您應該在事務中更新您的值。只要您的讀取和寫入在單個事務中,App Engine 的事務就會防止兩個更新相互覆寫。請務必注意有關物體組的討論。
你有兩個選擇:
- 實作自己的事務失敗邏輯(重試多少次等)
- 與其直接寫入資料存盤區,不如創建一個任務來修改物體。在任務中運行事務。如果失敗,App Engine 將重試此任務,直到成功。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/433879.html
標籤:database google-app-engine locking memcached overwrite
