Denormalisierung für schnelle Key/Value Speichersysteme

RDBMS kommen im distributed Cloud-Computing selten vor. Daher muss man auch die Datenhaltung überdenken. Ein Blog-Eintrag diskutiert die Änderungen sehr gut: http://highscalability.com/how-i-learned-stop-worrying-and-love-using-lot-disk-space-scale. Im Prinzip geht es darum, aus Joins zu verzichten, und Daten in den verschiedenen Entities zu duplizieren. Die Erfahrung habe ich bei meinen GWT und GAE/J Projekt ebenfalls gemacht. Es führt zu weniger Lesezugriffen insgesamt, wenn man die Daten lokal in der Entity hält und nicht erst aus verschiedenen Entities zusammensucht, wie man bei normalisierten relationalen Modellen tut. (Außerdem ist es bei vielen lese-Zugriffen auch billiger, die Daten an einer Stelle zu haben, anstatt sie von verschiedenen Stellen über Lesezugriffe, die jeweils Kosten nach sich ziehen, einzusammeln.)

In meinen GWT-GAE/J-Projekt habe ich noch einen anderen Ansatz, die Zugriffe auf die Big-Table zu minimieren. Grundsätzlich: Alle GWT-RPC Implementierungen sind Fassaden, die auf Services zurückgreifen (Googe Guice injiziert diese in die RPC-Fassaden). Für diese Endpoints gibt es dann noch einmal einen Caching-Dekorator als eine Art Caching-Aspekt, der sich wie ein Proxy um die eigentlichen RPC-Implementierungen legt. Diese speichert relevante Daten im MemCache zwischen und minimiert so (langsame) Zugriffe auf die Big-Table. Bei Schreibzugriffen löscht der Caching-Proxy die relevanten Daten aus dem Cache, sodass sie neu geladen werden müssen. Das ist einfach und fix.

Ähnliche Beiträge

4 Gedanken zu “Denormalisierung für schnelle Key/Value Speichersysteme

  1. Absolut. Denormalisierung und Redundanz sind nicht immer so schlecht wie man meint. Ich mache gerade die gleiche Erfahrung auf GAE/J und muss sagen, dass es teilweise wirklich angenehm ist.

  2. hey christian,

    mich würde interessieren wie du das chaching + guice genau machst… hast du da eventuell source code, den du veröffentlichen könntest?

    danke für den interessanten post 🙂

    ra

  3. Also Guice in einer Webanwendung (also auf der GAE/J Seite) ist relativ schnell integriert, wenn man den Schritten unter http://stuffthathappens.com/blog/2009/09/14/guice-with-gwt/ folgt.

    Ein GWT RPC Interface sieht etwa so aus:

    @RemoteServiceRelativePath( "GWT.rpc" )
    public interface TrainerServiceRPC extends RemoteService
    {
    Map getTrainerById( String id );
    }

    Das wird implementiert von einer Klasse (einer Fassade), die per Guice die ganzen echten Services injiziert bekommt und so die Daten für GWT aufbereitet.

    public class TrainerServiceRPCImpl implements TrainerServiceRPC
    {
    @Inject TrainerService trainerService;
    @Inject ContactService contactService;
    @Inject CourseService courseService;

    }

    So kann man das ganze schon mal zusammenbinden für Guice:

    bind( TrainerServiceRPC.class ).to( TrainerServiceRPCImpl.class );

    So läuft das ganze das schon einmal.

    Wenn es zum Caching kommt, wird nun eine andere Implementierung als GWT-RPC genommen:

    bind( TrainerServiceRPC.class ).to( CachingTrainerServiceRPCImpl.class );

    CachingTrainerServiceRPCImpl sieht so aus:

    public class CachingTrainerServiceRPCImpl implements TrainerServiceRPC
    {
    @Inject TrainerServiceRPCImpl delegate;

    private final MemcacheService cache = MemcacheServiceFactory.getMemcacheService();

    @SuppressWarnings( "unchecked" )
    @Override
    public Map getTrainerById( String id )
    {
    Object object = cache.get( "getTrainerById"+id );

    if ( object != null )
    return (Map) object;

    Map result = delegate.getTrainerById( id );
    cache.put( "getTrainerById"+id, result );

    return result;
    }
    }

    Die wirkliche Implementierung wird also injiziert, sodass bei Cachefehlhit sich die Fassade die Daten von der eigentlichen TrainerServiceRPCImpl holt.

    Zwei Dinge zeigt die Realisierung nicht, sind aber relevant:

    a) Namensräume sind eine gute Idee, damit die Schlüssel unterschiedenen werden können. Ich verwende hier nur den Methodennamen.

    b) Verwandte Services können über Schreiboperationen die gecachten Wert ungültig machen. Daher müssen sich die Caches kennen und wissen, wer etwas modifiziert.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert