簡體   English   中英

批量更新到大型InnoDB表中的未索引列

[英]bulk update to an unindexed column in a large InnoDB table

我在Mysql 5.1數據庫中有一個InnoDB表,該表大約有2700萬行。 該表具有三個我希望能夠定期全局索引重置為“ 0”的未索引的mediumint unsigned列。 例如:

update myTable set countA = 0;

這個非常簡單的更新查詢正在解決InnoDB的行級鎖定問題。 鎖定過多的行后,更新查詢將失敗,並記錄錯誤:

ERROR 1206 (HY000): The total number of locks exceeds the lock table size

問題在於,使用如此大的表,單個行鎖的數量已超過為存儲鎖分配的空間。

我發現了一些有關如何處理此問題的建議:

鎖定整個表以關閉行鎖定
這似乎是最好,最干凈的解決方案,並且在這些不常用的操作過程中,這個特殊的表被鎖定了幾分鍾沒有問題。 問題是,給定的解決方案實際上不適用於我。 也許它可以用於舊版本的Mysql?

增加鎖定緩沖區的大小
通過增加Mysql變量innodb_buffer_pool_size的值,我們可以為行鎖innodb_buffer_pool_size更多空間。 我對這種解決方案感到非常不舒服,因為即使我可以分配足夠的空間,但隨着表的增長,我都會為失敗做准備。 同樣,這似乎是一個糟糕的設置,需要創建千兆字節的無用鎖。

索引受影響的列 (請參閱注釋)
如果我們正在對相應索引支持的單個列進行批量更新,那么InnoDB可以避免鎖定所有行。 通過使用索引,它只能鎖定受影響的行。 我實際上進行了嘗試,但是發現管理這三個索引使我的增量更新慢得多。 由於我將有數千萬的更新查詢針對需要重置計數的每個實例調整這三個計數,因此我不想犧牲增量更新的效率。

批量更新列
源文檔將其描述為一種變通方法,但我發現它在一定程度上非常有效:

update myTable set countA = 0 where countA != 0 limit 500000;

通過重復執行此操作直到受影響的行數小於指定的limit ,所有行都將得到更新。 對於我來說,這種解決方案在特別大的表上不可行,因為一次迭代可以更新的行數急劇下降,因為Mysql必須進一步尋找匹配的行。 到更新1,000行時,一次執行的行太多了,我仍然有數百萬個非零值需要更新。

那我還有什么可能性呢?

  1. 停止使用InnoDB:這將需要對我當前的流程進行其他重組,但這是我要考慮的事情。
  2. 將計數列移出主表:如果我有CountA表,則可以通過使用delete from CountA來重置計數,並且可以使用針對主表的內部delete from CountA來檢索計數。 這將減慢我對單個計數的更新,因為在有條件地更新或在CountA表中插入一行之前,我必須從主表獲取ID。 不太好,但是我會考慮的。
  3. 還有什么既是干凈的解決方案,又可以期望在我的桌子上合理增長的解決方案?

更新:在已接受的響應的幫助下,我現在有了一個批處理實現,該實現可以在大約五分鍾內完成。 雖然我寧願批處理就沒有必要,直到一個更直接的解決方案來臨時它似乎是。 如果它可以幫助下一個人迷失這個問題,這是我相關的Java JDBC代碼。 (建議您閱讀接受的答案鏈接的博客文章。)

    int batchsize = 10_000;
    PreparedStatement pstmt = connection.prepareStatement
            ("UPDATE tableName SET countA = 0, countB = 0, countC = 0 "
                       + "WHERE id BETWEEN ? AND ?");
    for (int left = 0; left < maxId; left += batchsize) {
        pstmt.setInt(1, left + 1);
        pstmt.setInt(2, left + batchsize);
        pstmt.executeUpdate();
    }
    pstmt.close();

計划A

我喜歡分塊。 但是,您的代碼草圖不是很有效。 添加OFFSET無濟於事。 取而代之的是, 請參閱我的博客,了解如何仔細地瀏覽表格 即找到“下一個” 100-1000行; 執行UPDATE ; 環。 (注意:每個塊應該是自己的事務。)

“查找下N行並記住您離開的位置”的技術取決於PRIMARY KEY 我的博客涵蓋了大多數情況(數字,字符串,稀疏等)。 (該博客討論了DELETE ,但應該易於適應UPDATE 。)

InnoDB對於分塊很有用,因為PRIMARY KEY是集群的。 因此,每個塊將必須讀取最少數量的塊。

計划B

使用並行表(“將計數列從主表中移出”)可能是一個好主意,因為要觸摸的磁盤塊數量會更少,因此可以類似於計划A,但速度更快。 使用相同的PRIMARY KEY (不使用AUTO_INCREMENT )。

C計划

(1)並行表(如Plan B),再加上(2)缺少的行表示值= 0。 然后,通過TRUNCATE TABLE實現清除(與Plan A不同)。 由於您有三列需要清除,因此規則是

  • 當任何值更改為非零時,請確保該行存在於並行表中,並根據需要設置該值(其他值加零)。 可能是INSERT ... ON DUPLICATE KEY UPDATE...是最佳的。
  • 查找值( SELECT )時,請執行LEFT JOINIFNULL(col, 0)以獲得值或0。

計划X(非入門級)

為列建立索引會很麻煩-當您更新已索引的列時,必須同時更改數據和索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM