Confoo django

  • View
    252

  • Download
    1

Embed Size (px)

Text of Confoo django

  • Django et PostgreSQL sous la charge

    Rodolphe Quideville

    Pourquoi couper la queue du poulet ?Confoo - Montral

    18 fvrier 2015

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 1 / 51

  • #mylife

    Admin/Sys tendance DevOps depuis 20 ans84000 heures de connections au webNourri au logiciel libre exclusivementContributeur TsungFormateur auprs de Upstream-UniversityResponsable Performance chez Novapost / PeopleDocConsultant sur les performances des SI(G)

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 2 / 51

  • Assistant pour cette prsentation

    photo by InAweofGodsCreation flickr http://bit.ly/1EBQPrQ

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 3 / 51

  • Un dimanche matin dans la cuisinele petit Yohann demande sa mre

    Maman pourquoi tucoupes la queue dupoulet avant de le cuiredans le four ?

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 4 / 51

  • La maman de rpondre

    Je ne sais pas mais jaitoujours vu ma mre fairecomme a, vient on vademander ta grand-mre.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 5 / 51

  • Crer des donnes

    Limport est le parent pauvre des optimisations, sous prtexte quil seproduit rarement, ou pas ...Prenons un cas simple dimport de 300K objets dans une base.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 6 / 51

  • Crer des donnes

    Ce qui vient en premier lesprit, la boucle classique sur chaquelment

    create()def insert(values):

    """Insert values in DB"""for val in values:

    res = Item.objects.create(method="sequential",datetms=val[datetms],name=val[name],email=val[email],value=val[value])

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 7 / 51

  • Crer des donnes

    Il existe des mthodes pour linsertion en batch dans la doc de Django.

    bulk_create()def dobulk(part_values, method):

    """Do bulk insert on DB"""poms = []for val in part_values:

    poms.append(Item(datetms=val[datetms],method=method,name=val[name],email=val[email],value=val[value]))

    Item.objects.bulk_create(poms)

    Attention tout de mme aux effets de bords possibles.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 8 / 51

  • Crer des donnes

    Il existe des mthodes pour linsertion en batch dans la doc de Django.

    bulk_create()def dobulk(part_values, method):

    """Do bulk insert on DB"""poms = []for val in part_values:

    poms.append(Item(datetms=val[datetms],method=method,name=val[name],email=val[email],value=val[value]))

    Item.objects.bulk_create(poms)

    Attention tout de mme aux effets de bords possibles.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 8 / 51

  • Crer des donnes

    En utilisant linstruction COPY de PostgreSQL et un fichier temporaire(parfois inutile)

    cursor.copy_fromdef copyinsert(values, method):

    """Use COPY SQL FUNCTION to insert datas"""fields = [datetms, name, email, value, method]

    fpath = os.path.join(/tmp/, str(uuid4()))f = open(fpath, w)for val in values:

    f.write({},"{}","{}",{},{}\n.format(val[datetms],val[name],val[email],val[value],method))

    f.close()cursor = connection.cursor()cursor.copy_from(open(fpath, r), loader_item, columns=tuple(fields), sep=,)unlink(fpath)

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 9 / 51

  • Les rsultats

    Besoin de commentaires ?

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 10 / 51

  • Les rsultats

    Un peu plus prt des toiles

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 11 / 51

  • Pourquoi bulk_create est plus rapide ?

    Syntaxe connueINSERT INTO bar (value, ratio) VALUES (1, 3.4);INSERT INTO bar (value, ratio) VALUES (2, 5.4);INSERT INTO bar (value, ratio) VALUES (3, 3.14);

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 12 / 51

  • Pourquoi bulk_create est plus rapide ?

    Syntaxe connueINSERT INTO bar (value, ratio) VALUES (1, 3.4);INSERT INTO bar (value, ratio) VALUES (2, 5.4);INSERT INTO bar (value, ratio) VALUES (3, 3.14);

    Syntaxe moins connueINSERT INTO bar (value, ratio) VALUES (1, 3.4), (2, 5.4), (3, 3.14);

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 13 / 51

  • Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 14 / 51

  • Yohann et sa maman demande mre grand pourquoi elle coupaitla queue du poulet avant de lecuire

    Je ne sais pas mais jaitoujours vu ma mre fairecomme a, on va deman-der ta grand-mre.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 15 / 51

  • Par chance larrire grand-mre deYohann vit toujours, le voil enroute avec sa mre pour aller luiposer la question.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 16 / 51

  • Mettre jour un lot de donnes

    Pendant que Yohann est sur la route nous allons augments les ratiosde 5% des pommes qui ont un indice = 4

    Le modleclass Apple(models.Model):

    """An apple"""name = models.CharField(max_length=300)indice = IntegerField(default=0)ratio = FloatField(default=0)

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 17 / 51

  • Premier rflexe

    On lit et on boucleobjs = Apple.objects.filter(indice=4)

    for obj in objs:obj.ratio = obj.ratio * 1.05obj.save()

    1000 tuples dans la base ==> 1001 requtes (ou pire)

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 18 / 51

  • Premier rflexe

    On lit et on boucleobjs = Apple.objects.filter(indice=4)

    for obj in objs:obj.ratio = obj.ratio * 1.05obj.save()

    1000 tuples dans la base ==> 1001 requtes (ou pire)

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 18 / 51

  • F comme Facile

    Au sens SQL cela revient un UPDATE des plus classiques.

    SQLUPDATE apple_apple SET ratio=ratio * 1.5 WHERE indice=4;

    update et Fobjs = Apple.objects.filter(indice=indice).update(ratio=F(indice)*1.05)

    1 requte SQL excute au lieu de 1001, succs garantit

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 19 / 51

  • F comme Facile

    Au sens SQL cela revient un UPDATE des plus classiques.

    SQLUPDATE apple_apple SET ratio=ratio * 1.5 WHERE indice=4;

    update et Fobjs = Apple.objects.filter(indice=indice).update(ratio=F(indice)*1.05)

    1 requte SQL excute au lieu de 1001, succs garantit

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 19 / 51

  • F comme Facile

    Au sens SQL cela revient un UPDATE des plus classiques.

    SQLUPDATE apple_apple SET ratio=ratio * 1.5 WHERE indice=4;

    update et Fobjs = Apple.objects.filter(indice=indice).update(ratio=F(indice)*1.05)

    1 requte SQL excute au lieu de 1001, succs garantit

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 19 / 51

  • Paginator

    Si en plus on ajoute Paginator rien ne va plus

    Avantapples = Apple.objects.all().order_by(pk)paginator = Paginator(apples, 250)for p in paginator.page_range:

    for apple in paginator.page(p).object_list:apple.ratio = apple.ratio * 1.05apple.save()

    Ceci est une situation tire de faits rels

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 20 / 51

  • Paginator

    Si en plus on ajoute Paginator rien ne va plus

    Avantapples = Apple.objects.all().order_by(pk)paginator = Paginator(apples, 250)for p in paginator.page_range:

    for apple in paginator.page(p).object_list:apple.ratio = apple.ratio * 1.05apple.save()

    Ceci est une situation tire de faits rels

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 20 / 51

  • Paginator

    Key paginationkeyid = 0while True:

    apples = Apple.objects.filter(id__gt=keyid).order_by(id)[:250]for apple in apples:

    keyid = apple.id# do want you want hereapple.ratio = apple.ratio * 1.05apple.save()

    if len(apples) < 250:break

    Django cre par dfaut des cls primaires, a tombe bien onen a besoin ici

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 21 / 51

  • Paginator

    Key paginationkeyid = 0while True:

    apples = Apple.objects.filter(id__gt=keyid).order_by(id)[:250]for apple in apples:

    keyid = apple.id# do want you want hereapple.ratio = apple.ratio * 1.05apple.save()

    if len(apples) < 250:break

    Django cre par dfaut des cls primaires, a tombe bien onen a besoin ici

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 21 / 51

  • La table tient en cache

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 22 / 51

  • La table dpasse la taille du cache

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 23 / 51

  • Si vous parcourez des tables de grandes volumtrie, vous devezoublier OFFSET.

    Lisez lexcellent livre de Markus Winnand pour dcouvrir la paginationpar cle et la joyeuse vie des index.

    Rodolphe Quideville (Novapost) Django et PostgreSQL sous la charge 18 fvrier 2015 24 /