2010-10-04 17 views
6

Próbuję Java aplikacji Google App Engine, jednak brak unikalnego ograniczenia utrudnia pewne sprawy. Byłem through this post i this blog sugeruje metodę realizacji czegoś podobnego. Moje tło jest w MySQL. Przechodzenie do magazynu danych bez unikalnego ograniczenia sprawia, że ​​jestem roztrzęsiony, ponieważ nigdy nie musiałem się martwić o zduplikowane wartości przed i sprawdzanie każdej wartości przed wstawieniem nowej wartości wciąż ma miejsce na błąd.Wymuszanie wyjątkowego ograniczenia w GAE

"Nie, nadal nie można określić unikalnego podczas tworzenia schematu."

- David Underhill opowiada o GAE i unikalność (post link)

Co wy z wykorzystaniem zaimplementować coś podobnego do unikalnego lub klucza podstawowego?

Słyszałem o abstrakcyjnej warstwy magazynu danych utworzonej przy użyciu niskiego poziomu API, który działał jak zwykły RDB, który jednak nie był wolny (ale nie pamiętam nazwy programu)

Schematyczny widok mój problem

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 

jednak na linii numer 3 (kontrolnego), czuję się dwóch użytkowników może dodać taką samą Sno. I właśnie to uniemożliwia mi pracę z appengine.

Odpowiedz

4

Można generować unikalne numery seryjne produktów bez konieczności wymuszania unikalnych identyfikatorów lub sprawdzania całego zestawu podmiotów, aby dowiedzieć się, jaki jest obecnie największy numer seryjny. Możesz użyć transakcji i pojedynczego elementu, aby wygenerować "następny" numer seryjny. Ponieważ operacja odbywa się wewnątrz transakcji, możesz mieć pewność, że żadne dwa produkty nigdy nie uzyskają tego samego numeru seryjnego.

Takie podejście będzie jednak potencjalnym rozwiązaniem chokepoint wydajności i ograniczy skalowalność aplikacji. Jeśli jest tak, że tworzenie nowych numerów seryjnych nie zdarza się tak często, że można się spierać, może to zadziałać.

EDYTOWANIE: W celu wyjaśnienia, singleton zawierający bieżący - lub następny - numer seryjny, który ma zostać przypisany, jest całkowicie niezależny od wszelkich podmiotów, które faktycznie mają przypisane numery seryjne. Nie muszą wszyscy być częścią grupy podmiotów. Można mieć jednostki z wielu modeli używających tego samego mechanizmu, aby uzyskać nowy, unikalny numer seryjny.

Nie pamiętam Java na tyle dobrze, aby zapewnić przykładowy kod, a za moim przykładem Python może być bez znaczenia dla Ciebie, ale tutaj jest pseudo-kod do zilustrowania pomysł:

  1. Otrzymuj żądanie utworzenia nowej inwentaryzacji pozycja.
  2. Wprowadź transakcję.
  3. Odzyskaj bieżącą wartość pojedynczej encji modelu SerialNumber.
  4. Wartość przyrostu i zapisz ją w bazie danych
  5. Zwróć wartość przy wychodzeniu z transakcji.

Kod, który wykonuje całą pracę polegającą na tworzeniu elementu magazynu i zapisaniu go wraz z nowym numerem seryjnym NIE BĘDZIE musiał być uruchamiany w transakcji.

Zastrzeżenie: jak wskazałem powyżej, może to być poważne wąskie gardło, ponieważ tylko jeden numer seryjny może być utworzony w dowolnym momencie. Zapewnia jednak pewność, że wygenerowany właśnie numer seryjny jest unikalny i nie jest w użyciu.

+0

Czy transakcja na jednostce pojedynczej nie wymaga, aby singleton i wszystkie powiązane podmioty należały do ​​tej samej grupy encji? –

+0

@Jason, myślę, że Singleton można izolować we własnej grupie. Podmioty, które otrzymują numery seryjne, nie są z nim powiązane. Jedyne, co musi się zdarzyć w przypadku izolacji transakcji, to zwiększenie numeru seryjnego. Chętnie zakodowałbym przykład, ale programuję AppEngine z Pythonem. Nie dotykałem Javy od 1999 roku. –

+0

@Jason Hall @Adam Crossland Muszę Ci powiedzieć, że wiele z tego, co powiedziałeś, "przeleciało" ponad moją głową. Nie mam doświadczenia z "transakcjami", jestem nowy w JAVA, ale rozumiem trochę pseudokodu, który jest podobny do tego, co obecnie robię. Będzie działać w scenariuszu o małym natężeniu ruchu, ale chcę być względnie pewny również w złych scenariuszach. Dzięki za doskonały napis! – abel

12

To i inne podobne pytania pojawiają się często, gdy mówimy o przechodzeniu z tradycyjnego RDB do baz danych typu BigTable podobnego do App Engine.

Często przydaje się dyskusja na temat: dlaczego magazyn danych nie obsługuje kluczy unikatowych, ponieważ informuje o sposobie myślenia, w jaki należy się zastanowić przy planowaniu pamięci masowej. Powodem, dla którego unikalne ograniczenia nie są dostępne, jest to, że znacznie ogranicza skalowalność. Tak jak powiedziałeś, egzekwowanie tego ograniczenia oznacza sprawdzanie wszystkich innych obiektów dla tej właściwości. Niezależnie od tego, czy robisz to ręcznie w kodzie, czy w magazynie danych, robi to automatycznie za kulisami, to nadal musi się zdarzyć, a to oznacza niższą wydajność. Można dokonać pewnych optymalizacji, ale nadal musi to nastąpić w taki czy inny sposób.

Odpowiedź na pytanie brzmi, naprawdę pomyśl, dlaczego potrzebujesz tego wyjątkowego ograniczenia.

Po drugie, pamiętaj, że klucze do istnieją w magazynie danych i są doskonałym sposobem na egzekwowanie prostego unikalnego ograniczenia.

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

To gwarantuje, że nie MyUser nigdy nie będzie tworzony z tej wiadomości nigdy, a można także szybko odzyskać MyUser z tej wiadomości:

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

w środowisku wykonawczym Pythona można też zrobić :

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

Która spowoduje wstawienie lub pobranie użytkownika z tym adresem e-mail.

Wszystko, co jest bardziej złożone, nie będzie jednak skalowalne.Zastanów się więc, czy ta własność ma być globalnie unikalna, czy też istnieją sposoby, aby usunąć potrzebę tego unikalnego ograniczenia. Często zdarza się, że z niewielkimi obejściami nie potrzebujesz, aby ta właściwość była wyjątkowa.

+0

Dzięki za szczegółowe zapis. Częściowo rozumiem, dlaczego datastore GAE nie ma cech zwykłego RDB. Aplikacja, nad którą pracuję, wymaga obecnie przyrostowych numerów seryjnych do przypisania do wpisów. Obecnie sprawdzam db dla ostatniego numeru wpisu (sNo), dodam 1 (sNo ++) do niego, a następnie wstawiam nowy wpis z nowym sNo jako wartością, tak aby inni pracujący w systemie, nie otrzymywali duplikatów sNo do pracy z. Tak się nie stało, ale obawiam się, że w czasach ciężkiej pracy (gdy 100-120 pracowników dodaje wpisy), może powstać duplikat. Aktualizowanie pytania na przykładzie. – abel

+2

+1. Doskonała odpowiedź! –

+1

@abel W idealnej sytuacji system nie wymagałby monotonicznego zwiększania numerów seryjnych i można przypisać losowy klucz do każdej pozycji. Jeśli ma to na celu interakcję z innym systemem, może to nie być łatwe. Możesz spróbować użyć transakcji dla atomowości i memcache dla wydajności. –

3

Napotkałem ten sam problem w aplikacji, w której użytkownicy musieli rezerwować szczelinę czasową. Musiałem "wstawić" dokładnie jedną unikalną jednostkę czasu oczekiwania, oczekując jednocześnie, że użytkownicy zażądają tej samej szczeliny czasowej.

Wyodrębniłem przykład tego, jak to zrobić na silniku aplikacji, i I blogged about it. Wpis na blogu zawiera przykład kodu kanonicznego przy użyciu Datastore, a także Objectify. (BTW, radziłbym unikać JDO.)

Mam również wdrożony live demonstration, w którym można przejść dwóch użytkowników do rezerwowania tego samego zasobu. W tej wersji demonstracyjnej możesz zobaczyć dokładne zachowanie magazynu danych w wyszukiwarkach za pomocą kliknięcia.

Jeśli szukasz zachowania unikalnego ograniczenia, powinny one okazać się przydatne.

-broc

0

I pierwsza myśl alternatywę dla techniki transakcji w blogu BROC jest, może być do klasy singleton, który zawiera zsynchronizowany sposób (słownie nazwa addUserName (String)) odpowiedzialny dodając nową pozycję tylko wtedy, gdy jest wyjątkowy lub rzuca wyjątek. Następnie utwórz kontekstlistener, który tworzy instancję pojedynczego wystąpienia tego singletonu, dodając go jako atrybut do servletContext. Serwlety mogą następnie wywołać metodę addUserName() na instancji singleton, którą uzyskują poprzez getServletContext.

Nie jest to jednak dobry pomysł, ponieważ GAE prawdopodobnie podzieli aplikację na wiele maszyn JVM, więc wiele instancji klasy singleton może nadal występować, po jednym w każdej maszynie JVM. see this thread

Bardziej podobną do GAE alternatywą byłoby napisanie modułu GAE odpowiedzialnego za sprawdzanie unikalności i dodawanie nowych enterów; następnie użyć ręcznego lub podstawowe skalowanie z ...

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

Wtedy masz jedno wystąpienie uruchomionego na GAE, który działa jako pojedynczy punkt władzy, dodawanie użytkowników po jednym na raz do magazynu danych. Jeśli obawiasz się, że ta instancja jest wąskim gardłem, możesz ulepszyć moduł, dodając kolejkowanie lub wewnętrzną architekturę master/slave.

To rozwiązanie modułowe pozwoliłoby na dodanie wielu unikalnych nazw użytkowników do magazynu danych w krótkim czasie, bez ryzyka problemów z rywalizacją grup podmiotów.