2010-10-04 147 views
6

我嘗試使用Google App Engine Java,但是沒有唯一的約束會使事情變得困難。 我一直在through this postthis blog建議一種方法來實現類似的東西。我的背景是在MySQL中。移動到沒有唯一約束的數據存儲讓我感到緊張,因爲我從來不必擔心之前重複的值,並在插入新值之前檢查每個值仍然存在錯誤空間。在GAE中實施唯一約束

「不,在模式創建過程中,您仍然無法指定唯一的 。」

- David Underhill關於GAE和獨特的約束(post link

什麼是使用來實現類似的唯一或主鍵東西你們會談?

我聽說使用它的工作就像一個普通RDB,然而這是不是免費的(但我不記得軟件的名稱)

的示意圖低級API創建一個抽象的數據存儲層我的問題

sNo = biggest serial_number in the db 
sNo++ 
Insert new entry with sNo as serial_number value //checkpoint 
User adds data pertaining to current serial_number 
Update entry with data where serial_number is sNo 

然而,在3號線(檢測點),我覺得兩個用戶可以添加相同斯諾。這就是阻止我使用appengine的原因。

回答

4

您可以爲您的產品生成唯一的序列號,而無需強制使用唯一的ID或查詢整個實體集以找出當前最大的序列號。您可以使用交易和單一實體來生成「下一個」序列號。由於操作發生在交易中,因此您可以確定沒有兩個產品會獲得相同的序列號。

但是,這種方法將成爲潛在的性能阻塞點並限制了您的應用程序的可擴展性。如果創建新的序列號不是經常發生以至於爭用,那麼它可能適用於您。爲了說明,持有當前序列號或下一個序列號的單件將完全獨立於任何實際上有序號分配給它們的實體。他們不需要全部成爲實體組的一部分。您可以使用同一機制從多個模型中獲取新的唯一序列號。

我不記得Java的不夠好,提供的示例代碼,和我的Python的例子可能是毫無意義的給你,但這裏是僞代碼來說明這個想法:

  1. 接收請求創建一個新的庫存項目。
  2. 輸入交易。
  3. 檢索SerialNumber模型的單個實體的當前值。
  4. 遞增值並將其寫入數據庫
  5. 退出事務時的返回值。

現在,完成實際創建庫存項目並將其與新序列號一起存儲的所有工作的代碼不需要在事務中運行。警告:如上所述,這可能是一個主要的性能瓶頸,因爲在任何時候只能創建一個序列號。但是,它確實爲您提供了確定性,即您剛剛生成的序列號是唯一的,並且不在使用中。

+0

單身實體上的事務不需要單身人士和所有相關實體在同一實體組中嗎? – 2010-10-04 15:17:17

+0

@Jason,我認爲Singleton可以在它自己的組中被隔離。接收序列號的實體與它無關。在交易隔離中唯一需要發生的是序列號的遞增。我很樂意編寫一個示例,但我使用Python編寫AppEngine。自1999年以來,並沒有觸及過Java。 – 2010-10-04 15:21:19

+0

@Jason Hall @Adam Crossland我必須告訴你,你所說的大部分內容都是'woosh'。我沒有'交易'經驗,對於JAVA來說是新手,但我確實瞭解了一些僞代碼,這與我目前所做的類似。它將在低流量的情況下工作,但我想在惡劣的情況下也能相對確定。感謝您的優秀寫作! – abel 2010-10-04 16:08:59

12

當談到從傳統的RDB轉換到類似BigTable的數據存儲(如App Engine)時,經常會遇到這個問題和其他類似的問題。

討論爲什麼爲什麼不支持唯一密鑰,因爲它通知您在思考數據存儲方案時應該使用的心態。唯一約束不可用的原因是因爲它極大地限制了可伸縮性。就像你所說的那樣,強制約束意味着檢查該屬性的所有其他實體。無論您是在代碼中手動執行還是數據存儲在後臺自動執行它,它仍然需要發生,這意味着性能較低。可以進行一些優化,但仍然需要以某種方式發生。

你的問題的答案是,真的想想你爲什麼需要這個獨特的約束。

其次,請記住,數據存儲區中存在密鑰,並且這是執行簡單唯一約束的好方法。

my_user = MyUser(key_name=users.get_current_user().email()) 
my_user.put() 

這將保證不MyUser將永遠與電子郵件創建以後再,你也可以快速檢索MyUser與電子郵件:

my_user = MyUser.get(users.get_current_user().email()) 

在Python運行時,你也可以做:

my_user = MyUser.get_or_create(key_name=users.get_current_user().email()) 

這將插入或檢索用戶與該電子郵件。

雖然比這更復雜,但不會擴展。所以真的想想你是否需要這個屬性是全球唯一的,或者有什麼方法可以消除對這個唯一約束的需要。通常情況下,你會發現一些小的解決方法,你不需要該屬性是唯一的。

+0

感謝您的詳細描述。我部分理解GAE數據存儲爲什麼不具有常規RDB的功能。我當前正在使用的應用程序需要將增量序列號分配給條目。目前,我檢查數據庫的最後一個條目號(sNo),添加1(sNo ++),然後插入一個新的sNo作爲值的條目,以便其他人在系統上工作,不會得到重複sNo的工作用。這沒有發生,但我擔心在繁重的工作中(當100-120名員工增加分錄時),可能會出現重複的sNo。用一個例子更新問題。 – abel 2010-10-04 14:00:57

+2

+1。優秀的答案! – 2010-10-04 14:05:12

+1

@abel理想情況下,您的系統不需要單調遞增的序列號,您可以爲每個項目分配一個隨機密鑰。如果這是與另一個系統交互,但這可能不容易實現。您可以嘗試使用針對原子性和memcache的事務來獲得性能。 – 2010-10-04 15:15:31

3

我在用戶需要預訂時間段的應用程序中遇到了同樣的問題。我需要「插入」一個唯一的時隙實體,同時期望用戶同時請求相同的時隙。

我已經隔離了一個如何在應用程序引擎上執行此操作的示例,以及我blogged about it。該博文中有使用Datastore的典型代碼示例,還有Objectify。 (順便說一下,我建議避免JDO。)

我還部署了一個live demonstration,您可以提前兩個用戶預留相同的資源。在此演示中,您可以體驗應用程序引擎數據存儲點擊的確切行爲。

如果您正在尋找唯一約束的行爲,這些應該證明是有用的。

-broc

0

我首先想到的布羅克的博客的交易技術的替代,可以使一個單類,它包含一個同步的方法添加新的條目只有當(比方說addUserName(字符串名稱))負責它是唯一的或拋出異常。然後創建一個contextlistener來實例化這個單例的單個實例,並將其作爲一個屬性添加到servletContext中。然後,Servlet可以在通過getServletContext獲取的單例實例上調用addUserName()方法。

但是這不是一個好主意,因爲GAE很可能將應用程序分割到多個JVM中,因此多個單例類實例仍然可能發生,每個JVM中都有一個。 see this thread

更類似替代GAE會寫一個GAE模塊負責檢查唯一性和添加新的輸入;然後用與...

<max-instances>1</max-instances> 

然後你有一個實例上運行GAE充當權威的一個點,在時間到數據存儲添加用戶一個手動或基本縮放。如果您擔心此實例是瓶頸,您可以改進模塊,添加排隊或內部主/從架構。

這個基於模塊的解決方案將允許在短時間內將許多獨特的用戶名添加到數據存儲中,而不會冒實體組爭用問題的風險。