2012-02-22 11 views
8

Jestem nowicjuszem w bazach danych, ale mam problem, którego nie mogę wymyślić. Przepraszam z góry, jeśli to zbyt długo, staram się podsumować wszystkie moje wysiłki, aby dokładnie wiedzieć, co zrobiłem do tej pory. Moja aplikacja ma pewną logikę, a następnie wykonuje 3 zapytania do bazy danych. Pierwsze zapytanie sprawdza, czy istnieje wartość, drugie sprawdza, czy istnieje inna (powiązana) wartość, a trzecia, jeśli nie istnieje, dodaje powiązaną wartość. Pomyśl o tym, że robię zapytanie na numer 2, a jeśli istnieje, sprawdzam 3 i dodam go w razie potrzeby. Robię tę pętlę wiele razy (patrzę na ogólne zapytania, ale podejrzewam, że ten program jest bardziej ciężki niż zapis). Kiedyś używałem tylko hashtable w moim programie, ale gdy dodałem wiele procedur, które miałem problemy z synchronizacją, zdecydowałem się użyć bazy danych, aby wiele rdzeni pracowało nad tym w tym samym czasie.Jak mogę zwiększyć liczbę zapytań do odczytu/sekundę w mojej bazie danych?

Najpierw spróbowałem, mysql i użyłem silnika pamięci (może to wszystko zmieścić się w pamięci), wykonałem złożony klucz podstawowy, aby zreplikować słownik, który miałem w swoim programie, zindeksowałem go, zablokowałem blokadę, ale mogłem tylko dostać około 11 000 zapytań/sekundę od tego.

Potem wypróbowałem redis (usłyszałem, że to było jak memcache) i utworzyłem ten sam klucz/wartość, którą miałem wcześniej (tutaj jest aktualny tryb Can I make two columns unique to each other? or use composite primary key's in redis?) i usunąłem wszystkie rzeczy fsync, więc mam nadzieję, że nigdy nie uderzę w dysk twardy, ale ja wciąż dostaje około 30 000 zapytań/sekundę. Spojrzałem na ulepszenia systemu (używam linuxa), uruchamiając program w ramdrive itp., Ale nadal podobny wynik.

Mam skrypt instalacyjny i próbowałem zrobić to na ec2 przy użyciu instancji wysokiego cpu, ale wynik jest podobny (zapytania nie idą w górę dla obu rozwiązań). Jestem w pewnym sensie na końcu rozumu, ale nie chcę się poddawać, ponieważ czytam ludzi na stackoverflow, mówiących o tym, jak dostali 100 000 000 zapytań na wolnostojące. Czuję, że mój datamodel jest bardzo prosty (dwie kolumny INT lub I mogą uczynić go jednym ciągiem z obydwoma połączonymi INT, ale to nie wydawało się spowalniać ani w dół) i po utworzeniu danych (i zapytaniu przez inny proces) mam nie ma potrzeby utrzymywania (dlatego też staram się nie pisać na dysk twardy). Jakiej konfiguracji brakuje, aby programiści mogli uzyskać tego rodzaju wydajność? Czy jest wymagana specjalna konfiguracja poza tworzeniem tabeli? czy jest to jedyny sposób na uzyskanie tego rodzaju wydajności przez rozproszone bazy danych? Wiem, że problem jest związany z bazą danych, ponieważ kiedy zamykam proces pośredni w bazie danych, moja aplikacja pytona trafia w 100% na każdy rdzeń jej działania (chociaż nic nie pisze), to sprawia, że ​​myślę, że proces oczekiwania (dla odczytów, Podejrzewam, że to jest to, co spowalnia (mam dużo wolnego procesora/pamięci, więc zastanawiam się, dlaczego to nie max'ing, mam 50% procesora i 80% mojej wolnej pamięci podczas tych prac, więc nie mam pojęcia co się dzieje).

Mam mysql, redis i hbase. Mam nadzieję, że jest coś, co mogę zrobić, aby jedno z tych rozwiązań działało tak szybko, jak bym chciał, ale jeśli nie jest, to nie ma problemu z jakimkolwiek rozwiązaniem (jest to po prostu tymczasowy termostat, który może rozpytywać rozproszone procenty).

Co mogę zrobić?

Dzięki!

Aktualizacja: zgodnie z wnioskiem w komentarzach, oto niektóre kodu (po specyficznej logiki aplikacji, która wydaje się, że będzie w porządku):

cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s - c * x, i)) 
    if cursor.rowcount == 1: 
     cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s, i+1)) 
     if cursor.rowcount == 0: 
      cursor.execute (""" INSERT INTO data_table (key1, value1) VALUES ('%s', '%s')""" % (s, i+1)) 
      conn.commit() #this maybe not needed 
      #print 'commited ', c 

powyżej jest kod z 3 wyszukiwań na mysql.Próbowałem też zrobić jeden wielki odnośnika (ale to było w rzeczywistości wolniejsze):

 cursor.execute (""" 
INSERT INTO data_table (key1, value1) 
    SELECT '%s', '%s' 
    FROM dual 
    WHERE (SELECT COUNT(*) FROM data_table WHERE key1='%s' AND value1='%s') 
     = 1 
    AND NOT EXISTS 
     (SELECT * FROM data_table WHERE key1='%s' AND value1='%s') 
      """ % ((s), (i+1), (s - c * x), (i), (s), (i+1))) 

oto projekt stół na MySQL:

cursor.execute ("DROP TABLE IF EXISTS data_table") 
cursor.execute (""" 
    CREATE TABLE data_table(
     key1 INT SIGNED NOT NULL, 
     value1 INT SIGNED NOT NULL, 
     PRIMARY KEY (key1,value1) 
    ) ENGINE=MEMORY 
""") 
cursor.execute("CREATE INDEX ValueIndex ON data_table (key1, value1)") 

na Redis, jego simlair do struktury 3 zapytania (od to był najszybszy mogę dostać na mysql, z wyjątkiem nie trzeba zrobić odnośnika jeśli wartość nie istnieje, po prostu zastąpić go zapisać kwerendę):

if r_server.sismember(s - c * x, i): 
    r_server.sadd(s, i + 1) 

Moja struktura danych dla REDiS jest połączony pytanie (w zasadzie jego al ist, 3 => 1 2 3 zamiast mysql mającego 3 wiersze do ponownego wysłania 3 = 1, 3 = 2, 3 = 3.

Nadzieję, że pomaga, wszelkie inne pytania proszę dać mi znać.

+0

Być może uda się to osiągnąć za pomocą jednego zoptymalizowanego zapytania, co pozwoli zaoszczędzić na wycieczkach do bazy danych. Potrzebujemy więcej informacji, takich jak zapytania, schemat i logika. –

+0

@MarcusAdams Dzięki za komentarz. Zaktualizowałem to pytanie za pomocą mojego kodu mysql i redis. – Lostsoul

Odpowiedz

5

Patrząc na dostarczone fragmenty kodu, powiedziałbym, że główne wąskie gardło tutaj to sieć lub pętle TCP loopback. Zarówno MySQL, jak i Redis są synchronicznymi magazynami klient/serwer. Za każdym razem, gdy wyślesz zapytanie i poczekasz na odpowiedź, zapłacisz za planowanie jądra, opóźnienie sieci, współczynnik uderzeń pamięci podręcznej CPU, itp.

Ludzi, którzy wykonują setki tysięcy zapytań na sekundę na TCP serwery nie używają pojedynczego gniazda do kierowania serwera, ale wiele połączeń dla równoległości po stronie klienta i/lub pipeline their queries w celu ograniczenia wpływu tego opóźnienia.

W rzeczywistości, jeśli masz unikatowe gniazdo i przesyłasz zapytanie w kolejności bez żadnego potokowania, nie mierzysz maksymalnej przepustowości, jaką możesz osiągnąć na serwerze, ale raczej opóźnienia sieci lub IPC.

Mamy nadzieję, że protokoły używane przez większość serwerów NoSQL zazwyczaj obsługują potoki. Oto kilka porad dotyczących implementacji Redis.

Najpierw możesz przeczytać the Redis benchmark page. Omówiono wszystkie typowe wąskie gardła występujące podczas testowania wydajności Redis.

Oto kilka rad, aby osiągnąć maksymalną przepustowość dla odniesienia:

  • użyć wydajny język (Python, Ruby, JavaScript jest sposób wolniejsze niż C)
  • rurociągów Twoje zapytania o ile to możliwe
  • Jeśli klient i serwer znajdują się w tym samym polu, użyj gniazd domeny unix zamiast pętli zwrotnej TCP.
  • Optymalizacja na poziomie systemu i sieci dopiero po oprogramowanie zostało zoptymalizowane (konfiguracja NUMA, NIC, itp ...)

mam uruchomić prosty test przy użyciu hiredis (C Redis Client) do symulacji korzystania obudowa na Xeonie [email protected] Kod można znaleźć here.

if r_server.sismember(s - c * x, i): 
    r_server.sadd(s, i + 1) 

Program implementuje podobny kod, potokując zapytania. Partia elementów i wysyła kilka rozkazów sismember, aby wiedzieć, czy elementy istnieją, a następnie kilka sadd poleceń dla elementów, które ma dodać.

Wyniki:

  • bez przetwarzania potokowego, z pętli zwrotnej TCP => 66268 Q/S
  • bez potoku z gniazd domeny Unix => 89485 Q/S
  • z przetwarzaniem potokowym i pętli zwrotnej TCP => 273757 Q/s
  • z pipelining i UNIX domeny gniazd => 278254 Q/s

Tak więc wpływ używania gniazd domeny unix jest wysoki, gdy obie trasy nie są zoptymalizowane i staje się bardzo niski po użyciu potokowania. Większość zysków wynika z potokowania rurociągów. Dlatego powinieneś najpierw skupić się na optymalizacji oprogramowania/protokołu.

Wyniki można jeszcze poprawić, modyfikując konfigurację systemu/sieci, ale następnym krokiem do uzyskania większej przepustowości jest zwykle uruchomienie kilku instancji Redis i odszyfrowanie danych za pomocą mechanizmu mieszającego (próbującego zrównoleglić po stronie serwera) .

+0

Wow Didier, dziękuję bardzo. Twoja odpowiedź dała mi wiele wglądów i nie mogę się doczekać, aby sprawdzić twój kod. Twoje wyniki są na tyle imponujące, że mogę uświadomić sobie siłę potokowania. Zrobiłem wcześniej badania i pomyślałem, że redis nie może działać w rozproszonym środowisku, a zatem nie może się oderwać od innych serwerów redis. Jeśli to możliwe, co powinienem badać, żeby go znaleźć? – Lostsoul

+0

Zauważyłem, że zrobiłeś to na xeonie ... czy istnieje sposób, aby redis używał więcej niż jednego rdzenia (na mojej instalacji wydaje się, że używa się tylko jednego)? Twój kod może się po prostu rozwidlać, więc to sprawdzę. – Lostsoul

+0

Z pewnością możliwe jest użycie wielu instancji Redis w środowisku rozproszonym - należy zaprojektować strategię odłamywania i wdrożyć ją po stronie klienta. Dobry przykład można znaleźć na stronie http://blog.zawodny.com/2011/02/26/redis-sharding-at-craigslist/ –

Powiązane problemy