簡體   English   中英

如何提高大型 InnoDB 表的 DELETE FROM 性能?

[英]How can I improve DELETE FROM performance on large InnoDB tables?

我有一個相當大的 InnoDB 表,其中包含大約 1000 萬行(並且計數,預計將成為該大小的 20 倍)。 每行都不是那么大(平均 131 B),但有時我不得不刪除其中的一大塊,這需要很長時間。 這是表結構:

 CREATE TABLE `problematic_table` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `taxid` int(10) unsigned NOT NULL,
    `blastdb_path` varchar(255) NOT NULL,
    `query` char(32) NOT NULL,
    `target` int(10) unsigned NOT NULL,
    `score` double NOT NULL,
    `evalue` varchar(100) NOT NULL,
    `log_evalue` double NOT NULL DEFAULT '-999',
    `start` int(10) unsigned DEFAULT NULL,
    `end` int(10) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `taxid` (`taxid`),
    KEY `query` (`query`),
    KEY `target` (`target`),
    KEY `log_evalue` (`log_evalue`)
) ENGINE=InnoDB AUTO_INCREMENT=7888676 DEFAULT CHARSET=latin1;

從表中刪除大塊的查詢是這樣的:

DELETE FROM problematic_table WHERE problematic_table.taxid = '57';

像這樣的查詢只花了將近一個小時才完成。 我可以想象索引重寫開銷使這些查詢非常慢。

我正在開發一個將在預先存在的數據庫上運行的應用程序。 我很可能無法控制服務器變量,除非我對它們進行強制性更改(我不想這樣做),所以恐怕改變這些的建議價值不大。

我試圖INSERT ... SELECT那些我不想刪除到臨時表中的行,然后刪除其余的行,但是隨着刪除與保留的比率向保留轉變,這是不再是一個有用的解決方案。

這是一個表,將來可能會看到頻繁的INSERTSELECT ,但沒有UPDATE 基本上,它是一個需要不時刪除部分內容的日志記錄和參考表。

我可以通過限制它們的長度來改進我在這個表上的索引嗎? 切換到 MyISAM 是否有幫助,它在交易期間支持DISABLE KEYS 我還能嘗試什么來提高DELETE性能?

編輯:一個這樣的刪除將是大約一百萬行。

我有一個類似的場景,有一個包含 200 萬行的表和一個刪除語句,它應該刪除大約 10 萬行 - 大約需要 10 分鍾。

檢查配置后,我發現 MySQL Server 以默認的innodb_buffer_pool_size = 8 MB (!) 運行。

使用innodb_buffer_pool_size = 1.5GB 重啟后,同樣的場景需要 10 秒。

因此,如果“表的重新排序”是否適合 buffer_pool,似乎存在依賴性。

此解決方案一旦完成即可提供更好的性能,但該過程可能需要一些時間來實施。

可以添加一個新的BIT列,並將“活動”默認為TRUE ,“非活動”默認為FALSE 如果這還不夠狀態,您可以使用具有 256 個可能值的TINYINT

添加這個新列可能需要很長時間,但是一旦它結束,您的更新應該會更快,只要您像刪除刪除一樣在PRIMARY執行它並且不索引這個新列。

InnoDB 在像您這樣龐大的表上進行DELETE需要這么長時間的原因是集群索引。 它根據您的PRIMARY對您的表進行物理排序,首先是它找到的UNIQUE ,或者如果找不到PRIMARYUNIQUE可以確定為適當的替代品,因此當刪除一行時,它現在會在物理上重新排序整個表磁盤速度和碎片整理。 所以不是DELETE需要這么長時間; 這是刪除該行后的物理重新排序。

當您創建一個固定寬度的列並更新它而不是刪除時,不需要在巨大的表中進行物理重新排序,因為行和表本身消耗的空間是恆定的。

在下班時間,可以使用單個DELETE刪除不必要的行。 此操作仍然會很慢,但總體上比刪除單個行要快得多。

我通過使用存儲過程解決了類似的問題,從而將性能提高了數千倍。

我的表有 33M 行和幾個索引,我想刪除 10K 行。 我的數據庫在 Azure 中,無法控制 innodb_buffer_pool_size。

為簡單起見,我創建了一個只有一個主要id字段的表tmp_id

CREATE TABLE `tmp_id` (
    `id` bigint(20) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`)
)

我選擇了我想刪除到tmp_id的一組 id 並delete from my_table where id in (select id from tmp_id); 這並沒有在 12 小時內完成,所以我嘗試在tmp_id只使用一個 ID,花了 25 分鍾。 delete from my_table where id = 1234在幾毫秒內完成,所以我決定嘗試在一個過程中這樣做:

CREATE PROCEDURE `delete_ids_in_tmp`()
BEGIN
    declare finished integer default 0;
    declare v_id bigint(20);
    declare cur1 cursor for select id from tmp_id;
    declare continue handler for not found set finished=1;    
    open cur1;
    igmLoop: loop
        fetch cur1 into v_id;
        if finished = 1 then leave igmLoop; end if;
        delete from problematic_table where id = v_id;
    end loop igmLoop;
    close cur1;
END

現在call delete_ids_in_tmp(); 在不到一分鍾的時間內刪除了所有 10K 行。

    DELETE FROM problematic_table WHERE problematic_table.taxid = '57';

刪除引號,由於出租車是整數,並且在引號中傳遞值使其成為字符串,由於整數和字符串之間的比較,它不選擇索引。

    DELETE FROM problematic_table WHERE problematic_table.taxid = 57;

我有一個包含大約 2 億行的 InnoDB 表,我確實遇到了同樣的問題。 刪除行需要很長時間。

表上有一個主鍵、一個唯一鍵和多個復合索引。

當以較小的塊刪除時,它進行得非常快,因此我決定創建一個存儲過程,該過程可以在有限制的多次迭代中簡單地刪除行。 有點像 Jan Larsen 的回答,但不需要單獨的表格。

這使得在幾分鍾內刪除大塊數據(大約 50 萬行)成為可能。

看起來InnoDB為了能夠回滾錯誤更改而必須進行的事務太大,因此無法放入內存,這導致刪除執行非常糟糕。

程序,流程:

CREATE DEFINER=`root`@`%` PROCEDURE `delete_rows`()
BEGIN
    declare v_max int unsigned default 100;
    declare v_counter int unsigned default 1;

        while v_counter < v_max do
            DELETE from items where a = 'A' AND b = 'B' AND c = 'C' LIMIT 10000;
            set v_counter=v_counter+1;
        end while;
END

然后通過以下方式調用它:

CALL delete_rows();

where 語句匹配以 a,b,c-columns 開頭的復合索引,我認為這很重要,這樣 MySQL 就不必進行全表掃描來匹配行。

暫無
暫無

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

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