Создание характеристик для большого количества товаров
Доброе время суток!
Задача: создать в базе данных значения характеристик (10-15) для большого количества (15k+) товаров.
Проблема состоит в слишком долгой работе функции add_item_specs, которая добавляет в БД значения характеристик. Немного сокращенный код выглядит так:
# models.py
class Item(models.Model):
brand = models.CharField(max_length=250, blank=True, default="")
code = models.CharField(max_length=10, db_index=True, blank=True)
...
class SpecItem(models.Model):
"""
Характеристики товара
"""
code = models.CharField(max_length=20, verbose_name="Код характеристики", unique=True)
title = models.CharField(max_length=200, verbose_name='Наименование характеристики')
...
class SpecValue(models.Model):
"""
Занчение характеристики
"""
item = models.ForeignKey('Item', on_delete=models.CASCADE, related_name='spec_value')
spec_item = models.ForeignKey('SpecItem', on_delete=models.CASCADE, related_name='value')
value = models.CharField(max_length=150, verbose_name="Значение", null=True)
# services.py
def add_item_specs(items: dict):
# ВЫБИРАЮ ТОВАРЫ ИЗ БД ДЛЯ КОТОРЫХ БУДУ ДОБАВлЯТЬ ХАР-КИ
items_in_db = Item.objects.filter(code__in=items.keys())
count_items = items_in_db.count()
# ДЛЯ ПРОПУСКА УЖЕ СУЩЕСТВУЮЩИХ ЗНАЧЕНИЙ ХАР-К
exists_spec_values = SpecValue.objects.all().values_list('item', 'spec_item__code')
all_specs = SpecItem.objects.all()
spec_objects = list()
for item in items_in_db:
for spec in items[item.code]["Features"]:
if (item.pk, spec['FeatureCode']) in exists_spec_values:
# ПРОПУСКАЮ СУЩЕСТВУЮЩИЕ
continue
spec_item = all_specs.get(code=spec['FeatureCode'])
spec_value = SpecValue(
item=item,
spec_item=spec_item,
value=spec['FeatureValue'] if spec['FeatureValue'] is not None else ""
)
spec_objects.append(spec_value)
count_items -= 1
# Данное сообщение выводится примерно 1-2 раза в секунду
print(f"Добавлены характеристики для {item.title} \n\t>> Осталось => {count_items}")
print(f'SpecValue.objects.bulk_create(spec_objects) => {len(spec_objects)=} значений характеристик заливается в БД')
SpecValue.objects.bulk_create(spec_objects)
То есть, еще до записи в БД код работает почти 2,5 часа. Есть ли возможность ускорить данный код?
Ответы (2 шт):
Вы можете попробовать разбить количество товаров на куски по 3-5 к и делать всё это с помощью Threading
Примерно так стал выглядеть мой код после того как я пошел по пути подсказанным Антоном.
class CreateObjects(Thread):
def __init__(self, items_butch, data, all_specs, exists_spec_values, *args, **kwargs):
super().__init__(*args, **kwargs)
self.items_butch = items_butch
self.data = data
self.all_specs = all_specs
self.exists_spec_values = exists_spec_values
self.__result = list()
def run(self):
self.iterate_data()
def get_result(self):
return self.__result
def iterate_data(self):
for item in self.items_butch:
self.__result.extend(self.create_obj(item))
def create_obj(self, item):
return [
SpecValue(
item=item,
spec_item=self.all_specs.get(code=spec['FeatureCode']),
value=spec['FeatureValue'] if spec['FeatureValue'] is not None else ""
)
for spec in self.data[item.code]["Features"]
if (item.pk, spec['FeatureCode']) not in self.exists_spec_values
]
def chunks_generator(lst: list, n: int) -> list:
"""
Создание пачек из списка, по n штук
"""
for i in range(0, len(lst), n):
yield lst[i : i + n]
def add_item_specs(items: dict):
items_in_db = Item.objects.filter(code__in=items.keys())
exists_spec_values = set(SpecValue.objects.all().values_list('item', 'spec_item__code'))
all_specs = SpecItem.objects.all()
spec_objects = list()
threads = list()
for items_butch in chunks_generator(items_in_db, 700):
print(f"Добавляем в обработку список на {len(items_butch)} обьектов ", flush=True)
threads.append(
CreateObjects(
items_butch=items_butch,
data=items,
all_specs=all_specs,
exists_spec_values=exists_spec_values
)
)
for th in threads:
th.start()
print("Ждем создания объектов!", flush=True)
for th in threads:
th.join()
for th in threads:
spec_objects.extend(th.get_result())
print(f'SpecValue.objects.bulk_create(spec_objects) => {len(spec_objects)=} значений характеристик заливается в БД')
SpecValue.objects.bulk_create(spec_objects)
Честно говоря, я думал над этим ранее, но не реализовал, так как не мог даже представить что будет такой буст! В итоге то что ранее обрабатывалось за 2.5 - 3 часа (и это еще до записи в БД) прошло за 5.5 минут вместе с внесением в базу данных!!!
Спасибо, задача решена!