SlideShare ist ein Scribd-Unternehmen logo
1 von 34
Производительность в
       Django

        Иван Вирабян
     i.virabyan@gmail.com
                            1
db_index=True не творит
             чудеса

class Post(models.Model):
       category = models.CharField()
       is_editorial = models.BooleanField()
       created = models.DateTimeField()

Post.objects.filter(category=‘news’, is_editorial=True) 
            .order_by(‘-created’)



                                                        2
db_index=True не творит
             чудеса

class Post(models.Model):
       category = models.CharField(db_index=True)
       is_editorial = models.BooleanField(db_index=True)
       created = models.DateTimeField(db_index=True)

Post.objects.filter(category=‘news’, is_editorial=True) 
            .order_by(‘-created’)[:30]



                                                           3
db_index=True не творит
             чудеса

class Post(models.Model):
       category = models.CharField(db_index=True)
       is_editorial = models.BooleanField(db_index=True)
       created = models.DateTimeField(db_index=True)

Post.objects.filter(category=‘news’, is_editorial=True) 
            .order_by(‘-created’)[:30]

blog/sql/post.sql (либо в миграции south):
   CREATE INDEX idx_category_editorial_created
   ON blog_post(category, is_editorial, created);          4
Замеры

Таблица содержащая 200 тыс. записей:

Три одиночных индекса: 170мс
Комбинированный индекс: 1мс




                                       5
Пока нет поддержки в
              Django
Очень вероятно, что в Django 1.5 наряду с
параметром unique_together появится
index_together.
https://code.djangoproject.com/ticket/5805




                                             6
db_index=True не творит
               чудеса
class Post(models.Model):
    category = models.CharField()
    is_editorial = models.BooleanField()
    created = models.DateTimeField()

    class Meta:
        index_together = (‘category’, ‘is_editorial, ‘created’)

Post.objects.filter(category=‘news’, is_editorial=True) 
            .order_by(‘-created’)[:30]


                                                            7
Денормализация

class Post(models.Model):
    category = models.CharField(max_length=100)
    author = models.ForeignKey(Profile, related_name=‘posts’)


Post.objects.filter(category=‘news’).order_by(‘author__name’)[:30]




                                                                8
mysql> show profile;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| init                 | 0.000039 |
| optimizing           | 0.000029 |
| statistics           | 0.000148 |
| preparing            | 0.000028 |
| Creating tmp table   | 0.000073 |
| executing            | 0.000010 |
| Copying to tmp table | 4.347774 |
| Sorting result       | 0.090562 |
| Sending data         | 0.000132 |
| removing tmp table   | 0.000024 |
| query end            | 0.000012 |
| freeing items        | 0.000060 |
| cleaning up          | 0.000014 |   9
+----------------------+----------+
Выход есть!
class Post(models.Model):
    category = models.CharField(max_length=100)
    author = models.ForeignKey(Profile, related_name=‘posts’)
    author_name = models.CharField(max_length=100)

Post.objects.filter(category=‘news’).order_by(‘author_name’)

@receiver(signals.post_save, sender=User)
def update_author_name(instance, **kwargs):
    instance.posts.update(author_name=instance.name)




                                                                10
Multi-table Inheritance
             использовать с осторожностью!

class Employee(models.Model):
    name = models.CharField(max_length=100)
    salary = models.PositiveIntegerField()

class Developer(Employee):
    lang = models.CharField(max_length=50)


Developer.objects.filter(lang=‘python’).order_by(‘salary’)

           медленно при большом объеме данных




                                                             11
Что это запросы?
users = User.objects.filter(username__in=[‘vasya’, ‘petya’])
Post.objects.filter(author__in=users)




Post.objects.filter(author__isnull=True)




                                                               12
Что это запросы?
users = User.objects.filter(username__in=[‘vasya’, ‘petya’])
Post.objects.filter(author__in=users)

SELECT ... FROM `blog_post`
WHERE `blog_post`.`author_id` IN (
    SELECT U0.`id` FROM `auth_user` U0
    WHERE U0.`username` IN ('vasya', 'petya'))

Post.objects.filter(author__isnull=True)

SELECT ... FROM `blog_post`
LEFT OUTER JOIN `auth_user`
       ON (`blog_post`.`author_id` = `auth_user`.`id`)
WHERE `auth_user`.`id` IS NULL                                 13
Django Debug Toolbar




                       14
Defer() может быть
               медленней!
>>> timeit('list(Post.objects.all()[:30])',
           'from blog.models import Post', number=100)
1.3283531665802002

>>> timeit('list(Post.objects.defer(“author“, “body”)[:30])',
                   'from blog.models import Post', number=100)
1.6067261695861816




                                                                 15
Причина?
В случае с defer() в Model.__init__() значения полей передаются как
**kwargs

def __init__(self, *args, **kwargs):
    ...
    fields_iter = iter(self._meta.fields)
    if not kwargs:
        for val, field in izip(args, fields_iter):
            setattr(self, field.attname, val)
    # Далее идет длинный (~70 строк) код для случая с kwargs.
    # Этот код работает на 33% медленней.
    # Сильно тормозит хак для "умной" обработки ForeignKey полей

                                                                 16
Вывод?
.defer() имеет смысл только когда из выборки
исключается большая часть полей. Но тогда проще
использовать .only():

>>> timeit('list(Post.objects.only(“title”)[:30])',
           'from blog.models import Post', number=100)
0.88186287879943848

Если нужно только данные (без методов) то .values()
будет однозначно быстрее:

>>> timeit('list(Post.objects.values("id", “title")[:30])',
           'from blog.models import Post', number=100)
0.25724387168884277
                                                              17
Запросы в цикле
posts = list(Post.objects.all()[:100])

{% for post in posts %}
     {{ post.author.username }}
{% endfor %}

В цикле происходит примерно следующее:
User.objects.get(pk=post.author_id)


                                         18
Запросы в цикле
Замеряем время цикла: ~250мс !?

Да-да, мы слышали про select_related(),
но неужели БД настолько уныла?

Проверим!

                                      19
ncalls tottime cumtime percall    filename:lineno(function)
      100    0.002   0.283   0.003   manager.py:131(get)
      100    0.001   0.276   0.003   query.py:337(get)
2800/1600    0.001   0.192   0.000   {len}
      100    0.001   0.191   0.002   query.py:74(__len__)
      200    0.003   0.190   0.001   query.py:214(iterator)
      200    0.002   0.172   0.001   compiler.py:673(results_iter)
      100    0.001   0.159   0.002   compiler.py:711(execute_sql)
      100    0.005   0.086   0.001   util.py:31(execute)
      100    0.000   0.075   0.001   base.py:84(execute)
      100    0.002   0.075   0.001   cursors.py:139(execute)
      100    0.000   0.070   0.001   cursors.py:315(_query)
      200    0.002   0.068   0.000   query.py:752(_clone)
      200    0.006   0.066   0.000   query.py:223(clone)
      100    0.001   0.063   0.001   cursors.py:277(_do_query)
      100    0.058   0.058   0.001   {method 'query' of
'_mysql.connection' objects}
 4300/800    0.018   0.057   0.000   copy.py:144(deepcopy)
                                                                     20
К чему нам это знать?

Вопросы на stackoverflow.com:
Say, I have a page with a photo gallery. Each thumbnail has
e.g. a photo, country, author and so on. And it is very slow.
I have performed some profiling using django-debug-toolbar:
SQL Queries: default 84.81 ms (147 queries)
But:
Total CPU time: 5768.360 msec


                                                            21
Шаблонизатор
   (безбожно тормозит)




                         22
Нужно отрендерить
много-много всего…
На это уходит большая часть времени

Время рендеринга:

Django ~1сек
Jinja ~0.3сек

С Jinja можно получить
ускорение до 10 раз



                                      23
Рендеринг на клиенте
<script type="text/x-jquery-tmpl" id="post-tmpl">
    <li>${title} <div>${views}</div></li>
</script>

<script type="text/javascript">
    $(function() {
        var template = $('#post-tmpl');
        $.tmpl(template, {{ posts }}).appendTo('#posts');
    });
</script>

<ul id=“posts"></ul>
                                                       24
Рендеринг на клиенте
+ Освобождаются драгоценные ресурсы
сервера.
- Нельзя использовать имеющиеся template-теги
и фильтры




                                          25
Кеширование

val = cache.get("somekey")
if val is None:
    val = compute_val()
    cache.set("somekey", val)


Скорость повышается в разы!
Но возникает проблема актуальности
данных.
                                     26
Инвалидация
Пути решения:
• Инвалидация по таймауту
    + Легко реализовать
    - Не гарантируется актуальность данных
•   Инвалидация по событию
    + Данные всегда актуальны
    - Есть некоторые сложности (решаемые)




                                             27
Инвалидация по событию
@receiver(post_save, sender=Game)
def invalidate_games(**kwargs):
    cache.delete(‘game_list’)

А что если ключ с суффиксом:
‘game_list:%s’ % suffix


Как инвалидировать все комбинации?


                                     28
Инвалидация по событию
Вариант 1: список всех имеющихся ключей
key = ‘game_list:%s’ % suffix
val = cache.get(key)
if val is None:
    val = ...
    cache.set(key, val)
    # Запомним этот ключик для последующей инвалидации
    keys = cache.get(‘game_list_keys’, [])
    cache.set(‘game_list_keys’, set(keys) | set([key]))

# Инвалидация
keys = cache.get(‘game_list_keys’)
cache.delete_many(keys)
cache.delete(‘game_list_keys’)                            29
Инвалидация по событию
Вариант 2: версионирование
key = ‘game_list:%s’ % suffix
version = cache.get(‘top_games_version’, 1)
val = cache.get(key, version=version)
if val is None:
    val = ...
    cache.set(key, val, version=version)

# Инвалидация
try:
     cache.incr(‘top_games_version’)
except ValueError:
     cache.set(‘top_games_version’, 1)
                                              30
Инвалидация по событию
Мы используем удобный декоратор:
gametags.py:
   @register.simple_tag
   @cached(vary_on_args=True)
   def games(platform=None, genre=None):
       ...

signals.py:
   @receiver(post_save, sender=Game)
   def inval_games(**kwargs):
       invalidate(‘games.templatetags.gametags.games’)



                                                         31
Инвалидация по событию
Мы используем удобный декоратор:
gametags.py:
   @register.simple_tag
   @cached(vary_on_args=True, locmem=True)
   def games(platform=None, genre=None):
       ...

signals.py:
   @receiver(post_save, sender=Game)
   def inval_games(**kwargs):
       invalidate(‘games.templatetags.gametags.games’)



                                                         32
Оптимизация
Иногда есть смысл оптимизировать код,
работающий лишь несколько миллисекунд:
•   Middleware
•   Context processors
•   Template tags в базовом шаблоне

Если среднее время ответа 100мс, а время
работы middleware – 11мс, то снизив его до
1мс мы сможем обслуживать на 10%
больше запросов.

                                             33
Делайте их ленивыми
Вы не знаете наверняка, пригодится ли где-нибудь то, что
вы насчитали в своем context processor’е. Поэтому
middleware и context processors должны быть ленивыми!
from django.utils.functional import lazy

class LocationMiddleware(object):
    def process_request(self, request):
        request.location = lazy(get_location, dict)(request)

def get_location(request):
    g = GeoIP()
    remote_ip = request.META.get('REMOTE_ADDR')
    return g.city(remote_ip)

                                                          34

Weitere ähnliche Inhalte

Was ist angesagt?

Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Roman Brovko
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Roman Brovko
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.Roman Brovko
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Лекция 6. Классы 1.
Лекция 6. Классы 1.Лекция 6. Классы 1.
Лекция 6. Классы 1.Roman Brovko
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonPython Meetup
 
Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Roman Brovko
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?PyNSK
 
Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.Roman Brovko
 
Максим Щепелин. "Unittesting. Как?"
Максим Щепелин. "Unittesting. Как?"Максим Щепелин. "Unittesting. Как?"
Максим Щепелин. "Unittesting. Как?"Python Meetup
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.Roman Brovko
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILRoman Brovko
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.Roman Brovko
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Roman Brovko
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPython Meetup
 
Магия метаклассов
Магия метаклассовМагия метаклассов
Магия метаклассовAndrey Zakharevich
 
8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)Smolensk Computer Science Club
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Vasya Petrov
 
Python dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущееPython dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущееdelimitry
 

Was ist angesagt? (20)

Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Лекция 6. Классы 1.
Лекция 6. Классы 1.Лекция 6. Классы 1.
Лекция 6. Классы 1.
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
 
Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?
 
Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.
 
Максим Щепелин. "Unittesting. Как?"
Максим Щепелин. "Unittesting. Как?"Максим Щепелин. "Unittesting. Как?"
Максим Щепелин. "Unittesting. Как?"
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GIL
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стиль
 
Магия метаклассов
Магия метаклассовМагия метаклассов
Магия метаклассов
 
Charming python sc2-8
Charming python sc2-8Charming python sc2-8
Charming python sc2-8
 
8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
Python dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущееPython dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущее
 

Ähnlich wie Производительность в Django

Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6Technopark
 
Разработка расширяемых приложений на Django
Разработка расширяемых приложений на DjangoРазработка расширяемых приложений на Django
Разработка расширяемых приложений на DjangoMoscowDjango
 
Web весна 2013 лекция 4
Web весна 2013 лекция 4Web весна 2013 лекция 4
Web весна 2013 лекция 4Technopark
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"LogeekNightUkraine
 
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8Technopark
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlOleksandr Petrov
 
Sequel — механизм доступа к БД, написанный на Ruby
Sequel — механизм доступа к БД, написанный на RubySequel — механизм доступа к БД, написанный на Ruby
Sequel — механизм доступа к БД, написанный на RubyAlexey Nayden
 
12 - Web-технологии. Django модели
12 - Web-технологии. Django модели12 - Web-технологии. Django модели
12 - Web-технологии. Django моделиRoman Brovko
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
View как чистая функция от состояния базы данных  - Илья Беда, bro.agencyView как чистая функция от состояния базы данных  - Илья Беда, bro.agency
View как чистая функция от состояния базы данных - Илья Беда, bro.agencyit-people
 
Как мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектовКак мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектовBoris Tsema
 
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
WebCamp: Developer Day: Parse'им бэкенд - Аким ХалиловWebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
WebCamp: Developer Day: Parse'им бэкенд - Аким ХалиловGeeksLab Odessa
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному кодуVasiliy Deynega
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4Technopark
 
automation is iOS development
automation is iOS developmentautomation is iOS development
automation is iOS developmentIvan Trifonov
 
тестирование защищенности веб приложений
тестирование защищенности веб приложенийтестирование защищенности веб приложений
тестирование защищенности веб приложенийZestranec
 

Ähnlich wie Производительность в Django (20)

Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
Разработка расширяемых приложений на Django
Разработка расширяемых приложений на DjangoРазработка расширяемых приложений на Django
Разработка расширяемых приложений на Django
 
Web весна 2013 лекция 4
Web весна 2013 лекция 4Web весна 2013 лекция 4
Web весна 2013 лекция 4
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"
 
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher Sql
 
Sequel — механизм доступа к БД, написанный на Ruby
Sequel — механизм доступа к БД, написанный на RubySequel — механизм доступа к БД, написанный на Ruby
Sequel — механизм доступа к БД, написанный на Ruby
 
12 - Web-технологии. Django модели
12 - Web-технологии. Django модели12 - Web-технологии. Django модели
12 - Web-технологии. Django модели
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
View как чистая функция от состояния базы данных  - Илья Беда, bro.agencyView как чистая функция от состояния базы данных  - Илья Беда, bro.agency
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
 
Как мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектовКак мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектов
 
Survive with OOP
Survive with OOPSurvive with OOP
Survive with OOP
 
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
WebCamp: Developer Day: Parse'им бэкенд - Аким ХалиловWebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
WebCamp: Developer Day: Parse'им бэкенд - Аким Халилов
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному коду
 
Лекция #7. Django ORM
Лекция #7. Django ORMЛекция #7. Django ORM
Лекция #7. Django ORM
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4
 
automation is iOS development
automation is iOS developmentautomation is iOS development
automation is iOS development
 
тестирование защищенности веб приложений
тестирование защищенности веб приложенийтестирование защищенности веб приложений
тестирование защищенности веб приложений
 

Mehr von MoscowDjango

Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и DjangoMoscowDjango
 
Пример fuzzy testing для поиска URL в тексте
Пример fuzzy testing для поиска URL в текстеПример fuzzy testing для поиска URL в тексте
Пример fuzzy testing для поиска URL в текстеMoscowDjango
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать кодMoscowDjango
 
Cyclone + Eventsource (realtime push-сообщения)
Cyclone + Eventsource (realtime push-сообщения)Cyclone + Eventsource (realtime push-сообщения)
Cyclone + Eventsource (realtime push-сообщения)MoscowDjango
 
Django на Android
Django на AndroidDjango на Android
Django на AndroidMoscowDjango
 
Работа со статикой в Django
Работа со статикой в DjangoРабота со статикой в Django
Работа со статикой в DjangoMoscowDjango
 
Class Based Generic Views в Django
Class Based Generic Views в DjangoClass Based Generic Views в Django
Class Based Generic Views в DjangoMoscowDjango
 
Простой и удобный деплоймент проекта
Простой и удобный деплоймент проектаПростой и удобный деплоймент проекта
Простой и удобный деплоймент проектаMoscowDjango
 
Django South. Миграция баз данных.
Django South. Миграция баз данных.  Django South. Миграция баз данных.
Django South. Миграция баз данных. MoscowDjango
 
Журнальная вёрстка в Django
Журнальная вёрстка в DjangoЖурнальная вёрстка в Django
Журнальная вёрстка в DjangoMoscowDjango
 

Mehr von MoscowDjango (10)

Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и Django
 
Пример fuzzy testing для поиска URL в тексте
Пример fuzzy testing для поиска URL в текстеПример fuzzy testing для поиска URL в тексте
Пример fuzzy testing для поиска URL в тексте
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать код
 
Cyclone + Eventsource (realtime push-сообщения)
Cyclone + Eventsource (realtime push-сообщения)Cyclone + Eventsource (realtime push-сообщения)
Cyclone + Eventsource (realtime push-сообщения)
 
Django на Android
Django на AndroidDjango на Android
Django на Android
 
Работа со статикой в Django
Работа со статикой в DjangoРабота со статикой в Django
Работа со статикой в Django
 
Class Based Generic Views в Django
Class Based Generic Views в DjangoClass Based Generic Views в Django
Class Based Generic Views в Django
 
Простой и удобный деплоймент проекта
Простой и удобный деплоймент проектаПростой и удобный деплоймент проекта
Простой и удобный деплоймент проекта
 
Django South. Миграция баз данных.
Django South. Миграция баз данных.  Django South. Миграция баз данных.
Django South. Миграция баз данных.
 
Журнальная вёрстка в Django
Журнальная вёрстка в DjangoЖурнальная вёрстка в Django
Журнальная вёрстка в Django
 

Производительность в Django

  • 1. Производительность в Django Иван Вирабян i.virabyan@gmail.com 1
  • 2. db_index=True не творит чудеса class Post(models.Model): category = models.CharField() is_editorial = models.BooleanField() created = models.DateTimeField() Post.objects.filter(category=‘news’, is_editorial=True) .order_by(‘-created’) 2
  • 3. db_index=True не творит чудеса class Post(models.Model): category = models.CharField(db_index=True) is_editorial = models.BooleanField(db_index=True) created = models.DateTimeField(db_index=True) Post.objects.filter(category=‘news’, is_editorial=True) .order_by(‘-created’)[:30] 3
  • 4. db_index=True не творит чудеса class Post(models.Model): category = models.CharField(db_index=True) is_editorial = models.BooleanField(db_index=True) created = models.DateTimeField(db_index=True) Post.objects.filter(category=‘news’, is_editorial=True) .order_by(‘-created’)[:30] blog/sql/post.sql (либо в миграции south): CREATE INDEX idx_category_editorial_created ON blog_post(category, is_editorial, created); 4
  • 5. Замеры Таблица содержащая 200 тыс. записей: Три одиночных индекса: 170мс Комбинированный индекс: 1мс 5
  • 6. Пока нет поддержки в Django Очень вероятно, что в Django 1.5 наряду с параметром unique_together появится index_together. https://code.djangoproject.com/ticket/5805 6
  • 7. db_index=True не творит чудеса class Post(models.Model): category = models.CharField() is_editorial = models.BooleanField() created = models.DateTimeField() class Meta: index_together = (‘category’, ‘is_editorial, ‘created’) Post.objects.filter(category=‘news’, is_editorial=True) .order_by(‘-created’)[:30] 7
  • 8. Денормализация class Post(models.Model): category = models.CharField(max_length=100) author = models.ForeignKey(Profile, related_name=‘posts’) Post.objects.filter(category=‘news’).order_by(‘author__name’)[:30] 8
  • 9. mysql> show profile; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | init | 0.000039 | | optimizing | 0.000029 | | statistics | 0.000148 | | preparing | 0.000028 | | Creating tmp table | 0.000073 | | executing | 0.000010 | | Copying to tmp table | 4.347774 | | Sorting result | 0.090562 | | Sending data | 0.000132 | | removing tmp table | 0.000024 | | query end | 0.000012 | | freeing items | 0.000060 | | cleaning up | 0.000014 | 9 +----------------------+----------+
  • 10. Выход есть! class Post(models.Model): category = models.CharField(max_length=100) author = models.ForeignKey(Profile, related_name=‘posts’) author_name = models.CharField(max_length=100) Post.objects.filter(category=‘news’).order_by(‘author_name’) @receiver(signals.post_save, sender=User) def update_author_name(instance, **kwargs): instance.posts.update(author_name=instance.name) 10
  • 11. Multi-table Inheritance использовать с осторожностью! class Employee(models.Model): name = models.CharField(max_length=100) salary = models.PositiveIntegerField() class Developer(Employee): lang = models.CharField(max_length=50) Developer.objects.filter(lang=‘python’).order_by(‘salary’) медленно при большом объеме данных 11
  • 12. Что это запросы? users = User.objects.filter(username__in=[‘vasya’, ‘petya’]) Post.objects.filter(author__in=users) Post.objects.filter(author__isnull=True) 12
  • 13. Что это запросы? users = User.objects.filter(username__in=[‘vasya’, ‘petya’]) Post.objects.filter(author__in=users) SELECT ... FROM `blog_post` WHERE `blog_post`.`author_id` IN ( SELECT U0.`id` FROM `auth_user` U0 WHERE U0.`username` IN ('vasya', 'petya')) Post.objects.filter(author__isnull=True) SELECT ... FROM `blog_post` LEFT OUTER JOIN `auth_user` ON (`blog_post`.`author_id` = `auth_user`.`id`) WHERE `auth_user`.`id` IS NULL 13
  • 15. Defer() может быть медленней! >>> timeit('list(Post.objects.all()[:30])', 'from blog.models import Post', number=100) 1.3283531665802002 >>> timeit('list(Post.objects.defer(“author“, “body”)[:30])', 'from blog.models import Post', number=100) 1.6067261695861816 15
  • 16. Причина? В случае с defer() в Model.__init__() значения полей передаются как **kwargs def __init__(self, *args, **kwargs): ... fields_iter = iter(self._meta.fields) if not kwargs: for val, field in izip(args, fields_iter): setattr(self, field.attname, val) # Далее идет длинный (~70 строк) код для случая с kwargs. # Этот код работает на 33% медленней. # Сильно тормозит хак для "умной" обработки ForeignKey полей 16
  • 17. Вывод? .defer() имеет смысл только когда из выборки исключается большая часть полей. Но тогда проще использовать .only(): >>> timeit('list(Post.objects.only(“title”)[:30])', 'from blog.models import Post', number=100) 0.88186287879943848 Если нужно только данные (без методов) то .values() будет однозначно быстрее: >>> timeit('list(Post.objects.values("id", “title")[:30])', 'from blog.models import Post', number=100) 0.25724387168884277 17
  • 18. Запросы в цикле posts = list(Post.objects.all()[:100]) {% for post in posts %} {{ post.author.username }} {% endfor %} В цикле происходит примерно следующее: User.objects.get(pk=post.author_id) 18
  • 19. Запросы в цикле Замеряем время цикла: ~250мс !? Да-да, мы слышали про select_related(), но неужели БД настолько уныла? Проверим! 19
  • 20. ncalls tottime cumtime percall filename:lineno(function) 100 0.002 0.283 0.003 manager.py:131(get) 100 0.001 0.276 0.003 query.py:337(get) 2800/1600 0.001 0.192 0.000 {len} 100 0.001 0.191 0.002 query.py:74(__len__) 200 0.003 0.190 0.001 query.py:214(iterator) 200 0.002 0.172 0.001 compiler.py:673(results_iter) 100 0.001 0.159 0.002 compiler.py:711(execute_sql) 100 0.005 0.086 0.001 util.py:31(execute) 100 0.000 0.075 0.001 base.py:84(execute) 100 0.002 0.075 0.001 cursors.py:139(execute) 100 0.000 0.070 0.001 cursors.py:315(_query) 200 0.002 0.068 0.000 query.py:752(_clone) 200 0.006 0.066 0.000 query.py:223(clone) 100 0.001 0.063 0.001 cursors.py:277(_do_query) 100 0.058 0.058 0.001 {method 'query' of '_mysql.connection' objects} 4300/800 0.018 0.057 0.000 copy.py:144(deepcopy) 20
  • 21. К чему нам это знать? Вопросы на stackoverflow.com: Say, I have a page with a photo gallery. Each thumbnail has e.g. a photo, country, author and so on. And it is very slow. I have performed some profiling using django-debug-toolbar: SQL Queries: default 84.81 ms (147 queries) But: Total CPU time: 5768.360 msec 21
  • 22. Шаблонизатор (безбожно тормозит) 22
  • 23. Нужно отрендерить много-много всего… На это уходит большая часть времени Время рендеринга: Django ~1сек Jinja ~0.3сек С Jinja можно получить ускорение до 10 раз 23
  • 24. Рендеринг на клиенте <script type="text/x-jquery-tmpl" id="post-tmpl"> <li>${title} <div>${views}</div></li> </script> <script type="text/javascript"> $(function() { var template = $('#post-tmpl'); $.tmpl(template, {{ posts }}).appendTo('#posts'); }); </script> <ul id=“posts"></ul> 24
  • 25. Рендеринг на клиенте + Освобождаются драгоценные ресурсы сервера. - Нельзя использовать имеющиеся template-теги и фильтры 25
  • 26. Кеширование val = cache.get("somekey") if val is None: val = compute_val() cache.set("somekey", val) Скорость повышается в разы! Но возникает проблема актуальности данных. 26
  • 27. Инвалидация Пути решения: • Инвалидация по таймауту + Легко реализовать - Не гарантируется актуальность данных • Инвалидация по событию + Данные всегда актуальны - Есть некоторые сложности (решаемые) 27
  • 28. Инвалидация по событию @receiver(post_save, sender=Game) def invalidate_games(**kwargs): cache.delete(‘game_list’) А что если ключ с суффиксом: ‘game_list:%s’ % suffix Как инвалидировать все комбинации? 28
  • 29. Инвалидация по событию Вариант 1: список всех имеющихся ключей key = ‘game_list:%s’ % suffix val = cache.get(key) if val is None: val = ... cache.set(key, val) # Запомним этот ключик для последующей инвалидации keys = cache.get(‘game_list_keys’, []) cache.set(‘game_list_keys’, set(keys) | set([key])) # Инвалидация keys = cache.get(‘game_list_keys’) cache.delete_many(keys) cache.delete(‘game_list_keys’) 29
  • 30. Инвалидация по событию Вариант 2: версионирование key = ‘game_list:%s’ % suffix version = cache.get(‘top_games_version’, 1) val = cache.get(key, version=version) if val is None: val = ... cache.set(key, val, version=version) # Инвалидация try: cache.incr(‘top_games_version’) except ValueError: cache.set(‘top_games_version’, 1) 30
  • 31. Инвалидация по событию Мы используем удобный декоратор: gametags.py: @register.simple_tag @cached(vary_on_args=True) def games(platform=None, genre=None): ... signals.py: @receiver(post_save, sender=Game) def inval_games(**kwargs): invalidate(‘games.templatetags.gametags.games’) 31
  • 32. Инвалидация по событию Мы используем удобный декоратор: gametags.py: @register.simple_tag @cached(vary_on_args=True, locmem=True) def games(platform=None, genre=None): ... signals.py: @receiver(post_save, sender=Game) def inval_games(**kwargs): invalidate(‘games.templatetags.gametags.games’) 32
  • 33. Оптимизация Иногда есть смысл оптимизировать код, работающий лишь несколько миллисекунд: • Middleware • Context processors • Template tags в базовом шаблоне Если среднее время ответа 100мс, а время работы middleware – 11мс, то снизив его до 1мс мы сможем обслуживать на 10% больше запросов. 33
  • 34. Делайте их ленивыми Вы не знаете наверняка, пригодится ли где-нибудь то, что вы насчитали в своем context processor’е. Поэтому middleware и context processors должны быть ленивыми! from django.utils.functional import lazy class LocationMiddleware(object): def process_request(self, request): request.location = lazy(get_location, dict)(request) def get_location(request): g = GeoIP() remote_ip = request.META.get('REMOTE_ADDR') return g.city(remote_ip) 34