[英]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行時,一次執行的行太多了,我仍然有數百萬個非零值需要更新。
那我還有什么可能性呢?
delete from CountA
來重置計數,並且可以使用針對主表的內部delete from CountA
來檢索計數。 這將減慢我對單個計數的更新,因為在有條件地更新或在CountA表中插入一行之前,我必須從主表獲取ID。 不太好,但是我會考慮的。 更新:在已接受的響應的幫助下,我現在有了一個批處理實現,該實現可以在大約五分鍾內完成。 雖然我寧願批處理就沒有必要,直到一個更直接的解決方案來臨時它似乎是。 如果它可以幫助下一個人迷失這個問題,這是我相關的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 JOIN
和IFNULL(col, 0)
以獲得值或0。 計划X(非入門級)
為列建立索引會很麻煩-當您更新已索引的列時,必須同時更改數據和索引。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.