2012-04-20 22 views
6

Tôi đang cố xóa 267 bản ghi trong số khoảng 40 triệu. Truy vấn trông giống như:"Tổng số khóa vượt quá kích thước bảng khóa" Xóa 267 Bản ghi

delete from pricedata 
where 
pricedate > '20120413' 

giá là trường char(8).

tôi biết về việc điều chỉnh innodb_buffer_pool_size, nhưng nếu tôi có thể làm

select from pricedata 
where 
pricedate > '20120413' 

và nhận được 267 hồ sơ (và đó là tất cả có), không có lỗi, tại sao nó nghẹt thở trên xóa?

Và nếu điều chỉnh innodb_buffer_pool_size không hoạt động, tôi nên làm gì?

+2

Bạn có chỉ mục trên 'giá trị'? – Quassnoi

+0

Tôi đặt một cái vào ngay sau khi đăng (và trước khi phản hồi của bạn, thật không may). Nó vẫn đang xây dựng! Điều tốt là một ngày cuối tuần ... – davej

Trả lời

2

Điều gì đã làm: thay đổi innodb_buffer_pool_size thành 256M (xem nhận xét trong bình luận gốc của Quassnoi).

7

Dường như bạn không có chỉ mục trên pricedate (hoặc MySQL không sử dụng chỉ mục này vì một số lý do).

Với REPEATABLE READ (mức cách ly giao dịch mặc định), InnoDB địa điểm đã chia sẻ khóa trên hồ sơ đã đọc và lọc theo truy vấn và có vẻ như bạn không có đủ không gian cho khóa 40M.

Để làm việc xung quanh vấn đề này sử dụng bất kỳ các giải pháp này:

  1. Tạo chỉ số trên pricedate nếu nó không có ở đó (có thể mất thời gian)

  2. Phá truy vấn của bạn thành những phần nhỏ hơn:

    DELETE 
    FROM pricedata 
    WHERE pricedate > '20120413' 
         AND id BETWEEN 1 AND 1000000 
    
    DELETE 
    FROM pricedata 
    WHERE pricedate > '20120413' 
         AND id BETWEEN 1000001 AND 2000000 
    

    vv (thay đổi các phạm vi id nếu cần). Lưu ý rằng mỗi câu lệnh phải được chạy trong giao dịch của chính nó (đừng quên cam kết sau mỗi câu lệnh nếu AUTOCOMMIT bị tắt).

  3. Chạy truy vấn DELETE với READ COMMITTED mức cách ly giao dịch. Nó sẽ làm cho InnoDB khóa nâng từ hồ sơ ngay khi chúng được đọc. Điều này sẽ không hoạt động nếu bạn đang sử dụng chế độ đăng nhập nhị phân trong chế độ câu lệnh và không cho phép truy vấn không an toàn cho binlog (đây là cài đặt mặc định).

+0

Tuyệt vời, cảm ơn bạn. Tôi sẽ cố gắng và báo cáo lại. – davej

+0

@davej: trên một bảng có kích thước này sẽ mất ít nhất là '40' phút, và nếu bạn có nhiều cột (hoặc dài' TEXT' hoặc cột nhị phân) điều này làm cho mất giờ và ngày. Nếu bạn không sử dụng 'InnoDB 1.1' (hoặc plugin), nó sẽ mất nhiều hơn. – Quassnoi

+0

Cảm ơn bạn một lần nữa vì thông tin đầu vào của bạn. Có tổng cộng 8 trường; "có giá trị", chỉ là số không. Có, tho, 2 BIGINT (20), vì vậy tôi không biết nó sẽ ảnh hưởng như thế nào. Vì nó là, tôi phải ra ngoài vài giờ, nên tôi sẽ để nó chạy. Điều tốt là trên một thử nghiệm db! – davej

4

(Một câu trả lời muộn, nhưng alwayx tốt để có nó khi người ta tìm thấy vấn đề này trong google)

Một giải pháp mà không cần phải thay đổi innodb_buffer_pool_size hoặc tạo ra một chỉ số có thể để hạn chế số lượng hàng bị xóa .

Vì vậy, trong trường hợp của bạn là DELETE from pricedata where pricedata > '20120413' limit 100; chẳng hạn. Điều này sẽ xóa 100 hàng và để lại 167 đằng sau. Vì vậy, bạn có thể chạy cùng một truy vấn một lần nữa và xóa một truy vấn khác 100. Đối với lần cuối cùng 67 nó phức tạp ... khi số lượng hàng còn lại trong cơ sở dữ liệu nhỏ hơn giới hạn cho trước, bạn sẽ lại kết thúc với lỗi về số khóa. Có thể vì máy chủ sẽ tìm kiếm nhiều hàng phù hợp hơn để lấp đầy 100. Trong trường hợp này, hãy sử dụng limit 67 để xóa phần cuối cùng. (Ofcourse, bạn có thể sử dụng limit 267 đã có trong đầu cũng)

Và đối với những ai muốn kịch bản ... một ví dụ tốt đẹp tôi đã sử dụng trong một kịch bản bash để dọn dẹp dữ liệu cũ:

# Count the number of rows left to be deleted 
    QUERY="select count(*) from pricedata where pricedata > '20120413';" 
    AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1` 
    ERROR=0 
    while [ ${AMOUNT} -gt 0 -a ${ERROR} -eq 0 ] 
    do 
     ${LOGGER} " ${AMOUNT} rows left to delete" 
     if [ ${AMOUNT} -lt 1000 ] 
     then 
     LIMIT=${AMOUNT} 
     else 
     LIMIT=1000 
     fi 
     QUERY="delete low_priority from pricedata where pricedata > '20120413' limit ${LIMIT};" 
     ${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} 
     STATUS=$? 
     if [ ${STATUS} -ne 0 ] 
     then 
     ${LOGGER} "Cleanup failed for ${TABLE}" 
     ERROR=1 
     fi 
     QUERY="select count(*) from pricedata where pricedata > '20120413';" 
     AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1` 
    done 
Các vấn đề liên quan