Mysql Multiple Join Perfromance с Django Rest Framework - PullRequest
0 голосов
/ 26 декабря 2018

Я боролся с проблемой, которую, я думаю, каждый делает в какой-то момент.У меня пока есть небольшая база данных из 150 тыс. Товаров.(Увеличивается, когда я пишу это.)

Я использую DRF для API и борюсь с производительностью категорий, в которых у меня много продуктов.

IE у меня естькатегория называется платья, которые имеют 34633 продуктов.Как устроена моя база данных, у меня есть пара отношений под ней.

Продукт имеет категории, атрибуты, цвет, размеры, сопутствующие товары M2M

Запросы

Количество запросов 809.83 мс

SELECT COUNT(*) 
FROM (
  SELECT DISTINCT `catalog_products`.`id` AS Col1 
  FROM `catalog_products` 
  INNER JOIN `catalog_products_category` ON (`catalog_products`.`id` =
                           `catalog_products_category`.`products_id`) 
  WHERE (`catalog_products`.`deleted` = 0 
     AND `catalog_products`.`in_stock` = 1 
     AND `catalog_products_category`.`categories_id` = 183)
) subquery

Результат запроса 2139.52 мс

SELECT DISTINCT `catalog_products`.`id`, `catalog_products`.`sku`,
  `catalog_products`.`title`, `catalog_products`.`old_price`,
  `catalog_products`.`price`, `catalog_products`.`sale`,
  `catalog_products`.`original_categories`,
  `catalog_products`.`original_conv_color`, `catalog_products`.`original_sizes` 
FROM `catalog_products` 
INNER JOIN `catalog_products_category` ON (`catalog_products`.`id` =
                         `catalog_products_category`.`products_id`) 
WHERE (`catalog_products`.`deleted` = 0 
  AND `catalog_products`.`in_stock` = 1 
  AND `catalog_products_category`.`categories_id` = 183) 
ORDER BY `catalog_products`.`title` ASC LIMIT 48

как вы можетеПосмотрим, сколько времени для запроса, но вот сложная часть, когда я применяю фильтры IE. Я выбираю цветной фильтр, и время начала уменьшаться.

Запросы с примененными фильтрами

Количество запросов 264.63 мс

SELECT COUNT(*) FROM (
  SELECT DISTINCT `catalog_products`.`id` AS Col1 
  FROM `catalog_products` 
  INNER JOIN `catalog_products_color` ON (`catalog_products`.`id` =
                           `catalog_products_color`.`products_id`) 
  INNER JOIN `catalog_products_category` ON (`catalog_products`.`id` =
                           `catalog_products_category`.`products_id`) 
  INNER JOIN `catalog_sizethrough` ON (`catalog_products`.`id` =
                            `catalog_sizethrough`.`product_id`) 
  WHERE (`catalog_products`.`deleted` = 0 
    AND `catalog_products`.`in_stock` = 1 
    AND `catalog_products_color`.`color_id` = 1 
    AND `catalog_products_category`.`categories_id` = 183 
    AND `catalog_sizethrough`.`size_id` IN (262) 
    AND `catalog_sizethrough`.`stock` = 1)
) subquery

Результат запроса 351.43 мс

SELECT DISTINCT `catalog_products`.`id`, `catalog_products`.`sku`,
  `catalog_products`.`title`, `catalog_products`.`old_price`,
  `catalog_products`.`price`, `catalog_products`.`sale`,
  `catalog_products`.`original_categories`,
  `catalog_products`.`original_conv_color`,
  `catalog_products`.`original_sizes` 
FROM `catalog_products` 
INNER JOIN `catalog_products_color` ON (`catalog_products`.`id` =
                         `catalog_products_color`.`products_id`) 
INNER JOIN `catalog_products_category` ON (`catalog_products`.`id` =
                         `catalog_products_category`.`products_id`) 
INNER JOIN `catalog_sizethrough` ON (`catalog_products`.`id` =
                          `catalog_sizethrough`.`product_id`) 
WHERE (`catalog_products`.`deleted` = 0 
  AND `catalog_products`.`in_stock` = 1 
  AND `catalog_products_color`.`color_id` = 1 
  AND `catalog_products_category`.`categories_id` = 183 
  AND `catalog_sizethrough`.`size_id` IN (262) 
  AND `catalog_sizethrough`.`stock` = 1) 
ORDER BY `catalog_products`.`title` ASC 
LIMIT 48

Я пробовал так много вещейчтобы это исправить, но я не могу это исправить, мне нужно улучшить скорость загрузки моей страницы, но поскольку запрос занимает больше времени, это не так хорошо для пользователя.Я использовал загрузку Eager, поэтому улучшение уже не поможет, если у вас нет дополнений.

Код

Сериализатор

class ProductsListSerializer(serializers.ModelSerializer):

images = ImagesSerializer(many=True, source='get_first_two_images')
related_color = serializers.SerializerMethodField()

def get_related_color(self, obj):
    return obj.related_color.count()

class Meta:

    fields = (
        'id',
        'sku',
        "title",
        "old_price",
        "price",
        "sale",
        "images",
        "original_categories",
        "related_color",
        "original_conv_color",
        "original_sizes",
    )
    model = Products

@staticmethod
def setup_eager_loading(queryset):
    queryset = queryset.only('id', 'sku', 'title', 'old_price', 'price', 'sale', 'original_categories', 'original_conv_color', 'original_sizes').prefetch_related('images', 'related_color')

    return queryset

Просмотр

class ProductsViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Products.objects.all()
permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
filter_backends = (filters.SearchFilter, DjangoFilterBackend, filters.OrderingFilter, CustomFilter, SizeFilter)
filter_fields = ('slug', 'code', 'sku', 'color', 'attributes', 'category', 'original_color')
min_max_fields = ('price', 'sale')
search_fields = ('title', 'original_color', 'original_categories', 'original_conv_color', 'original_sizes')
ordering_fields = ('sale', 'price', 'created_at')
pagination_class = StandardResultsSetPagination

def get_queryset(self):
    if self.action == 'list':
        queryset = self.get_serializer_class().setup_eager_loading(self.queryset.filter(deleted=0,in_stock=1))
        return queryset
    return self.queryset

def get_serializer_class(self):
    if self.action == 'list':
        return ProductsListSerializer
    if self.action == 'retrieve':
        return ProductsSerializer
    return ProductsSerializer

Ответы [ 2 ]

0 голосов
/ 26 декабря 2018

Оптимизация ваших запросов кажется вполне возможной - если честно.Я уверен, что это вопрос использования правильных индексов.

Я не знаю всех деталей избирательности столбцов в каждой таблице (это очень важно), поэтому я предположу, что, например,categories_id = 183 фактически отфильтрует большинство строк;Я могу ошибатьсяЯ предполагаю аналогичную избирательность для всех связанных таблиц (catalog_products_category, catalog_products_color и catalog_sizethrough).

В таком случае я бы порекомендовал следующие индексы для ускорения поиска:

create index ix1 on catalog_products_category (categories_id, products_id);
create index ix2 on catalog_products_color (color_id, products_id);
create index ix3 on catalog_sizethrough (size_id, stock, products_id);
create index ix4 on catalog_products (deleted, in_stock, id);

Попробуйте их.Если ваши запросы все еще медленные, пожалуйста, опубликуйте план выполнения самого медленного, чтобы интерпретировать его.

0 голосов
/ 26 декабря 2018

просто предложение, обращающееся к коду ваших запросов, убедитесь, что у вас есть правильный составной индекс для

table catalog_products  index  on (deleted, in_stock, id )
table catalog_products_category index  on  ( categories_id, products_id, id  )

и избегайте использования без кода () вокруг кода ..

SELECT COUNT(*) 
FROM (
  SELECT DISTINCT `catalog_products`.`id` AS Col1 
  FROM `catalog_products` 
  INNER JOIN `catalog_products_category` 
    ON `catalog_products`.`id` = `catalog_products_category`.`products_id` 
  WHERE `catalog_products`.`deleted` = 0 
  AND `catalog_products`.`in_stock` = 1 
  AND `catalog_products_category`.`categories_id` = 183
  ) subquery


SELECT DISTINCT `catalog_products`.`id`
  , `catalog_products`.`sku`
  , `catalog_products`.`title`
  , `catalog_products`.`old_price`
  , `catalog_products`.`price`
  , `catalog_products`.`sale`
  , `catalog_products`.`original_categories`
  , `catalog_products`.`original_conv_color`
  , `catalog_products`.`original_sizes` 
FROM `catalog_products` 
INNER JOIN `catalog_products_category` 
  ON `catalog_products`.`id` = `catalog_products_category`.`products_id`
WHERE `catalog_products`.`deleted` = 0 
AND `catalog_products`.`in_stock` = 1 
AND `catalog_products_category`.`categories_id` = 183 
ORDER BY `catalog_products`.`title` ASC LIMIT 48

и последнее предложение запомнитеэто упорядочение имеет довольно существенное влияние на упорядочение, и сам факт введения предела для результатов подразумевает, однако, что все строки должны быть выбраны, упорядочены и окончательно извлечены только на основе числа, указанного в пределе.

...