Membangun tabel MySQL baris 1.000M

18

Pertanyaan ini diposting ulang dari Stack Overflow berdasarkan saran di komentar, permintaan maaf untuk duplikasi.

Pertanyaan

Pertanyaan 1: saat ukuran tabel basis data semakin besar, bagaimana saya bisa menyetel MySQL untuk meningkatkan kecepatan panggilan LOAD DATA INFILE?

Pertanyaan 2: apakah akan menggunakan sekelompok komputer untuk memuat file csv yang berbeda, meningkatkan kinerja atau membunuhnya? (ini adalah tugas menandai bangku saya untuk besok menggunakan data muatan dan sisipan massal)

Tujuan

Kami mencoba kombinasi berbeda dari pendeteksi fitur dan parameter pengelompokan untuk pencarian gambar, sebagai akibatnya kami harus dapat membangun dan database besar secara tepat waktu.

Info Mesin

Mesin memiliki 256 gig ram dan ada 2 mesin lain yang tersedia dengan jumlah ram yang sama jika ada cara untuk meningkatkan waktu pembuatan dengan mendistribusikan database?

Skema Tabel

skema tabel terlihat seperti

+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| match_index   | int(10) unsigned | NO   | PRI | NULL    |                |
| cluster_index | int(10) unsigned | NO   | PRI | NULL    |                |
| id            | int(11)          | NO   | PRI | NULL    | auto_increment |
| tfidf         | float            | NO   |     | 0       |                |
+---------------+------------------+------+-----+---------+----------------+

dibuat dengan

CREATE TABLE test 
(
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL AUTO_INCREMENT,
  tfidf FLOAT NOT NULL DEFAULT 0,
  UNIQUE KEY (id),
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

Benchmarking sejauh ini

Langkah pertama adalah membandingkan sisipan massal vs memuat dari file biner ke tabel kosong.

It took:  0:09:12.394571  to do  4,000  inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file

Mengingat perbedaan dalam kinerja saya telah pergi dengan memuat data dari file csv biner, pertama saya memuat file biner yang berisi 100K, 1M, 20M, 200M baris menggunakan panggilan di bawah ini.

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;

Saya membunuh file biner baris 200M (~ file csv 3GB) dimuat setelah 2 jam.

Jadi saya menjalankan skrip untuk membuat tabel, dan memasukkan nomor baris yang berbeda dari file biner kemudian menjatuhkan tabel, lihat grafik di bawah ini.

masukkan deskripsi gambar di sini

Butuh sekitar 7 detik untuk menyisipkan baris 1M dari file biner. Selanjutnya saya memutuskan untuk benchmark memasukkan 1M baris sekaligus untuk melihat apakah akan ada hambatan pada ukuran database tertentu. Setelah Database mencapai sekitar 59M baris, waktu memasukkan rata-rata turun menjadi sekitar 5.000 / detik

masukkan deskripsi gambar di sini

Mengatur key_buffer_size global = 4294967296 meningkatkan kecepatan sedikit untuk memasukkan file biner yang lebih kecil. Grafik di bawah ini menunjukkan kecepatan untuk jumlah baris yang berbeda

masukkan deskripsi gambar di sini

Namun untuk memasukkan baris 1M itu tidak meningkatkan kinerja.

baris: 1.000.000 waktu: 0: 04: 13.761428 sisipan / detik: 3.940

vs untuk database kosong

baris: 1.000.000 waktu: 0: 00: 6.339295 sisipan / detik: 315.492

Memperbarui

Melakukan memuat data menggunakan urutan berikut vs hanya menggunakan perintah memuat data

SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
masukkan deskripsi gambar di sini

Jadi ini terlihat cukup menjanjikan dalam hal ukuran basis data yang sedang dibuat tetapi pengaturan lain tampaknya tidak mempengaruhi kinerja panggilan infil data beban.

Saya kemudian mencoba memuat beberapa file dari mesin yang berbeda tetapi perintah memuat data infile mengunci tabel, karena ukuran file yang besar menyebabkan mesin lain kehabisan waktu dengan

ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction

Meningkatkan jumlah baris dalam file biner

rows:  10,000,000  seconds rows:  0:01:36.545094  inserts/sec:  103578.541236
rows:  20,000,000  seconds rows:  0:03:14.230782  inserts/sec:  102970.29026
rows:  30,000,000  seconds rows:  0:05:07.792266  inserts/sec:  97468.3359978
rows:  40,000,000  seconds rows:  0:06:53.465898  inserts/sec:  96743.1659866
rows:  50,000,000  seconds rows:  0:08:48.721011  inserts/sec:  94567.8324859
rows:  60,000,000  seconds rows:  0:10:32.888930  inserts/sec:  94803.3646283

Solusi: Precomputing id di luar MySQL alih-alih menggunakan penambahan otomatis

Membangun meja dengan

CREATE TABLE test (
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL ,
  tfidf FLOAT NOT NULL DEFAULT 0,
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

dengan SQL

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"

masukkan deskripsi gambar di sini

Membuat skrip untuk melakukan pre-compute indeks tampaknya telah menghapus hit kinerja sebagai database tumbuh dalam ukuran.

Perbarui 2 - menggunakan tabel memori

Secara kasar 3 kali lebih cepat, tanpa memperhitungkan biaya memindahkan tabel di-memori ke meja berbasis disk.

rows:  0  seconds rows:  0:00:26.661321  inserts/sec:  375075.18851
rows:  10000000  time:  0:00:32.765095  inserts/sec:  305202.83857
rows:  20000000  time:  0:00:38.937946  inserts/sec:  256818.888187
rows:  30000000  time:  0:00:35.170084  inserts/sec:  284332.559456
rows:  40000000  time:  0:00:33.371274  inserts/sec:  299658.922222
rows:  50000000  time:  0:00:39.396904  inserts/sec:  253827.051994
rows:  60000000  time:  0:00:37.719409  inserts/sec:  265115.500617
rows:  70000000  time:  0:00:32.993904  inserts/sec:  303086.291334
rows:  80000000  time:  0:00:33.818471  inserts/sec:  295696.396209
rows:  90000000  time:  0:00:33.534934  inserts/sec:  298196.501594

dengan memuat data ke dalam tabel berbasis memori dan kemudian menyalinnya ke meja berbasis disk dalam potongan memiliki overhead 10 menit 59,71 detik untuk menyalin 107.356.741 baris dengan kueri

insert into test Select * from test2;

yang membuatnya sekitar 15 menit untuk memuat 100M baris, yang kira-kira sama dengan langsung memasukkannya ke dalam tabel berbasis disk.

Ben
sumber
1
Saya pikir mengubah kunci primer idmenjadi lebih cepat. (Meskipun saya pikir Anda tidak mencari ini)
DavidEG
Hai David, terima kasih atas komentarnya, sayangnya tanpa kunci pertanyaan yang perlu kita lakukan tidak cukup cepat (logika di balik pemilihan kunci utama diuraikan dalam posting ini stackoverflow.com/questions/4282526/mysql-group-by- optimasi )
Ben
1
Apakah ini hanya untuk pengujian? Anda mungkin ingin melihat mesin MySQL MEMORY: dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html Jika Anda berencana untuk menggunakan ini sebagai arsitektur, saya ingin tahu bagaimana Anda berencana untuk pulih dari kegagalan, sepertinya sesuatu yang akan lebih baik ditangani oleh MapReduce / Hadoop.
jumlahnya banyak
Hai polinomial, terima kasih atas tipnya, saat ini kami hanya menguji detektor fitur yang berbeda pada skala yang berbeda, setelah database dihasilkan tidak akan banyak berubah (dalam spec saat ini)
Ben

Jawaban:

4

Pertanyaan bagus - dijelaskan dengan baik.

bagaimana saya bisa menyetel MySQL untuk meningkatkan kecepatan panggilan LOAD DATA INFILE?

Anda sudah mendapatkan pengaturan tinggi (ish) untuk buffer kunci - tetapi apakah itu cukup? Saya berasumsi ini adalah instalasi 64-bit (jika tidak maka hal pertama yang perlu Anda lakukan adalah memutakhirkan) dan tidak berjalan di MSNT. Lihat output mysqltuner.pl setelah menjalankan beberapa tes.

Untuk menggunakan cache dengan efek terbaik, Anda mungkin menemukan manfaat dalam batching / pre-sorting data input (versi terbaru dari perintah 'sort' memiliki banyak fungsi untuk menyortir dataset besar). Juga jika Anda menghasilkan nomor ID di luar MySQL, maka mungkin lebih efisien.

akan menggunakan sekelompok komputer untuk memuat file csv yang berbeda

Dengan asumsi (sekali lagi) bahwa Anda ingin agar himpunan output berperilaku sebagai tabel tunggal, maka satu-satunya manfaat yang akan Anda dapatkan adalah dengan mendistribusikan pekerjaan menyortir dan membuat id - yang Anda tidak memerlukan database lebih banyak. OTOH menggunakan cluster database, Anda akan mendapatkan masalah dengan pertikaian (yang seharusnya tidak Anda lihat selain sebagai masalah kinerja).

Jika Anda dapat membuang data dan menangani kumpulan data yang dihasilkan secara independen, maka ya, Anda akan mendapatkan manfaat kinerja - tetapi ini tidak meniadakan kebutuhan untuk menyempurnakan setiap node.

Periksa Anda punya setidaknya 4 Gb untuk sort_buffer_size.

Selain itu, faktor pembatas pada kinerja adalah semua tentang disk I / O. Ada banyak cara untuk mengatasinya - tetapi Anda mungkin harus mempertimbangkan set set mirror set data pada SSD untuk kinerja optimal.

symcbean
sumber
1
  • Pertimbangkan faktor pembatas Anda. Ini hampir pasti pemrosesan CPU single-threaded.
  • Anda telah menentukan bahwa load data...itu lebih cepat daripada memasukkan, jadi gunakan itu.
  • Anda telah menentukan bahwa file yang sangat besar (berdasarkan nomor baris) banyak memperlambatnya; Anda ingin memecahnya menjadi beberapa bagian.
  • Menggunakan kunci primer yang tidak tumpang tindih, antri setidaknya set N * CPU, menggunakan tidak lebih dari satu juta baris ... mungkin lebih sedikit (benchmark).
  • Gunakan blok sekuensial dari kunci primer di setiap file.

Jika Anda ingin benar-benar keren, Anda bisa membuat program multi-utas untuk memberi makan satu file ke kumpulan pipa bernama dan mengelola instance sisipan.

Singkatnya, Anda tidak menyetel MySQL untuk ini sebanyak Anda menyesuaikan beban kerja Anda ke MySQL.

Jeff Ferland
sumber
-1

Saya tidak ingat persis syntacx tetapi jika inno db Anda dapat mematikan cek kunci asing.

Anda juga dapat membuat indeks setelah impor, itu bisa menjadi keuntungan kinerja yang sangat.

Julien Duponchelle
sumber
Menunda pembangunan kembali indeks hanya akan meningkatkan kinerja di mana jumlah baris yang sudah ada dalam tabel secara signifikan lebih kecil daripada jumlah baris yang Anda tambahkan.
symcbean