Лучший способ обработать базу данных кусками с помощью Django QuerySet? - PullRequest
5 голосов
/ 03 января 2012

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

В настоящее время я использую Paginator, потому что это удобно. Это означает, что мне нужно упорядочить значения, чтобы их можно было просматривать по порядку. Это генерирует операторы SQL, которые имеют предложения order и limit, и для каждого куска я думаю, что Postgres может сортировать всю таблицу (хотя я не могу утверждать, что у меня есть какие-либо знания о внутренностях). Все, что я знаю, это то, что база данных находится примерно на 50% ЦП, и я думаю, что это слишком высоко, чтобы делать select с.

Каков наилучший способ перебора всей таблицы с использованием RDMBS / CPU-friendly?

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

Ответы [ 2 ]

5 голосов
/ 03 января 2012

Из вашего описания вы на самом деле не заботитесь о порядке сортировки строк, которые вы обрабатываете.Если у вас есть первичные ключи в ваших таблицах (что я и ожидаю!), Этот грубый метод разбиения будет намного быстрее :

SELECT * FROM tbl WHERE id BETWEEN 0    AND 1000;
SELECT * FROM tbl WHERE id BETWEEN 1001 AND 2000;
...

. То же самое выполняется для любого смещения и (почти) одинаково для любого размера стола.Получите минимальный и максимальный значения вашего первичного ключа и раздела соответственно:

SELECT min(id), max(id) from tbl; -- then divide in suitable chunks

В отличие от:

SELECT * FROM tbl ORDER BY id LIMIT 1000;
SELECT * FROM tbl ORDER BY id LIMIT 1000 OFFSET 1000;
...

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

3 голосов
/ 17 сентября 2016

Следующий код реализует ответ Эрвина выше (используя BETWEEN) для Django QuerySet:

Утилита, которая сделает это для произвольного Django QuerySet, выглядит следующим образом.По умолчанию предполагается, что 'id' является подходящим полем для использования в предложении between.

def chunked_queryset(qs, batch_size, index='id'):
    """
    Yields a queryset split into batches of maximum size 'batch_size'.
    Any ordering on the queryset is discarded.
    """
    qs = qs.order_by()  # clear ordering
    min_max = qs.aggregate(min=models.Min(index), max=models.Max(index))
    min_id, max_id = min_max['min'], min_max['max']
    for i in range(min_id, max_id + 1, batch_size):
        filter_args = {'{0}__range'.format(index): (i, i + batch_size - 1)}
        yield qs.filter(**filter_args)

Оно будет использоваться следующим образом:

for chunk in chunked_queryset(SomeModel.objects.all(), 20):
    # `chunk` is a queryset
    for item in chunk:
        # `item` is a SomeModel instance
        pass
...