Как эффективно кэшировать большой django набор запросов? - PullRequest
1 голос
/ 17 марта 2020

Я работаю над проектом Django, использующим базу данных PostgreSQL, в которой мне нужно выполнить иногда сложный запрос к таблице профиля пользователя с ~ 1М записей, и возвращаемый набор данных может быть в диапазоне от 0 до 1М записей. Моя проблема в том, что после того, как я соберу все записи, я хочу иметь возможность отфильтровать их для запуска аналитики этих профилей. Аналитика не может быть завершена в том же запросе / ответе l oop, поскольку это приведет к превышению времени ожидания для всех, кроме самых маленьких наборов запросов. Поэтому я использую asyn c javascript, чтобы отбрасывать новые запросы для каждого типа аналитики, которую я хочу.

Например, я возьму все профили в первоначальном запросе, а затем отправлю последующие запросы, чтобы взять эти профили и вернуть мне процентное соотношение полов или процент пользователей, имеющих определенную должность, и др c. Проблема заключается в том, что каждый последующий запрос должен снова запускать исходный запрос. Я хотел бы как-то кэшировать этот запрос, а затем выполнить мой запрос на этом подмножестве таблицы профиля, не выполняя первоначальный длительный запрос.

Я пытался использовать функцию кэширования для кэшировать сам набор запросов, но это на самом деле немного замедляет производительность, я полагаю, потому что набор запросов должен быть засечен или не засечен, а затем он все еще должен работать? Я также попытался кэшировать список идентификаторов из длинного родительского запроса (это потенциально ОЧЕНЬ длинный список, вплоть до 1М целых чисел), и это приводит к полной остановке моей системы для чего-то большего, чем, например, 44k записей.

Кто-нибудь имел дело с этим вопросом раньше? Я знаю, что мог бы настроить систему «работник / очередь», которая включена в мою дорожную карту, но было бы замечательно, если бы существовало простое решение, использующее встроенные возможности Django.

Пример кода:

def get_analytics(request):
    data_type = request.POST.get('data_type')
    query_params = request.POST.get('query_params') # a crazy filter with lots of Q objects
    profile_ids = get_profiles(query_params) # I WANT TO CACHE THIS
    profiles = Profile.objects.filter(id__in=profile_ids).distinct()

    if data_type == 'overview':
        return profiles.count()

    else if data_type == 'gender':
        gender_breakdown = profiles.filter(a_filter_for_gender).values('gender').annotate(Count('gender', distinct=True))
        return gender_breakdown

def cache_function(length):
    """
    A variant of the snippet posted by Jeff Wheeler at
    http://www.djangosnippets.org/snippets/109/
    Caches a function, using the function and its arguments as the key, and the return
    value as the value saved. It passes all arguments on to the function, as
    it should.
    The decorator itself takes a length argument, which is the number of
    seconds the cache will keep the result around.
    """
    def decorator(func):
        def inner_func(*args, **kwargs):
            if hasattr(settings, 'IS_IN_UNITTEST'):
                return func(*args, **kwargs)

            key = get_cache_key(func.__name__, func.__module__, args, kwargs)
            value = cache.get(key)

            if key in cache:
                return value
            else:
                result = func(*args, **kwargs)
                cache.set(key, result, length)
                return result
        return inner_func
    return decorator

@cache_function(60*2)
def get_profiles(query_params):
    return Profile.objects.filter(query_params).values_list('id')

Почему кэширование идентификаторов замедляет работу моей системы? Есть ли лучший способ сделать это sh? 1012 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...