SORU
16 Temmuz 2012, PAZARTESİ


En iyi ndb varlıkların sorgu çok sayıda uygulama deposuna

App Engine veri deposuna ilginç bir sınırı içine çalıştırmak. Bize üretim sunucuları üzerinde bazı kullanım verileri analiz yardımcı olmak için bir işleyici yaratıyorum. Ve 10.000 varlıklar deposundan çekti sorgu özetlemek istiyorum analizleri yapmak için. Hesaplaması zor değil, sadece kullanım örnekleri belirli bir geçiş filtresi öğeleri bir çubuk grafik. Vurdum sorun veri deposuna geri sorgu son çıkmadan önce herhangi bir işlem yapmak için yeterince hızlı alamıyorum.

Aklıma gelen her şeyi performansını artırmak için paralel RPC çağrıları sorgu kısmını denedim, ama appstats göre sorgular aslında paralel bir şekilde yürütmek için elde edemiyorum. Ne yaparsam yapayım (aşağıya bakınız) her zaman RPC döndü sıralı bir sonraki sorgular bir şelale düşmek gibi görünüyor.

Sorgu ve analiz kod çalışır, sadece yavaş yavaş veri deposundan hızlı doyamıyorum çünkü çalışır. not:

Arka plan

Paylaşabilirim canlı versiyonu var bilmiyorum ama burada bahsettiğim sistem için temel model:

class Session(ndb.Model):
   """ A tracked user session. (customer account (company), version, OS, etc) """
   data = ndb.JsonProperty(required = False, indexed = False)

class Sample(ndb.Model):
   name      = ndb.StringProperty  (required = True,  indexed = True)
   session   = ndb.KeyProperty     (required = True,  kind = Session)
   timestamp = ndb.DateTimeProperty(required = True,  indexed = True)
   tags      = ndb.StringProperty  (repeated = True,  indexed = True)

Bir kullanıcı adı bir yeteneği kullanır zamanlar gibi örnekler düşünebilirsiniz. (örn: 'systemA.') feature_x. Etiketler müşteri bilgileri, sistem bilgileri ve özelliğine dayanmaktadır. örn: ['', '2.5.1', '', '', ''] premium_account). feature_x systemA Vista Etiketleri ilgi çekici örnekler bulmak için kullanılabilecek belirteçleri normal dışı bir seti oluşturuyor.

Analiz yapmaya çalışıyorum oluşmaktadır alarak bir tarih aralığı ve soran kaç kez oldu bir özelliği set özellikleri (belki de tüm özellikler) kullanılan günlük (veya saatlik) başına müşteri hesabı (şirket, kullanıcı başına).

Yani işleyicisi girdi bir şeyler olacak gibi

  • Başlangıç Tarihi
  • Bitiş Tarihi
  • Etiket(ler)

Çıkış şöyle olacaktır:

[{
   'company_account': <string>,
   'counts': [
      {'timeperiod': <iso8601 date>, 'count': <int>}, ...
   ]
 }, ...
]

Sorgular için ortak Kodu

İşte tüm sorgular için ortak bir kod. İşleyici genel yapısı sorgu parametreleri ayarlar, sorgu çalıştırır, sonuçları, süreçleri, geri dönmek için veri oluşturur olsun işleyicisi kullanarak basit bir webapp2.

# -- Build Query Object --- #
query_opts = {}
query_opts['batch_size'] = 500   # Bring in large groups of entities

q = Sample.query()
q = q.order(Sample.timestamp)

# Tags
tag_args = [(Sample.tags == t) for t in tags]
q = q.filter(ndb.query.AND(*tag_args))

def handle_sample(sample):
   session_obj = sample.session.get()    # Usually found in local or memcache thanks to ndb
   count_key   = session_obj.data['customer']
   addCountForPeriod(count_key, sample.timestamp)

Yöntemleri Denedim

Yöntemleri veri deposuna veri mümkün olduğunca hızlı bir şekilde çekmeyi denemek ve paralel olarak çeşitli denedim. Şimdiye kadar çalıştık yöntemleri vardır:

A. Yineleme, Tek

Bu diğer yöntemlere karşı karşılaştırmak için basit temel bir olgu. Ben sorgu oluşturmak ve tüm öğeleri ndb sizi çekmek için yaptığı şeyi yapmasına izin üzerinde yineleme birbiri ardına.

q = q.filter(Sample.timestamp >= start_time)
q = q.filter(Sample.timestamp <= end_time)
q_iter = q.iter(**query_opts)

for sample in q_iter:
   handle_sample(sample)

B. Büyük Getir

Buradaki fikir, eğer bir tek çok büyük getir yapabilir miyim diye görmek için.

q = q.filter(Sample.timestamp >= start_time)
q = q.filter(Sample.timestamp <= end_time)
samples = q.fetch(20000, **query_opts)

for sample in samples:
   handle_sample(sample)

C. zaman Uyumsuz zaman aralığında getirir

Burada fikir olduğunu tanıması örneklerindendir oldukça iyi aralıklı karşısında zaman ben oluşturmak bir dizi bağımsız sorgular bölünmüş toplam süreyi bölgeye parçalar ve denemek için her çalıştırdığınızda bu paralel kullanarak zaman uyumsuz:

# split up timestamp space into 20 equal parts and async query each of them
ts_delta       = (end_time - start_time) / 20
cur_start_time = start_time
q_futures = []

for x in range(ts_intervals):
   cur_end_time = (cur_start_time   ts_delta)
   if x == (ts_intervals-1):    # Last one has to cover full range
      cur_end_time = end_time

   f = q.filter(Sample.timestamp >= cur_start_time,
                Sample.timestamp < cur_end_time).fetch_async(limit=None, **query_opts)
   q_futures.append(f)
   cur_start_time = cur_end_time

# Now loop through and collect results
for f in q_futures:
   samples = f.get_result()
   for sample in samples:
      handle_sample(sample)

D. eşleme Uyumsuz

Belgelere ndb Sorgu kullanırken paralellik otomatik olarak yararlanabilir gibi ses yaptı diye bu yöntemi denedim.map_async yöntemi.

q = q.filter(Sample.timestamp >= start_time)
q = q.filter(Sample.timestamp <= end_time)

@ndb.tasklet
def process_sample(sample):
   period_ts   = getPeriodTimestamp(sample.timestamp)
   session_obj = yield sample.session.get_async()    # Lookup the session object from cache
   count_key   = session_obj.data['customer']
   addCountForPeriod(count_key, sample.timestamp)
   raise ndb.Return(None)

q_future = q.map_async(process_sample, **query_opts)
res = q_future.get_result()

Sonuç

Bir örnek genel tepki süresi toplamak için sorgu test ettim ve izleri appstats. Sonuçlar:

A. Yineleme, Tek

gerçek: 15.645

Bu sırayla toplu birbiri ardına alınıyor geçer ve sonra memcache her oturum alır.

Method A appstats

B. Büyük Getir

gerçek: s 12.12

Etkili aynı seçenek ama biraz daha hızlı nedense.

Method B appstats

C. zaman Uyumsuz zaman aralığında getirir

gerçek: 15.251

Başlangıçta daha fazla paralellik sağlamak gibi görünüyor ama sonuçları yineleme sırasında gelecek çağrıları bir dizi yavaşladı gibi görünüyor. Ayrıca oturum bekleyen sorunları ile memcache aramaları örtüşme mümkün görünmüyor.

Method C appstats

D. eşleme Uyumsuz

gerçek: 13.752

Bu anlamak benim için zor. Q örtüşen iyi bir anlaşma gibi görünüyor, ama her şey paralel bir şelale yerine germek gibi görünüyor.

Method D appstats

Öneriler

Tüm bu dayanarak, neyi kaçırıyorum? Sadece App Engine üzerinde bir sınırı isabet yoksa daha iyi bir şekilde paralel olarak işletmelerin çok sayıda çekmek için değil mi?

Bir sonraki denemeye ne diyeceğimi bilemiyorum. İstemci, ama bu oldukça kaba görünüyor paralel olarak motor uygulaması için birden fazla istekte yeniden kuvvet düşündüm. Gerçekten bu app engine kaçırdığım bir şey mi var diye tahmin ediyorum bu kullanma durumu ele almak gerekir beklerdim.

Güncelleme

Bu seçenek C buldum sonunda benim hikayemde için en iyisi. Bu 6.1 saniye içinde tamamlamak için optimize edebildim. Hala mükemmel değil, ama çok daha iyi.

Birkaç kişi tavsiye aldıktan sonra, aşağıdaki öğeleri anlamak ve akılda tutmak için anahtar olduğunu buldum:

  • Birden fazla sorgu paralel olarak çalışabilir
  • Sadece 10 RPC uçuş aynı anda olabilir
  • İkincil sorguları vardır hiçbir konu için denormalize etmeye çalışın
  • Bu tür bir görev ve görev sıraları, gerçek zamanlı değil sorgular göster azaltmak için daha iyi kalır

Daha hızlı yapmak için yaptım ne:

  • Başlangıçta süre dayanan, sorgu yerim bölümlenmiş. daha eşit bölümleri varlıklar dönen, daha iyi) açısından (not:
  • Veriler ikincil oturum için gerek sorgu çıkarmam normal dışı
  • Ndb zaman uyumsuz işlemler ve wait_any kullanımı yapılmış() işleme sorgular örtüşüyor

Hala ya da beklediğiniz performansı alıyorum, ama şimdilik işe yarar. Ben sadece onların hızlı işleyicileri belleğe sıralı işletmelerin çok sayıda çekmek için daha iyi bir yolu olsaydı.

CEVAP
17 Temmuz 2012, Salı


Böyle büyük işleme 60 bir zaman sınırı olan bir kullanıcı isteği, yapılamaz. Bunun yerine, uzun süren istekleri destekleyen bir çerçevede yapılmalıdır. task queue istekleri 10 dakika, ve (sanırım) bellek koltuk (F1 örnekleri, varsayılan, 128MB of memory) normale destekler. Daha yüksek limitler için (istek, 1GB bellek, zaman aşımı), backends kullanın.

Denemek için bir şey daha: erişirken, görev sırası görev patlar bir URL ayarlayın. Gerçek/ile görev sırası görev tamamlandı false henüz yanıt ~5s başka her URL anketler bir web sayfası döndürür. Görev sırası bazıları 10 saniye sürebilir, veri, süreçler, ya da hesaplanmış bir veri olarak ya da işlenen bir web sayfası veri deposuna sonucu kaydeder. İlk sayfa tamamlandığını tespit ettiğinde, kullanıcı deposundan şimdi hesaplanan sonuçlar getirir sayfaya yönlendirilir.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Boiler Room

    Boiler Room

    10 Mayıs 2012
  • Caramella Girls

    Caramella Gi

    19 Mayıs 2008
  • Menglong Tav

    Menglong Tav

    18 Temmuz 2010