InnoDB DELETE performansı nasıl geliştirilir?


9

Bu denetim tablosu var (veritabanımdaki herhangi bir tabloda eylemleri izler):

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

ve eski öğeleri arşivlemeye başlamam gerekiyor. Tablo yaklaşık 50 milyon satıra ulaştı, bu yüzden satırları silmenin en hızlı yolu bir kerede bir tabloyu silmekti (dayalı tableName).

Bu oldukça iyi çalışıyor, ancak yazma ağır olan bazı tablolarda tamamlanmayacak. deleteSorgum, tupleID / tableName birleşiminde ilişkili eylemi olan tüm öğeleri siler :

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

Bu sunucum üzerinde 3 gün boyunca çalışmasına izin ve asla en büyük tablo için tamamlandı. Açıklama çıkışı (silme işlemini seçmek için değiştirirsem:

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

Yani 4 milyon satırın silinmesi 3 gün sürmemeli diye düşünüyorum. Benim innodb_buffer_pool_size 3GB olarak ayarlanmış ve sunucu one_file_per_table kullanmak için ayarlanmamış. InnoDB silme performansını nasıl artırabilirim? (Mac OSX'te MySQL 5.1.43'ü çalıştırma)

Yanıtlar:


11

Verileri toplu olarak silebilirsiniz.

SQL Server'da sözdizimi delete top Xbir tablodaki satırlardır. Daha sonra, her toplu iş için bir işlemle (elbette birden fazla ifadeniz varsa) bir döngüde yaparsınız, böylece işlemleri kısa tutmak ve kilitleri sadece kısa süreler için korumak.

MySQL sözdiziminde: DELETE FROM userTable LIMIT 1000

Bununla ilgili kısıtlamalar vardır ( LIMITörneğin, birleştirmelerle silme işlemlerinde kullanılamaz ), ancak bu durumda bu şekilde yapabilirsiniz.

Kullanarak ek bir tehlike var LIMITolan DELETEbu çoğaltma gelince; silinen satırlar bazen slave üzerinde master'da silindiği sırayla silinmez.


6

Geçici tablo yaklaşımı kullanmayı deneyin. Bunun gibi bir şey deneyin:

Aşama 1) CREATE TABLE track_table_new LIKE track_table;

Adım 2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

Aşama 3) ALTER TABLE track_table RENAME track_table_old;

Adım 4) ALTER TABLE track_table_new RENAME track_table;

Adım 5) DROP TABLE track_table_old;

Adım 2'de tuple alanını dahil etmedim. Lütfen bunun istenen efekti üretip üretmediğine bakın. İstediğiniz buysa, demet alanını başka nedenlerle kullanmadığınız sürece demet alanını tamamen kapatmak isteyebilirsiniz.


Bu ilginç bir çözüm. Tablodaki tuple alanına ihtiyacım var. tableName / tupleID, günlüğe kaydedilen tablonun tanımsız bir yabancı anahtarıdır. Tanımsız çünkü yakın zamana kadar bu tablo yabancı anahtarları desteklemeyen MyISAM'dı.
Derek Downey

1

İstenmeyen satırların toplu olarak silinmesi, diğer işlemlerin uygulanabilir olmasını sağlamalıdır. Ancak işlem silme işleminizin koşulları vardır, bu nedenle koşullar üzerinde sütunlarda uygun bir dizin bulunduğundan emin olun.

MySQL gevşek endeksi taramanın tam işlevini desteklemediğinden, sizin için sırasını ayarlamak deneyebilir KEY actionDate (action, date_insert)için KEY actionDate (date_insert, action). 'Date_insert' önekiyle, MySQL bu dizini datetime koşulunuzdan önceki satırları taramak için kullanmalıdır.

Böyle bir dizinde SQL'i şöyle yazabilirsiniz:

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch

1
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-Fist, key_len'i açıkla o kadar büyük => boyutu mümkün olduğunca küçük düşürmeniz gerekiyor. Sorgunuz için en iyi yolun, veri alanının eylem alanını char (12) 'den tinyint'e değiştirmek olduğunu düşünüyorum, bu nedenle veri eşleme şöyle görünür:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

tablename yerine table_id öğesini de değiştirebilirsiniz. en iyi performans için DDL şunları yapabilir:

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

böylece sorgu çalışan şöyle görünebilir:

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

Ama en hızlı yol bölümü kullanmaktı. böylece bölümü bırakabilirsiniz. Şu anda, masamın yaklaşık 40mil satır var. ve saatlik güncelleme (her seferinde 400 bin satır güncelleme) ve curr_date bölümünü bırakıp verileri tabloya yeniden yükleyebilirim. bırakma komutu çok hızlı (<100ms). Umarım bu yardım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.