2012-01-03 44 views
5

Tôi đang chạy một thao tác lô trên tất cả các hàng trong cơ sở dữ liệu. Điều này liên quan đến việc chọn từng mô hình đơn lẻ và làm điều gì đó với nó. Nó làm cho tinh thần để chia này thành từng phần và làm nó chunk bởi chunk.Cách tốt nhất để xử lý cơ sở dữ liệu trong các khối với Django QuerySet?

Tôi hiện đang sử dụng Trình tạo trang, vì nó thuận tiện. Điều này có nghĩa là tôi cần đặt hàng trên các giá trị để chúng có thể được phân trang theo thứ tự. Điều này tạo ra các câu lệnh SQL có các mệnh đề orderlimit và cho mỗi đoạn tôi nghĩ Postgres có thể sắp xếp toàn bộ bảng (mặc dù tôi không thể yêu cầu có bất kỳ kiến ​​thức nào về nội bộ). Tất cả tôi biết là cơ sở dữ liệu là khoảng 50% CPU và tôi nghĩ rằng đó là quá cao chỉ để làm select s.

Cách tốt nhất để lặp lại toàn bộ bảng theo cách RDMBS/CPU thân thiện là gì?

Giả sử rằng nội dung của cơ sở dữ liệu không thay đổi trong quá trình thực hiện hàng loạt.

Trả lời

5

Từ mô tả của bạn, bạn thực sự không quan tâm đến đơn đặt hàng sắp xếp của các hàng bạn xử lý. Nếu bạn có từ khóa chính trong bảng của bạn (mà tôi mong đợi!), Phương pháp thô này của phân vùng sẽ nhanh hơn nhiều:

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

này thực hiện tương tự cho bất kỳ bù đắp và (hầu như) tương tự cho bất kỳ kích thước của bảng. Lấy min và max của chính và phân vùng chính của bạn cho phù hợp:

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

Trái ngược với:

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

này nói chung là chậm hơn bởi vì tất cả các hàng đã được sắp xếp và làm giảm hiệu suất bổ sung với chênh lệch cao hơn và các bảng lớn hơn.

+0

này giả định rằng các hồ sơ được trả về theo thứ tự mà không có một 'khoản sort'. Điều này có đúng không? Ngoài ra, nếu tôi có một sắp xếp mặc định trong lớp 'Meta' của tôi, bằng cách nào đó tôi có thể loại bỏ nó cho truy vấn? – Joe

+0

@Joe: Về cơ bản bạn nhận được các bản ghi giống nhau, nhưng không được phân loại. Nếu có khoảng trống trong không gian ID của bạn, số lượng bản ghi được trả về có thể ít hơn mong đợi cho mọi cuộc gọi. Trong khi với LIMIT/OFFSET bạn nhận được một số cố định của các hàng được sắp xếp (ngoại trừ cuộc gọi cuối cùng cho mỗi bảng). Tôi không làm thế nào để xử lý lớp 'Meta', nhưng bạn * cần * để sắp xếp các hàng của bạn cho LIMIT/OFFSET. –

+0

Erwin, tôi thực sự xin lỗi tôi đã không đọc câu trả lời của bạn một cách chính xác. Bạn có chắc chắn điều này nhanh hơn không? mệnh đề 'between' chắc chắn chỉ hoạt động nếu một trong hai id đã được sắp xếp, hoặc nó thực hiện quét toàn bộ bảng mỗi lần? – Joe

2

Các mã sau thực hiện câu trả lời Erwin của trên (sử dụng BETWEEN) cho một Django QuerySet:

Một chức năng tiện ích sẽ làm việc này cho một tùy Django QuerySet là như sau. Nó mặc định là giả định 'id' là một trường thích hợp để sử dụng cho mệnh đề 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) 

Nó sẽ được sử dụng như thế này:

for chunk in chunked_queryset(SomeModel.objects.all(), 20): 
    # `chunk` is a queryset 
    for item in chunk: 
     # `item` is a SomeModel instance 
     pass 
Các vấn đề liên quan