2010-02-19 23 views
16

वर्तमान में अपने आवेदन इस तरह मेम्कैश में मॉडल कैश:AppEngine मॉडल Memcaching करने का सबसे अच्छा तरीका क्या है?

memcache.set("somekey", aModel) 

लेकिन http://blog.notdot.net/2009/9/Efficient-model-memcaching पर Nicks 'पोस्ट पता चलता है कि पहले protobuffers करने के लिए इसे परिवर्तित करने में बहुत अधिक कुशल है। लेकिन कुछ परीक्षण चलाने के बाद मुझे पता चला कि यह आकार में वास्तव में छोटा है, लेकिन वास्तव में धीमी (~ 10%)।

क्या दूसरों के पास एक ही अनुभव है या क्या मैं कुछ गलत कर रहा हूं?

टेस्ट परिणाम: http://1.latest.sofatest.appspot.com/?times=1000

import pickle 
import time 
import uuid 

from google.appengine.ext import webapp 
from google.appengine.ext import db 
from google.appengine.ext.webapp import util 
from google.appengine.datastore import entity_pb 
from google.appengine.api import memcache 

class Person(db.Model): 
name = db.StringProperty() 

times = 10000 

class MainHandler(webapp.RequestHandler): 

def get(self): 

    self.response.headers['Content-Type'] = 'text/plain' 

    m = Person(name='Koen Bok') 

    t1 = time.time() 

    for i in xrange(int(self.request.get('times', 1))): 
    key = uuid.uuid4().hex 
    memcache.set(key, m) 
    r = memcache.get(key) 

    self.response.out.write('Pickle took: %.2f' % (time.time() - t1)) 


    t1 = time.time() 

    for i in xrange(int(self.request.get('times', 1))): 
    key = uuid.uuid4().hex 
    memcache.set(key, db.model_to_protobuf(m).Encode()) 
    r = db.model_from_protobuf(entity_pb.EntityProto(memcache.get(key))) 


    self.response.out.write('Proto took: %.2f' % (time.time() - t1)) 


def main(): 
application = webapp.WSGIApplication([('/', MainHandler)], debug=True) 
util.run_wsgi_app(application) 


if __name__ == '__main__': 
main() 
+0

मैंने अभी भी इसे वास्तव में बड़े और जटिल मॉडल के साथ भी आजमाया, लेकिन परिणाम इसके बारे में था। –

+0

शायद GAE पर http://docs.python.org/library/timeit.html है? यह अधिक सटीक परिणाम दिखाना चाहिए, लेकिन फिर भी - आपके द्वारा लिंक किए गए ब्लॉग एंट्री को पढ़ने के बाद, मैं प्रोटोबफर्स ​​और अचार के प्रदर्शन के बीच परिमाण अंतर का ऑर्डर करने की अपेक्षा करता हूं - और इसे समय-समय पर कैच किया जाना चाहिए। ( –

+0

मैं जावा एपेंगेन का उपयोग करके, इसलिए मैं इस सिद्धांत का परीक्षण करने के लिए बहुत आलसी हूं - क्या अचार (कहीं) दृश्यों के पीछे परिणामों को कैशिंग कर रहा है, जबकि to_protobuf नहीं है? लेख के आधार पर, मुझे यकीन नहीं है कि मैं गति में परिमाण में वृद्धि के पूर्ण क्रम की अपेक्षा करता हूं, क्योंकि प्रोटेलफ संस्करण का उपयोग करके भी अचार को अभी भी बुलाया जा रहा है। इस्तेमाल की जाने वाली जगह निश्चित रूप से काफी छोटी हो सकती है। –

उत्तर

4

Memcache कॉल अभी भी साथ या Protobuf का उपयोग किए बिना वस्तु अचार। अचार, तेजी से एक Protobuf वस्तु के बाद से यह एक बहुत ही सरल मॉडल

सादा अचार वस्तुओं Protobuf + अचार वस्तुओं से बड़े होते हैं के साथ है इसलिए वे Memcache में समय की बचत, लेकिन वहाँ Protobuf रूपांतरण

करने में अधिक प्रोसेसर समय है

इसलिए आम तौर पर विधि के बारे में कोई भी तरीका काम करता है ... लेकिन

प्रोटोबफ का उपयोग करने का कारण यह है कि यह मॉडल के संस्करणों के बीच परिवर्तनों को संभाल सकता है, जबकि पिकल त्रुटि होगी। यह समस्या आपको एक दिन काट देगी, इसलिए इसे जल्द से जल्द संभालने के लिए

+1

हालांकि कुछ अच्छे अंक बनाए गए हैं, लेकिन सबकुछ नहीं बताया गया है। यदि आप कोड को देखते हैं तो memcache api केवल अचार-स्ट्रिंग को अचार करता है। तो protobuffed मॉडल के साथ सूचियों मसालेदार, एकल मॉडल नहीं होगा। वास्तव में प्रोटोबफ आउटपुट सरल और छोटा है, मेरे परीक्षणों से पता चलता है कि यह कम सीपीयू गहन नहीं है - इसलिए मूल प्रश्न। मॉडल संस्करण बिंदु मान्य है, लेकिन मेरे लिए बहुत महत्वपूर्ण नहीं है क्योंकि आपके पास अमान्य कैश परिणामों से निपटने का कोई तरीका होना चाहिए और यह अक्सर मुझे नहीं लगता है। –

1

ऐप इंजन में दोनों अचार और प्रोटोबफ धीमे हैं क्योंकि वे शुद्ध पायथन में लागू होते हैं। मैंने पाया है कि str.join जैसी विधियों का उपयोग करके अपना खुद का, सरल क्रमबद्धता कोड लिखना तेजी से होता है क्योंकि अधिकांश काम सी में किया जाता है लेकिन यह केवल सरल डेटाटाइप के लिए काम करता है।

+0

क्या आपने मॉडल ऑब्जेक्ट्स के लिए भी ऐसा किया था? मैं आपके कार्यान्वयन को देखने के लिए उत्सुक होगा। –

+0

मैं ऐसा करता था, लेकिन python2.7 हमें cpickle देता है और यह अब तेज़ है। – FoxyLad

1

इसे और अधिक तेज़ी से करने का एक तरीका है अपने मॉडल को एक शब्दकोश में बदलना और अपने ev (de) serializers के रूप में मूल eval/repr फ़ंक्शन का उपयोग करना - निश्चित रूप से बुराई eval के साथ, लेकिन यह चाहिए यहां सुरक्षित रहें कि कोई बाहरी कदम नहीं है।

ठीक उसी तरह कार्यान्वित करने वाली कक्षा Fake_entity का एक उदाहरण नीचे। आप पहले अपना शब्दकोश fake = Fake_entity(entity) के माध्यम से बनाते हैं, तो आप बस अपना डेटा memcache.set(key, fake.serialize()) के माध्यम से स्टोर कर सकते हैं। Serialize() repr के मूल शब्दकोश विधि के लिए एक साधारण कॉल है, यदि आपको आवश्यकता हो तो कुछ जोड़ों के साथ (उदाहरण के लिए स्ट्रिंग की शुरुआत में एक पहचानकर्ता जोड़ें)।

इसे वापस लाने के लिए, बस fake = Fake_entity(memcache.get(key)) का उपयोग करें। Fake_entity ऑब्जेक्ट एक साधारण शब्दकोश है जिसका कुंजी विशेषताओं के रूप में भी पहुंच योग्य है। संदर्भ को छोड़कर आप सामान्य रूप से अपनी इकाई गुणों तक पहुंच सकते हैं, ऑब्जेक्ट लाने के बजाए प्रॉपर्टीज कुंजी देते हैं (जो वास्तव में काफी उपयोगी है)। आप fake.get(), या अधिक रुचि के साथ वास्तविक इकाई भी प्राप्त कर सकते हैं, इसे बदल सकते हैं और फिर fake.put() के साथ सहेज सकते हैं।

यह सूचियों के साथ काम नहीं करता है (यदि आप किसी क्वेरी से कई इकाइयां प्राप्त करते हैं), लेकिन पहचानकर्ता का उपयोग करके आसानी से समायोजित किया जा सकता है जैसे '### मॉडल ENTITY ###' को विभाजक के रूप में । केवल db.Model के साथ प्रयोग करें, Expando के लिए छोटे समायोजन की आवश्यकता होगी।

class Fake_entity(dict): 
    def __init__(self, record): 
     # simple case: a string, we eval it to rebuild our fake entity 
     if isinstance(record, basestring): 
      import datetime # <----- put all relevant eval imports here 
      from google.appengine.api import datastore_types 
      self.update(eval(record)) # careful with external sources, eval is evil 
      return None 

     # serious case: we build the instance from the actual entity 
     for prop_name, prop_ref in record.__class__.properties().items(): 
      self[prop_name] = prop_ref.get_value_for_datastore(record) # to avoid fetching entities 
     self['_cls'] = record.__class__.__module__ + '.' + record.__class__.__name__ 
     try: 
      self['key'] = str(record.key()) 
     except Exception: # the key may not exist if the entity has not been stored 
      pass 

    def __getattr__(self, k): 
     return self[k] 

    def __setattr__(self, k, v): 
     self[k] = v 

    def key(self): 
     from google.appengine.ext import db 
     return db.Key(self['key']) 

    def get(self): 
     from google.appengine.ext import db 
     return db.get(self['key']) 

    def put(self): 
     _cls = self.pop('_cls') # gets and removes the class name form the passed arguments 
     # import xxxxxxx ---> put your model imports here if necessary 
     Cls = eval(_cls) # make sure that your models declarations are in the scope here 
     real_entity = Cls(**self) # creates the entity 
     real_entity.put() # self explanatory 
     self['_cls'] = _cls # puts back the class name afterwards 
     return real_entity 

    def serialize(self): 
     return '### FAKE MODEL ENTITY ###\n' + repr(self) 
     # or simply repr, but I use the initial identifier to test and eval directly when getting from memcache 

मैं इस पर गति परीक्षण का स्वागत करता हूं, मुझे लगता है कि यह अन्य दृष्टिकोणों की तुलना में काफी तेज़ है। इसके अलावा, यदि आपके मॉडल किसी भी तरह से बदल गए हैं तो आपके पास कोई जोखिम नहीं है।

धारावाहिक नकली इकाई कैसा दिखता है इसका एक उदाहरण नीचे।datetime (बनाया) पर एक विशेष देखो के साथ ही संदर्भ गुण (उप डोमेन) लें:

### वेबपेज ENTITY को ###
{ 'स्थिति': u'admin ',' session_expiry ': कोई नहीं,' first_name ': u'Louis', 'last_name': u'Le Sieur ',' modified_by ': कोई नहीं,' password_hash ': u'a9993e364706816aba3e25717000000000000000', 'भाषा': u'fr ',' बनाया गया ': datetime.datetime (2010, 7, 18, 21, 50, 11, 750000), 'संशोधित': कोई नहीं, 'बनाया_by': कोई नहीं, 'ईमेल': u' [email protected] ',' key ':' agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw ',' session_ref ': कोई नहीं,' _cls ':' model.Login ',' groups ': [],' email___password_hash ': u' [email protected]+a9993e364706816aba3e25717000000000000000', 'सबडोमेन': datastore_types.Key.from_path (u'ububdomain ' , 22 9 एल, _app = u'jeregle '),' अनुमति ': [],' अनुमतियां ': []}


व्यक्तिगत रूप से मैं छोटी अवधि में अपनी इकाइयों को कैश करने के लिए स्थैतिक चर (memcache से तेज़) का उपयोग करता हूं, और सर्वर को बदलते समय डेटास्टोर लाता है या इसकी याददाश्त किसी कारण से फ़्लश हो जाती है (जो वास्तव में अक्सर होता है) ।

संबंधित मुद्दे