InnoDB içe aktarma performansı


10

Ben yaklaşık 10 Milyon satır (veya 7GB) oluşan oldukça büyük bir InnoDB-Tablo toplu ithalat ile mücadele ediyorum (ki benim için şimdiye kadar çalıştığım en büyük tablo).

Inno'nun ithalat hızını nasıl artıracağımı araştırdım ve şu an kurulumum şöyle görünüyor:

/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8


import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;

Veriler bir CSVdosyada sağlanır.
Şu anda ayarlarımı her biri 2 milyon, 3 milyon, ... sıralı daha küçük 'test dökümleriyle' test ediyorum ve time import_script.shperformansı karşılaştırmak için kullanıyorum .

Dezavantajı sadece genel bir çalışma süresi olsun, bu yüzden sonuç almak için tam içe aktarma işleminin bitmesini beklemem.

Şimdiye kadarki sonuçlarım:

  • 10000 satır: <1 saniye
  • 100000 satır: 10 saniye
  • 300000 satır: 40 saniye
  • 2 milyon sıra: 18 dakika
  • 3 milyon satır: 26 dakika
  • 4 milyon satır: (2 saat sonra iptal edildi)

Görünüşe göre 'yemek kitabı' çözümü yok ve kişi kendi ayarlarının en uygun karışımını bulmalı.
Kurulumumda neyi değiştireceğime dair önerilerin yanı sıra, içe aktarma sürecini nasıl daha iyi değerlendirebileceğimi / neler olduğunu ve darboğazın nerede olabileceğini daha fazla kavrayışımı nasıl artırabileceğimi de çok takdir ediyorum.
Değiştirdiğim ayarlar için belgeleri okumaya çalıştım, ancak yine de herhangi bir yan etkinin farkında değilim ve hatta performansı kötü seçilmiş bir değerle düşürebilirsem.

Şimdilik, MyISAMiçe aktarma ve değiştirme sırasında tablo motorunu kullanmak için sohbetten bir öneri denemek istiyorum .
Bunu denemek istiyorum ama şu anda benim DROP TABLEsorgu da bitmesi saatler sürüyor. (Bu başka bir gösterge gibi görünüyor benim ayarı daha az optimal).

Ek bilgi:
Şu anda kullandığım makinede 8GB RAM ve 5400RPM ile Katı Hal Hibrit sabit sürücüsü var.
Ayrıca eski verileri söz konusu tablodan kaldırmayı hedeflerken,
a) automatic data cleanup featuregeliştirme sırasında teste biraz hızlı aktarmaya ihtiyacım var ve
b) sunucumuzun çökmesi durumunda 2. sunucumuzu bir yedek olarak kullanmak istiyoruz (ihtiyaç duyulan -veri verileri, son içe aktarma 24 saatten fazla sürdü)

mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
       Table: monster
Create Table: CREATE TABLE `monster` (
  `monster_id` int(11) NOT NULL AUTO_INCREMENT,
  `ext_monster_id` int(11) NOT NULL DEFAULT '0',
  `some_id` int(11) NOT NULL DEFAULT '0',
  `email` varchar(250) NOT NULL,
  `name` varchar(100) NOT NULL,
  `address` varchar(100) NOT NULL,
  `postcode` varchar(20) NOT NULL,
  `city` varchar(100) NOT NULL,
  `country` int(11) NOT NULL DEFAULT '0',
  `address_hash` varchar(250) NOT NULL,
  `lon` float(10,6) NOT NULL,
  `lat` float(10,6) NOT NULL,
  `ip_address` varchar(40) NOT NULL,
  `cookie` int(11) NOT NULL DEFAULT '0',
  `party_id` int(11) NOT NULL,
  `status` int(11) NOT NULL DEFAULT '2',
  `creation_date` datetime NOT NULL,
  `someflag` tinyint(1) NOT NULL DEFAULT '0',
  `someflag2` tinyint(4) NOT NULL,
  `upload_id` int(11) NOT NULL DEFAULT '0',
  `news1` tinyint(4) NOT NULL DEFAULT '0',
  `news2` tinyint(4) NOT NULL,
  `someother_id` int(11) NOT NULL DEFAULT '0',
  `note` varchar(2500) NOT NULL,
  `referer` text NOT NULL,
  `subscription` int(11) DEFAULT '0',
  `hash` varchar(32) DEFAULT NULL,
  `thumbs1` int(11) NOT NULL DEFAULT '0',
  `thumbs2` int(11) NOT NULL DEFAULT '0',
  `thumbs3` int(11) NOT NULL DEFAULT '0',
  `neighbours` tinyint(4) NOT NULL DEFAULT '0',
  `relevance` int(11) NOT NULL,
  PRIMARY KEY (`monster_id`),
  KEY `party_id` (`party_id`),
  KEY `creation_date` (`creation_date`),
  KEY `email` (`email`(4)),
  KEY `hash` (`hash`(8)),
  KEY `address_hash` (`address_hash`(8)),
  KEY `thumbs3` (`thumbs3`),
  KEY `ext_monster_id` (`ext_monster_id`),
  KEY `status` (`status`),
  KEY `note` (`note`(4)),
  KEY `postcode` (`postcode`),
  KEY `some_id` (`some_id`),
  KEY `cookie` (`cookie`),
  KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8

2
10 bin veya 100 bin satır gibi daha az büyük içe aktarmayı denediniz mi?
ypercubeᵀᴹ

1
Lütfen SHOW CREATE TABLE yourtable\Gbize bu 10 milyon sıralı masanın tablo yapısını göstermek için koşun .
RolandoMySQLDBA

@RolandoMySQLDBA ben yaptım (gizlenmiş alan adları ile)
nuala

Çift yazma arabelleğini ( innodb_doublewrite = 0) devre dışı bırakarak MySQL kurulumunuz kilitlenmeyebilir: bir elektrik kesintiniz (MySQL çökmesi değil) varsa, verileriniz sessizce bozulabilir.
jfg956

Yanıtlar:


13

İlk olarak, InnoDB tablosuna milyonlarca satırı sürdüğünüzde InnoDB'ye ne yaptığınızı bilmeniz gerekir. InnoDB Mimarisine bir göz atalım.

InnoDB Mimarisi

Sol üst köşede, InnoDB Buffer Pool'un bir resmi var. Ekleme tamponuna adanmış bir bölümüne dikkat edin. Bu ne işe yarar? İkincil dizinlerde yapılan değişikliklerin Tampon Havuzundan sistem tablo alanı içindeki Ekle Tamponuna (diğer adıyla ibdata1) geçirilmesi gerekir. Varsayılan olarak, innodb_change_buffer_max_size 25 olarak ayarlanır. Bu, Tampon Havuzunun% 25'ine kadar ikincil dizinleri işlemek için kullanılabileceği anlamına gelir.

Sizin durumunuzda, InnoDB Buffer Pool için 6.935 GB'ınız var. İkincil dizinlerinizi işlemek için maksimum 1,734 GB kullanılacaktır.

Şimdi masanıza bakın. 13 ikincil dizininiz var. İşlediğiniz her satır ikincil bir dizin girdisi oluşturmalı, bunu satırın birincil anahtarıyla birleştirmeli ve bunları Tampon Havuzundaki Insert Buffer'dan ibdata1'deki Insert Buffer'a bir çift olarak göndermelidir. Bu, her satırda 13 kez olur. Bunu 10 milyonla çarptığınızda neredeyse bir darboğazın geldiğini hissedebilirsiniz.

Tek bir işlemde 10 milyon satır içe aktarmanın, her şeyi tek bir geri alma segmentine ayıracağını ve ibdata1'deki UNDO alanını dolduracağını unutmayın.

ÖNERİLER

ÖNERİ # 1

Bu oldukça büyük tabloyu içe aktarmak için ilk önerim

  • Benzersiz olmayan tüm dizinleri bırak
  • Verileri içe aktarın
  • Benzersiz olmayan tüm dizinleri oluşturun

ÖNERİ # 2

Yinelenen dizinlerden kurtulun. Sizin durumunuzda,

KEY `party_id` (`party_id`),
KEY `party_id_2` (`party_id`,`status`)

Her iki dizin de ile başlar party_id, ikincil dizin işlemeyi en az% 7,6 artırabilirsiniz. Bir dizin 13'ten kurtulur.

ALTER TABLE monster DROP INDEX party_id;

ÖNERİ # 3

Kullanmadığınız dizinlerden kurtulun. Uygulama kodunuzu gözden geçirin ve sorgularınızın tüm dizinleri kullanıp kullanmadığını görün. Hangi dizinlerin kullanılmadığını önermek için pt-index-use içine bakmak isteyebilirsiniz .

ÖNERİ # 4

Varsayılan 8M olduğu için innodb_log_buffer_size değerini 64M'ye yükseltmelisiniz . Daha büyük bir günlük arabelleği InnoDB yazma G / Ç performansını artırabilir.

SONSÖZ

İlk iki öneriyi yerine koyarak aşağıdakileri yapın:

  • Benzersiz olmayan 13 dizini bırak
  • Verileri içe aktarın
  • Dışındaki tüm benzersiz olmayan dizinleri oluşturma party_idendeksi

Belki aşağıdakiler yardımcı olabilir

CREATE TABLE monster_new LIKE monster;
ALTER TABLE monster_new
  DROP INDEX `party_id`,
  DROP INDEX `creation_date`,
  DROP INDEX `email`,
  DROP INDEX `hash`,
  DROP INDEX `address_hash`,
  DROP INDEX `thumbs3`,
  DROP INDEX `ext_monster_id`,
  DROP INDEX `status`,
  DROP INDEX `note`,
  DROP INDEX `postcode`,
  DROP INDEX `some_id`,
  DROP INDEX `cookie`,
  DROP INDEX `party_id_2`;
ALTER TABLE monster RENAME monster_old;
ALTER TABLE monster_new RENAME monster;

Verileri içine aktarın monster. Sonra bunu çalıştırın

ALTER TABLE monster
  ADD INDEX `creation_date`,
  ADD INDEX `email` (`email`(4)),
  ADD INDEX `hash` (`hash`(8)),
  ADD INDEX `address_hash` (`address_hash`(8)),
  ADD INDEX `thumbs3` (`thumbs3`),
  ADD INDEX `ext_monster_id` (`ext_monster_id`),
  ADD INDEX `status` (`status`),
  ADD INDEX `note` (`note`(4)),
  ADD INDEX `postcode` (`postcode`),
  ADD INDEX `some_id` (`some_id`),
  ADD INDEX `cookie` (`cookie`),
  ADD INDEX `party_id_2` (`party_id`,`status`);

BİR ŞANS VER !!!

ALTERNATİF

monster_csvDizini olmayan MyISAM tablosu olarak adlandırılan bir tablo oluşturabilir ve bunu yapabilirsiniz:

CREATE TABLE monster_csv ENGINE=MyISAM AS SELECT * FROM monster WHERE 1=2;
ALTER TABLE monster RENAME monster_old;
CREATE TABLE monster LIKE monster_old;
ALTER TABLE monster DROP INDEX `party_id`;

Verilerinizi içine aktarın monster_csv. Ardından, başka bir içe aktarma oluşturmak için mysqldump kullanın

mysqldump -t -uroot -p mydb monster_csv | sed 's/monster_csv/monster/g' > data.sql

Mysqldump dosyası data.sql, bir kerede 10.000-20.000 satır içe aktararak INSERT komutlarını genişletir.

Şimdi, mysqldump'ı yükleyin

mysql -uroot -p mydb < data.sql

Son olarak, MyISAM tablosundan kurtulun

DROP TABLE monster_csv;

Tüm bu anahtarların farkında bile değildim (benim tasarımım değil) ama açıklamanız çok inandırıcı görünüyor. Bugün başka bir denemeye başlamak için geç ama yarın ne denemek için bazı harika tavsiyeler görüyorum. Sizi bilgilendirecek! <3
nuala

1
monsterInnoDB tablolar üzerinde hiçbir anahtarınız olduğunda daha az 20 dakika içinde tam veritabanı (sadece tablo) almak başardı . Anahtar eklemek yakl. 20 dakika daha. Bu durumda bu sorunumu çözdüğünü söyleyebilirim. Çok teşekkür ederim!
nuala

8

(Bu kesin bir cevap olmadığı için) bir yorum yazmak istedim, ama çok uzun oldu:

Size birkaç geniş tavsiye vereceğim ve isterseniz her biri için ayrıntılara girebiliriz:

  • Dayanıklılığı azaltın (bazılarını zaten yaptınız). En son sürümler daha da fazlasını yapmanızı sağlar. Yolsuzluk ithalat için bir sorun olmadığından, çift yazma arabelleğini devre dışı bırakma kadar ileri gidebilirsiniz.
  • Arabelleğe almayı şu şekilde artır: İşlem günlüğü boyutunu artırın ve kullanılabilir arabellek havuzu boyutunu artırın. İşlem günlüğü dosyası kullanımını ve denetim noktalarını izleyin. Bir ithalat için büyük günlüklerden korkmayın.
  • Büyük işlemlerden kaçının - geri dönüşünüz gereksiz verilerle dolu olacaktır. Bu muhtemelen en büyük problemin.
  • SQL bir darboğaz olacak, SQL ek yükü (handlersocket, memcached) kaçının ve / veya aynı anda birkaç iş parçacığı ile aynı anda yükleyin. Eşzamanlılık tatlı bir noktaya ulaşmalı, çok fazla değil, çok az değil.
  • Birincil anahtar sırası parçalanmasında veri yükleme bir sorun olabilir
  • IO darboğazınız ve CPU ve bellek yavaşlatıyorsa InnoDB sıkıştırmasını test edin
  • Daha sonra ikincil anahtarlarınızı oluşturmayı deneyin (bazı durumlarda daha hızlı), dizine alınmış verileri yüklemeyin - KİLİTLERİ DEVRE DIŞI BIRAK InnoDB'yi etkilemez . Değilse, ekleme tamponunuzu izleyin (tampon havuzunuzun yarısını sollayabilir).
  • Sağlama toplamı algoritmasını değiştirme veya devre dışı bırakma - muhtemelen sorun değil, ancak üst düzey flash kartlarda bir darboğaz haline gelir.
  • Son çare: Mevcut darboğazınızı bulmak için sunucunuzu izleyin ve azaltmaya çalışın (InnoDB bu konuda çok esnektir).

Bunlardan bazılarının ithalat dışı ürünler için güvenli veya tavsiye edilmediğini unutmayın (normal çalışma).


Çok teşekkür ederim! Önce Rolando'nun endekslerle ilgili fikrini denemek istiyorum ama sanırım bu "geri alma" şeyleri hala bir sorun olacak. Bu konuda ayrıntılı misiniz? Sanırım ithalat sırasında bu işlevselliği mümkün olduğunca devre dışı bırakmak ve üretime girerken yeniden etkinleştirmek istiyorum ~ Sanırım ...
nuala

1
Rolando'nun önerisi benim 7 numaralı nokta. Geri alma ek SET SESSION tx_isolation='READ-UNCOMMITTED';yükünden kaçınmak (yalnızca paralel olarak birkaç iş parçacığıyla içe aktarırsanız yararlıdır) ve @ypercube toplu olarak ekleme hakkındaki yorumların birleşimi kadar kolaydır . Burada tam bir örneğiniz var: mysqlperformanceblog.com/2008/07/03/… En son InnoDB sürümlerindeki tüm özelliklerden yararlandığınızdan emin olun: mysqlperformanceblog.com/2011/01/07/…
jynus

1
Ben daha küçük aynalar ithal önlemek önlemek genel bir izlenim vardı ama yerine "herşey dahil" bir operasyon için gitmek ama çok iş parçacığı bazı olasılıklar açabilir görebilirsiniz. Sanırım bu duruma çok özel. Ancak Rolando'nun cevabını kabul ettim, çünkü bu tweak (# 7) tek başına <1 saat içinde tam ithalat almama yardımcı oldu, ancak listeniz kesinlikle değersiz değil ve sanırım DB'mizin oranı oldukça kısa bir süre sonra referans olarak kullanacağım beni korkutuyor :)
nuala

@Yoshi ile aynı fikirdeyim. Yanıtınız, sorun giderme ve performans iyileştirmeleri açısından daha kapsamlıdır. +1
RolandoMySQLDBA

3

İyi ipuçlarının çoğu şimdiye kadar verilmiştir, ancak en iyileri için çok fazla açıklama yapılmamıştır. Daha fazla ayrıntı vereceğim.

İlk olarak, dizin oluşturmayı geciktirmek iyi bir yanıttır ve diğer yanıtlarda yeterli ayrıntı vardır. Ben geri gelmeyecek.

Daha büyük bir InnoDB günlük dosyası size çok yardımcı olacaktır (MySQL 5.6 kullanıyorsanız MySQL 5.5'te artırmak mümkün değildir). 7 GB veri ekliyorsunuz, toplam günlük boyutunu en az 8 GB öneriyoruz ( innodb_log_files_in_groupvarsayılan değerini koru (2) ve innodb_log_file_size4 GB'de çarpma ). Bu 8 GB tam değil: en azından REDO günlüğündeki içe aktarma boyutu olmalı ve muhtemelen bu boyutu iki veya dört katına çıkarmalıdır. InnoDB günlük boyutunun ardındaki mantık, günlük neredeyse dolmaya başladığında, doldurma günlüğünü önlemek için InnoDB'nin arabellek havuzunu agresif bir şekilde temizlemeye başlayacağını (günlük dolduğunda, InnoDB'nin arabellek havuzu sayfaları diske yazılır).

Daha büyük bir InnoDB günlük dosyası size yardımcı olacaktır, ancak birincil anahtar sırasını da eklemeniz gerekir (eklemeden önce dosyanızı sıralayın). Birincil anahtar sırasıyla eklerseniz, InnoDB bir sayfayı, ardından başka bir sayfayı doldurur ve bu böyle devam eder. Birincil anahtar sırasına eklemezseniz, bir sonraki ekiniz dolu ve "sayfa bölünmesi" ile sonuçlanabilecek bir sayfaya dönüşebilir. Bu sayfa bölünmesi InnoDB için pahalı olacak ve içe aktarma işleminizi yavaşlatacaktır.

RAM'inizin izin verdiği kadar büyük bir tampon havuzunuz var ve tablonuz buna uymuyorsa, daha fazla RAM satın almak dışında yapabileceğiniz çok şey yok. Ancak bu tablo tampon havuzuna sığar ancak tampon havuzunuzun% 75'inden daha büyükse innodb_max_dirty_pages_pct, içe aktarma sırasında 85 veya 95'e yükselmeyi deneyebilirsiniz (varsayılan değer 75'tir). Bu yapılandırma parametresi, InnoDB'ye kirli sayfaların yüzdesi bu sınıra ulaştığında tampon havuzunu agresif bir şekilde temizlemeye başlamasını söyler. Bu parametreyi çarparak (ve veri boyutu konusunda şanslıysanız), içe aktarma sırasında agresif G / Ç'den kaçınabilir ve bu G / Ç'yi daha sonra erteleyebilirsiniz.

Belki (ve bu bir tahmin) birçok küçük işlemde verilerinizi almak size yardımcı olacaktır. REDO günlüğünün nasıl oluşturulduğunu tam olarak bilmiyorum, ancak işlem ilerlerken RAM'de (ve çok fazla RAM gerektiğinde diskte) tamponlanmışsa, gereksiz IO'larla sonuçlanabilir. Bunu deneyebilirsiniz: Dosyanız sıralandığında, birçok parçaya bölün (16 MB ve diğer boyutlarla deneyin) ve bunları tek tek içe aktarın. Bu ayrıca içe aktarma işleminizin ilerlemesini kontrol etmenizi sağlar. İçe aktarma işlemi sırasında verilerinizin diğer okuyucu tarafından kısmen görünür olmasını istemiyorsanız, farklı bir tablo adı kullanarak içe aktarabilir, daha sonra dizinler oluşturabilir ve ardından tabloyu yeniden adlandırabilirsiniz.

Hibrid SSD / 5400RPM diskiniz hakkında, bunları ve bunu nasıl optimize edeceğinizi bilmiyorum. 5400 RPM bir veritabanı için yavaş görünüyor, ancak SSD bundan kaçınıyor olabilir. Belki de diskinizin SSD kısmını REDO günlüğüne ardışık yazmalarla dolduruyorsunuz ve SSD performanslara zarar veriyor. Bilmiyorum.

Denememeniz (veya dikkatli olmamanız) gereken kötü ipuçları şunlardır: çok iş parçacığı kullanmayın: InnoDB'de sayfa bölünmelerini önlemek için optimize etmek çok zor olacaktır. Çok iş parçacığı kullanmak istiyorsanız, farklı tablolara (veya aynı tablonun farklı bölümlerine) ekleyin.

Çoklu iş parçacığı düşünüyorsanız, belki de çok yuvalı (NUMA) bir bilgisayarınız var. Bu durumda, MySQL takas deliliği sorununu önlediğinizden emin olun .

MySQL 5.5 kullanıyorsanız, MySQL 5.6'ya yükseltin: REDO günlük boyutunu artırma seçeneğine ve daha iyi tampon havuzu yıkama algoritmalarına sahiptir.

İçe aktarma işleminde iyi şanslar.

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.