Kinerja impor InnoDB

10

Saya berjuang dengan mengimpor sebagian besar InnoDB-Table cukup besar yang terdiri dari sekitar 10 Juta baris (atau 7GB) (yang bagi saya adalah meja terbesar yang pernah saya kerjakan sejauh ini).

Saya melakukan riset bagaimana meningkatkan kecepatan impor Inno dan untuk saat ini setup saya terlihat seperti ini:

/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;

Data disediakan dalam CSVfile.
Saat ini saya menguji pengaturan saya dengan 'test dumps' yang lebih kecil dengan 2 juta, 3 juta, ... masing-masing baris dan digunakan time import_script.shuntuk membandingkan kinerja.

Kekurangannya adalah saya hanya mendapatkan waktu berjalan keseluruhan jadi saya harus menunggu impor penuh selesai untuk mendapatkan hasil.

Hasil saya sejauh ini:

  • 10 000 baris: <1 detik
  • 100.000 baris: 10 detik
  • 300.000 baris: 40 detik
  • 2 juta baris: 18 menit
  • 3 juta baris: 26 menit
  • 4 juta baris: (dibatalkan setelah 2 jam)

Tampaknya tidak ada solusi 'buku masak' dan kita harus mencari tahu kombinasi yang optimal dari pengaturan mereka sendiri.
Selain saran tentang apa yang harus diubah dalam pengaturan saya, saya juga akan sangat menghargai informasi lebih lanjut bagaimana saya bisa melakukan benchmark proses impor yang lebih baik / mendapatkan lebih banyak wawasan tentang apa yang terjadi dan di mana kemacetan mungkin terjadi.
Saya mencoba membaca dokumentasi untuk pengaturan yang saya ubah tetapi sekali lagi saya tidak mengetahui adanya efek samping dan jika saya bahkan menurunkan kinerja dengan nilai yang dipilih dengan buruk.

Untuk saat ini saya ingin mencoba saran dari obrolan untuk digunakan MyISAMselama impor dan mengganti mesin tabel sesudahnya.
Saya ingin mencoba ini tetapi untuk saat ini DROP TABLEpermintaan saya juga membutuhkan waktu berjam-jam untuk menyelesaikannya. (Yang sepertinya indikator lain pengaturan saya kurang optimal).

Informasi tambahan:
Mesin yang saya gunakan saat ini memiliki 8GB RAM dan hard drive Solid State Hybrid w / 5400RPM.
Sementara kami juga bertujuan untuk menghapus data yang tidak terpakai dari tabel yang dimaksud, saya masih memerlukan impor yang agak cepat untuk
a) menguji automatic data cleanup featuresementara mengembangkan dan
b) seandainya server kami mogok kami ingin menggunakan server 2 kami sebagai pengganti (yang membutuhkan data terkini, impor terakhir memakan waktu lebih dari 24 jam)

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
nuala
sumber
2
Apakah Anda mencoba dengan impor yang kurang besar, seperti baris 10K atau 100K?
ypercubeᵀᴹ
1
Jalankan SHOW CREATE TABLE yourtable\Guntuk menunjukkan kepada kami struktur tabel dari 10 juta baris tabel ini.
RolandoMySQLDBA
@RolandoMySQLDBA jadi saya lakukan (dengan nama field yang dikaburkan)
nuala
Dengan menonaktifkan buffer tulis ganda ( innodb_doublewrite = 0) instalasi MySQL Anda tidak aman crash: jika Anda mengalami kegagalan daya (bukan crash MySQL), data Anda mungkin rusak secara diam-diam.
jfg956

Jawaban:

13

Pertama, Anda perlu tahu apa yang Anda lakukan pada InnoDB ketika Anda membajak jutaan baris ke dalam tabel InnoDB. Mari kita lihat Arsitektur InnoDB.

Arsitektur InnoDB

Di sudut kiri atas, ada ilustrasi Pool Buffer InnoDB. Perhatikan ada bagian yang didedikasikan untuk menyisipkan penyangga. Apa fungsinya? Ia diminta untuk memigrasikan perubahan ke indeks sekunder dari Buffer Pool ke Insert Buffer di dalam tablespace sistem (alias ibdata1). Secara default, innodb_change_buffer_max_size diatur ke 25. Ini berarti bahwa hingga 25% dari Buffer Pool dapat digunakan untuk memproses indeks sekunder.

Dalam kasus Anda, Anda memiliki 6,935 GB untuk InnoDB Buffer Pool. Maksimum 1,734 GB akan digunakan untuk memproses indeks sekunder Anda.

Sekarang, lihat meja Anda. Anda memiliki 13 indeks sekunder. Setiap baris yang Anda proses harus menghasilkan entri indeks sekunder, pasangkan dengan kunci utama dari baris tersebut, dan kirim sebagai pasangan dari Sisipkan Penyangga di Kumpulan Penyangga ke Sisipkan Penyangga di ibdata1. Itu terjadi 13 kali dengan setiap baris. Kalikan ini dengan 10 juta dan Anda hampir bisa merasakan hambatan datang.

Jangan lupa bahwa mengimpor 10 juta baris dalam satu transaksi akan menumpuk semuanya menjadi satu segmen rollback dan mengisi ruang UNDO di ibdata1.

SARAN

SARAN # 1

Saran pertama saya untuk mengimpor tabel yang agak besar ini

  • Jatuhkan semua indeks yang tidak unik
  • Impor data
  • Buat semua indeks non-unik

SARAN # 2

Singkirkan indeks rangkap. Dalam kasus Anda, Anda punya

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

Kedua indeks dimulai dengan party_id, Anda dapat meningkatkan pemrosesan indeks sekunder dengan setidaknya 7,6% menyingkirkan satu indeks dari 13. Anda akhirnya harus menjalankan

ALTER TABLE monster DROP INDEX party_id;

SARAN # 3

Singkirkan indeks yang tidak Anda gunakan. Lihatlah kode aplikasi Anda dan lihat apakah pertanyaan Anda menggunakan semua indeks. Anda mungkin ingin melihat penggunaan indeks pt untuk membiarkannya menyarankan indeks apa yang tidak digunakan.

SARAN # 4

Anda harus meningkatkan innodb_log_buffer_size menjadi 64M karena standarnya adalah 8M. Buffer log yang lebih besar dapat meningkatkan kinerja I / O InnoDB.

EPILOG

Dengan menerapkan dua saran pertama, lakukan yang berikut:

  • Jatuhkan 13 indeks tidak unik
  • Impor data
  • Buat semua indeks non-unik kecuali party_idindeks

Mungkin yang berikut bisa membantu

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;

Impor data ke monster. Lalu, jalankan ini

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`);

COBALAH !!!

ALTERNATIF

Anda bisa membuat tabel yang disebut monster_csvsebagai tabel MyISAM tanpa indeks dan melakukan ini:

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`;

Impor data Anda ke monster_csv. Kemudian, gunakan mysqldump untuk membuat impor lain

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

File mysqldump data.sqlakan memperpanjang perintah INSERT yang mengimpor 10.000-20.000 baris sekaligus.

Sekarang, muat mysqldump

mysql -uroot -p mydb < data.sql

Akhirnya, singkirkan tabel MyISAM

DROP TABLE monster_csv;
RolandoMySQLDBA
sumber
Saya bahkan tidak mengetahui semua kunci itu (ini bukan desain saya) tetapi penjelasan Anda tampaknya sangat meyakinkan. Untuk hari ini terlambat untuk memulai percobaan lain, tetapi saya melihat beberapa saran hebat apa yang harus dicoba besok. Akan membuat Anda mendapat informasi! <3
nuala
1
Saya berhasil mengimpor database lengkap (tidak hanya monstertabel) dalam waktu kurang dari 20 menit ketika tidak memiliki kunci pada tabel InnoDB. Menambahkan kunci membutuhkan sekitar. 20 menit lagi. Saya akan mengatakan ini cukup banyak memecahkan masalah saya dalam kasus ini. Terima kasih banyak!
nuala
8

Saya ingin menulis komentar (karena ini bukan jawaban yang pasti), tetapi terlalu lama:

Saya akan memberikan beberapa saran luas kepada Anda, dan kami dapat memerinci masing-masing, jika Anda ingin:

  • Kurangi daya tahan (Anda telah melakukan sebagian dari itu). Versi terbaru bahkan memungkinkan lebih banyak melakukannya. Anda dapat menonaktifkan buffer tulis ganda, karena korupsi bukan masalah untuk impor.
  • Tingkatkan buffering dengan: Meningkatkan ukuran log transaksi dan menambah ukuran kumpulan buffer yang tersedia. Monitor penggunaan file log transaksi dan pos pemeriksaan. Jangan takut log besar untuk impor.
  • Hindari transaksi besar - kemunduran Anda akan menjadi penuh dengan data yang tidak dibutuhkan. Ini mungkin masalah terbesar Anda.
  • SQL akan menjadi hambatan, hindari overhead SQL (handlersocket, memcached) dan / atau muat dalam concurrency dengan beberapa utas secara bersamaan. Concurrency harus mencapai sweet spot, tidak terlalu banyak, tidak terlalu sedikit.
  • Memuat data dalam fragmentasi urutan kunci primer dapat menjadi isse
  • Uji kompresi InnoDB jika IO adalah hambatan dan CPU Anda dan memori tidak membuatnya lebih lambat
  • Coba buat kunci sekunder Anda setelah itu (lebih cepat dalam beberapa kasus), jangan memuat data yang diindeks- KUNCI DISABLE tidak mempengaruhi InnoDB . Jika tidak, monitor buffer insert Anda (mungkin menyalip setengah dari pool buffer Anda).
  • Ubah atau nonaktifkan algoritme checksum - mungkin bukan masalah Anda, tetapi menjadi hambatan pada kartu flash kelas atas.
  • Usaha terakhir: Pantau server Anda untuk menemukan kemacetan Anda saat ini dan cobalah untuk mengurangi (InnoDB sangat fleksibel tentang hal itu).

Ingatlah bahwa beberapa di antaranya tidak aman atau disarankan untuk non-impor (operasi normal).

jynus
sumber
Terima kasih banyak! Saya ingin mencoba ide Rolando tentang indeks terlebih dahulu, tetapi saya kira hal-hal "transaksi-rollback" ini masih akan menjadi masalah. Bisakah Anda menjelaskan hal ini? Saya rasa saya ingin menonaktifkan sebanyak mungkin fungsi ini selama impor dan mengaktifkan kembali ketika masuk ke produksi ~ Saya pikir ...
nuala
1
Saran Rolando adalah poin saya # 7. Menghindari rollback overhead semudah kombinasi SET SESSION tx_isolation='READ-UNCOMMITTED';(hanya berguna jika Anda mengimpor dengan beberapa utas secara paralel) dan komentar @ypercube tentang memasukkan bets. Anda memiliki contoh lengkap di sini: mysqlperformanceblog.com/2008/07/03/... Pastikan Anda mendapatkan keuntungan dari semua fitur di versi InnoDB terbaru: mysqlperformanceblog.com/2011/01/07/…
jynus
1
Saya memiliki kesan umum bahwa seseorang akan menghindari mengimpor dalam chucks yang lebih kecil tetapi lebih memilih untuk operasi "semua termasuk" tapi saya melihat multi-threading dapat membuka beberapa kemungkinan. Kira itu sangat spesifik kasus. Namun saya menerima jawaban Rolando karena tweak ini (Anda # 7) sendiri membantu saya untuk mendapatkan impor penuh dalam <1 jam tetapi daftar Anda jelas jauh dari tidak berharga dan saya kira akan menggunakannya untuk referensi segera setelah tingkat DB kami tumbuh agak membuatku takut :)
nuala
Saya setuju dengan @yoshi. Jawaban Anda lebih komprehensif dalam hal pemecahan masalah dan peningkatan kinerja. +1
RolandoMySQLDBA
3

Sebagian besar tips yang baik telah diberikan sejauh ini, tetapi tanpa banyak penjelasan untuk yang terbaik. Saya akan memberikan lebih banyak detail.

Pertama, menunda pembuatan indeks adalah yang baik, dengan rincian yang cukup dalam tanggapan lain. Saya tidak akan kembali lagi.

File log InnoDB yang lebih besar akan banyak membantu Anda (jika Anda menggunakan MySQL 5.6 karena tidak mungkin untuk meningkatkannya di MySQL 5.5). Anda memasukkan 7 GB data, saya akan merekomendasikan ukuran total log minimal 8 GB (tetap innodb_log_files_in_grouppada default-nya (2) dan menabrak innodb_log_file_sizepada 4 GB). 8 GB ini tidak tepat: harus setidaknya ukuran impor di log REDO dan mungkin dua kali lipat atau empat kali lipat ukuran itu. Alasan di balik ukuran log InnoDB meningkatkannya bahwa ketika log akan menjadi hampir penuh, InnoDB akan mulai menyiram kolam penyangga secara agresif ke disk untuk menghindari log pengisian (ketika log penuh, InnoDB tidak dapat melakukan penulisan database sampai beberapa halaman kumpulan buffer ditulis ke disk).

File log InnoDB yang lebih besar akan membantu Anda, tetapi Anda juga harus memasukkan urutan kunci primer (urutkan file Anda sebelum memasukkan). Jika Anda memasukkan urutan kunci utama, InnoDB akan mengisi satu halaman, lalu satu lagi, dan seterusnya. Jika Anda tidak memasukkan urutan kunci utama, masukkan berikutnya mungkin berakhir di halaman yang penuh dan akan menimbulkan "pemisah halaman". Pemecahan halaman ini akan mahal untuk InnoDB dan akan memperlambat impor Anda.

Anda sudah memiliki buffer pool sebesar RAM yang memungkinkan dan jika meja Anda tidak pas, tidak banyak yang bisa Anda lakukan kecuali membeli lebih banyak RAM. Tetapi jika tabel Anda cocok dalam buffer pool tetapi lebih besar dari 75% pool buffer Anda, Anda dapat mencoba meningkatkan innodb_max_dirty_pages_pctke 85 atau 95 selama impor (nilai defaultnya adalah 75). Parameter konfigurasi ini memberi tahu InnoDB untuk memulai pembilasan kolam penyangga secara agresif ketika persentase halaman kotor mencapai batas ini. Dengan menaikkan parameter ini (dan jika Anda beruntung dengan ukuran data), Anda mungkin menghindari IO agresif selama impor dan menunda IO tersebut nanti.

Mungkin (dan ini dugaan) mengimpor data Anda dalam banyak transaksi kecil akan membantu Anda. Saya tidak tahu persis bagaimana REDO log dibangun, tetapi jika itu buffered dalam RAM (dan disk ketika terlalu banyak RAM akan dibutuhkan) saat transaksi sedang membuat kemajuan, Anda mungkin berakhir dengan IO yang tidak perlu. Anda dapat mencoba ini: setelah file Anda diurutkan, pisahkan dalam banyak potongan (coba dengan 16 MB dan ukuran lainnya) dan impor satu per satu. Ini juga akan memungkinkan Anda untuk mengontrol kemajuan impor Anda. Jika Anda tidak ingin data Anda terlihat sebagian oleh pembaca lain saat Anda melakukan impor, Anda bisa mengimpor menggunakan nama tabel yang berbeda, membuat indeks nanti, dan kemudian mengganti nama tabel.

Tentang disk SSD / 5400RPM hibrid Anda, saya tidak tahu tentang itu dan bagaimana mengoptimalkannya. 5400RPM terlihat lambat untuk basis data, tetapi mungkin SSD menghindarinya. Tentu saja Anda mengisi bagian SSD dari disk Anda dengan menulis berurutan ke REDO log dan SSD merusak kinerja. Saya tidak tahu.

Kiat buruk yang tidak boleh Anda coba (atau berhati-hati) adalah sebagai berikut: jangan gunakan multi-utas: akan sangat sulit untuk mengoptimalkan untuk menghindari pemisahan halaman di InnoDB. Jika Anda ingin menggunakan multi-utas, masukkan dalam tabel yang berbeda (atau di partisi berbeda dari tabel yang sama).

Jika Anda mempertimbangkan multi-thread, mungkin Anda memiliki komputer multi-socket (NUMA). Dalam hal ini, pastikan Anda menghindari masalah kegilaan swap MySQL .

Jika Anda menggunakan MySQL 5.5, tingkatkan ke MySQL 5.6: ia memiliki opsi untuk meningkatkan ukuran log REDO dan memiliki algoritma penyiraman pool penyangga yang lebih baik.

Semoga berhasil dengan impor Anda.

jfg956
sumber