6

データベース内のすべての行に対してバッチ操作を実行しています。これには、すべてのモデルを選択し、それに何かをすることが含まれます。これをチャンクに分割し、チャンクごとに実行するのは理にかなっています。

便利なので、現在Paginatorを使用しています。これは、値を順番にページングできるように、値を並べ替える必要があることを意味します。orderこれにより、 and句を持つSQLステートメントが生成さlimitれ、チャンクごとにPostgresがテーブル全体を並べ替えている可能性があると思います(ただし、内部に関する知識があるとは言えません)。私が知っているのは、データベースのCPUが約50%であり、それを実行するには高すぎると思いますselect

RDMBS / CPUに適した方法でテーブル全体を反復処理するための最良の方法は何ですか?

バッチ操作中にデータベースの内容が変更されないと仮定します。

4

2 に答える 2

6

説明から、処理する行のソート順は実際には気になりません。テーブルに主キーがある場合(これは私が期待していることです!)、この大まかなパーティショニング方法の方がはるかに高速です。

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;
...

すべての行を並べ替える必要があり、オフセットが大きくなりテーブルが大きくなるとパフォーマンスがさらに低下するため、これは一般に遅くなります。

于 2012-01-03T03:24:00.260 に答える
5

BETWEEN次のコードは、Django QuerySetに対して上記のErwinの回答を(を使用して)実装します。

任意のDjangoQuerySetに対してこれを行うユーティリティ関数は次のとおりです。betweenデフォルトでは、「id」が句に使用するのに適したフィールドであると想定しています。

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

余分なネストされたループを必要としないようにインターフェースを変更することもできますが、次のことができますfor item in chunked_queryset(qs)

def chunked_queryset(qs, batch_size, index='id'):
    """
    Yields a queryset that will be evaluated in batches
    """
    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)}
        for item in qs.filter(**filter_args):
            yield item
于 2016-09-17T18:54:31.347 に答える