Quando la tua applicazione Django non va abbastanza veloce

Quando la tua applicazione Django non va abbastanza veloce

Django mette molta enfasi nel creare applicazioni velocemente. Questo è grandioso! Sviluppare applicazioni velocemente però non sempre significa sviluppare applicazioni veloci.

In questo talk mostrerò alcuni problemi di performance che ho incontrato (o introdotto) nella mia esperienza di sviluppatore. Vedremo esempi in diversi punti dell’applicazione: dai modelli, passando per le viste fino ai serializzatori. Alcuni di questi si sono rivelati facili da trovare e risolvere, altri subdoli o addirittura imbarazzanti. Tutti fortunatamente risolti.

61ba6f6b1fb82707b9344259f74a81b3?s=128

Riccardo Magliocchetti

May 04, 2019
Tweet

Transcript

  1. Quando la tua Quando la tua applicazione Django applicazione Django

    non va abbastanza veloce non va abbastanza veloce Riccardo Magliocchetti Riccardo Magliocchetti
  2. whoami whoami Software developer @ Maieutical Labs Consultant OSS contributor

  3. Ridiculously fast Ridiculously fast Django was designed to help developers

    take applications from concept to completion as quickly as possible. www.djangoproject.com
  4. La mia esperienza La mia esperienza Ottimizzazioni di piccoli e

    medi progetti Django in piccoli team
  5. DUE REGOLE PER DUE REGOLE PER OTTIMIZZARE OTTIMIZZARE

  6. 1. Don't do it Don't do it

  7. 1. Don't do it. 2. (for experts only) (for experts

    only) Don't do it yet. Don't do it yet.
  8. 1. Don't do it. 2. (for experts only) Don't do

    it yet. Not until you have a perfectly clear and unoptimized solution. Michael A. Jackson Principles of Program Design, 1975
  9. Hai raggiunto Hai raggiunto il product/market t? il product/market t?

  10. Quando pensare alle Quando pensare alle performance performance Idealmente prima

    di andare in produzione Code review: Quante query fa questo endpoint?
  11. questa URL è lenta questa URL è lenta Clienti insoddisfatti

    Clienti insoddisfatti
  12. uso uso troppa troppa RAM RAM Risorse sprecate Risorse sprecate

  13. COME SI OTTIMIZZA? COME SI OTTIMIZZA?

  14. Fare meno lavoro Fare meno lavoro Il codice sta facendo

    qualcosa che non serve
  15. Far meglio Far meglio Migliore algoritmo o struttura dati High

    Performance Python, Ian Ozsvald, Micha Gorelick
  16. MISURA PRIMA DI MISURA PRIMA DI OTTIMIZZARE OTTIMIZZARE

  17. Monitoring Monitoring SaaS: Datadog, New Relic, Elastic APM, etc... Prometheus,

    talk Pycon9 @davidesetti
  18. TOOL OPEN SOURCE TOOL OPEN SOURCE

  19. MISURARE RICHIESTE LENTE MISURARE RICHIESTE LENTE

  20. jazzband/django-silk jazzband/django-silk Pro lazione e introspezione di richieste Online, non

    in produzione
  21. Dashboard richieste Dashboard richieste

  22. Query per richiesta Query per richiesta

  23. SQL query SQL query

  24. piglei/uwsgi-sloth piglei/uwsgi-sloth Analizza richieste lente dai log Of ine, per

    uWSGI Attenzione alla media!
  25. None
  26. MISURARE MISURARE CONSUMO MEMORIA CONSUMO MEMORIA

  27. xrmx/pyuwsgimemhog xrmx/pyuwsgimemhog Trova viste con possibili memory leak dai log

    Of ine, per uWSGI
  28. Come ottimizzare in pratica Come ottimizzare in pratica 1. Fare

    un database fatto bene 2. Fare meno query 3. Fare query meno costose 4. Fare meno lavoro inutile
  29. Un database Un database User <- Profile <- Specific profile

    <- Specific Django reusable app profile
  30. Un database più veloce Un database più veloce Custom User

    <- Specific profile <- Specific Django reusable app profile
  31. L'ereditarietà concreta dei L'ereditarietà concreta dei modelli Django è una

    trappola modelli Django è una trappola Usa quella astratta o esplicitamente una relazione
  32. QUERY QUERY

  33. Prendere solo quello che serve Prendere solo quello che serve

    values() e values_list()
  34. Salvare solo quello che serve Salvare solo quello che serve

    - score.save() + score.save(update_fields=['points'])
  35. Le query N + 1 uccidono le Le query N

    + 1 uccidono le performance dell'applicazione performance dell'applicazione
  36. class Room: building = models.ForeignKey(Building) class Appointment: room = models.ForeignKey(Room)

    doctor = models.ForeignKey(Doctor) a = Appointment.objects.get( id=appointment_id ) print('In {} with {}'.format(a.room, a.doctor))
  37. Per evitare query N + 1 Per evitare query N

    + 1 select_related(), JOIN in SQL prefetch_related(), join in Python
  38. select_related() select_related() class Room: building = models.ForeignKey(Building) class Appointment: room

    = models.ForeignKey(Room) doctor = models.ForeignKey(Doctor) - a = Appointment.objects.get( + a = Appointment.objects.select_related( + 'doctor', 'room__building' ).get( id=appointment_id )
  39. select_related() è greedy select_related() è greedy + ).select_related( + 'doctor',

    'room__building' ) ^^^^^^^^^^^^^^^^ SELECT * in JOIN: doctor, room e building
  40. Attenzione a select_related() Attenzione a select_related() L'admin se nel list_display

    c'è un solo campo preso da una relazione seleziona tutti i modelli delle relazioni Memory leak in django 1.11?
  41. Attenzione alle librerie Attenzione alle librerie children = ListField( child=RecursiveField(),

    - source='children.all' + source='get_children' )
  42. Attenzione alle librerie Attenzione alle librerie Do less work in

    MySerializer Use django-mptt get_children method instead of going straight to the field model. /myapi/ does 40% less queries
  43. Il numero di query è testabile Il numero di query

    è testabile django.test.TransactionTestCase.assertNumQue ries() with self.assertNumQueries(2): Person.objects.create(name="Aaron") Person.objects.create(name="Daniel")
  44. INDICI INDICI

  45. Indici: se ltriamo o ordiniamo Indici: se ltriamo o ordiniamo

    - complete = models.BooleanField(default=False) + complete = models.BooleanField( + default=False, + db_index=True + )
  46. Ordinare non è gratis Ordinare non è gratis class MyAdmin:

    readonly_fields = ['creation_ts'] - ordering = ['creation_ts',] Remove unuseful ordering in MyAdmin Ordering a timestamp without indexes is sloooooow. Now the query takes 30 seconds less!
  47. Gli indici non sono gratis Gli indici non sono gratis

    class MyModel: elements = models.ManyToManyField(Element) Indici nella tabella di join: Indici nella tabella di join: 1. PK 2. FK verso MyModel 3. FK verso Element 4. UNIQUE sulle FK
  48. Gli indici ci servono sempre? Gli indici ci servono sempre?

    - elements = models.ManyToManyField(Element) + # Be aware we don't have indexes here + elements = ArrayField( + models.IntegerField(), + default=[], + blank=True + )
  49. Se gli indici non ci servono Se gli indici non

    ci servono Use an array field for MyContainer elements Instead of using an m2m. We don't need the field for the application, we need it for analytics. This let us save 2GB from tables and indexes.
  50. SERIALIZZATORI SERIALIZZATORI

  51. Usare il serializzatore giusto Usare il serializzatore giusto class MySerializer(serializers.ModelSerializer):

    - owner = UserSerializer() + owner = serializers.IntegerField( + read_only=True + )
  52. Esplicito meglio di implicito #1 Esplicito meglio di implicito #1

    class MySerializer(serializers.ModelSerializer): class Meta: model = MyModel fields = '__all__'
  53. Esplicito meglio di implicito #2 Esplicito meglio di implicito #2

    class MySerializer(serializers.ModelSerializer): class Meta: model = MyModel exclude = ('owner',)
  54. Non usare i ModelSerializer Non usare i ModelSerializer per serializzare

    per serializzare
  55. Il context ti è amico Il context ti è amico

    class MySerializer: - title = serializers.CharField( - source='assignment.task.title' - ) + title = serializers.SerializerMethodField() + def get_title(self, obj): + titles = self.context.get('titles') + return titles[obj.assignment_id] select_related() e prefetch_related() sarebbero state dispendiose
  56. Altre cose da guardare Altre cose da guardare e e

    e la documentazione del Database django ≥ 2 defer() only() union Prefetch aggregate() annotate() Database functions FilteredRelation() EXPLAIN
  57. Letture interessanti Letture interessanti The Dramatic Bene ts of Django

    Subqueries and Annotations Django ORM Cookbook Updating a Django queryset with annotation and subquery
  58. Conclusioni Conclusioni Misura prima di eventualmente ottimizzare Pensa alle performance

    Piccoli cambiamenti possono dare grandi risultati Conoscere SQL aiuta
  59. Grazie! Domande? Grazie! Domande? github.com/xrmx speakerdeck.com/xrmx @rmistaken