Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Pengaturan MySQL yang optimal untuk kueri yang mengirimkan data dalam jumlah besar?

Pasti ada masalah serius hingga kueri Anda membutuhkan waktu 2 jam untuk dieksekusi ketika saya dapat melakukan hal yang sama dalam waktu kurang dari 60 detik pada perangkat keras yang serupa.

Beberapa hal berikut mungkin terbukti membantu...

Setel MySQL untuk mesin Anda

Periksa konfigurasi server Anda dan optimalkan. Beberapa sumber daya berikut akan berguna.

Sekarang untuk yang kurang jelas...

Pertimbangkan untuk menggunakan prosedur tersimpan untuk memproses sisi server data

Mengapa tidak memproses semua data di dalam MySQL sehingga Anda tidak perlu mengirim data dalam jumlah besar ke lapisan aplikasi Anda? Contoh berikut menggunakan kursor untuk mengulang dan memproses 50 juta baris sisi server dalam waktu kurang dari 2 menit. Saya bukan penggemar berat kursor, terutama di MySQL di mana mereka sangat terbatas, tapi saya kira Anda akan mengulang hasil dan melakukan beberapa bentuk analisis numerik sehingga penggunaan kursor dapat dibenarkan dalam kasus ini.

Tabel hasil myisam yang disederhanakan - kunci berdasarkan milik Anda.

drop table if exists results_1mregr_c_ew_f;
create table results_1mregr_c_ew_f
(
id int unsigned not null auto_increment primary key,
rc tinyint unsigned not null,
df int unsigned not null default 0,
val double(10,4) not null default 0,
ts timestamp not null default now(),
key (rc, df)
)
engine=myisam;

Saya membuat 100 juta baris data dengan bidang kunci yang kira-kira memiliki kardinalitas yang sama seperti pada contoh Anda:

show indexes from results_1mregr_c_ew_f;

Table                   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Index_type
=====                   ==========  ========    ============    =========== =========   =========== ==========
results_1mregr_c_ew_f       0       PRIMARY         1               id          A       100000000   BTREE   
results_1mregr_c_ew_f       1       rc              1               rc          A               2   BTREE   
results_1mregr_c_ew_f       1       rc              2               df          A             223   BTREE   

Prosedur tersimpan

Saya membuat prosedur tersimpan sederhana yang mengambil data yang diperlukan dan memprosesnya (menggunakan kondisi tempat yang sama seperti contoh Anda)

drop procedure if exists process_results_1mregr_c_ew_f;

delimiter #

create procedure process_results_1mregr_c_ew_f
(
in p_rc tinyint unsigned,
in p_df int unsigned
)
begin

declare v_count int unsigned default 0;
declare v_done tinyint default 0;
declare v_id int unsigned;
declare v_result_cur cursor for select id from results_1mregr_c_ew_f where rc = p_rc and df > p_df;
declare continue handler for not found set v_done = 1;

open v_result_cur;

repeat
    fetch v_result_cur into v_id;

    set v_count = v_count + 1;
    -- do work...

until v_done end repeat;
close v_result_cur;

select v_count as counter;

end #

delimiter ; 

Runtime berikut diamati:

call process_results_1mregr_c_ew_f(0,60);

runtime 1 = 03:24.999 Query OK (3 mins 25 secs)
runtime 2 = 03:32.196 Query OK (3 mins 32 secs)

call process_results_1mregr_c_ew_f(1,60);

runtime 1 = 04:59.861 Query OK (4 mins 59 secs)
runtime 2 = 04:41.814 Query OK (4 mins 41 secs)

counter
========
23000002 (23 million rows processed in each case)

Hmmmm, performanya sedikit mengecewakan jadi ke ide selanjutnya.

Pertimbangkan untuk menggunakan mesin innodb (shock horror)

Kenapa innodb?? karena memiliki indeks berkerumun! Anda akan menemukan memasukkan lebih lambat menggunakan innodb tapi mudah-mudahan akan lebih cepat untuk membaca sehingga trade off yang mungkin worth it.

Mengakses baris melalui indeks berkerumun cepat karena data baris berada di halaman yang sama di mana pencarian indeks mengarah. Jika tabel besar, arsitektur indeks berkerumun sering menyimpan operasi I/O disk bila dibandingkan dengan organisasi penyimpanan yang menyimpan data baris menggunakan halaman yang berbeda dari catatan indeks. Misalnya, MyISAM menggunakan satu file untuk baris data dan satu lagi untuk catatan indeks.

Info lebih lanjut di sini :

Tabel hasil innodb yang disederhanakan

drop table if exists results_innodb;
create table results_innodb
(
rc tinyint unsigned not null,
df int unsigned not null default 0,
id int unsigned not null, -- cant auto_inc this !!
val double(10,4) not null default 0,
ts timestamp not null default now(),
primary key (rc, df, id) -- note clustered (innodb only !) composite PK
)
engine=innodb;

Satu masalah dengan innodb adalah tidak mendukung bidang auto_increment yang merupakan bagian dari kunci komposit sehingga Anda harus memberikan nilai kunci tambahan sendiri menggunakan generator urutan, pemicu, atau metode lain - mungkin dalam aplikasi yang mengisi tabel hasil itu sendiri ??

Sekali lagi, saya menghasilkan 100 juta baris data dengan bidang kunci yang kira-kira memiliki kardinalitas yang sama seperti pada contoh Anda. Jangan khawatir jika angka-angka ini tidak cocok dengan contoh myisam karena innodb memperkirakan kardinalitas sehingga tidak akan sama persis. (tetapi mereka - set data yang sama digunakan)

show indexes from results_innodb;

Table           Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Index_type
=====           ==========  ========    ============    =========== =========   =========== ==========
results_innodb      0       PRIMARY         1               rc          A                18     BTREE   
results_innodb      0       PRIMARY         2               df          A                18     BTREE   
results_innodb      0       PRIMARY         3               id          A         100000294     BTREE   

Prosedur tersimpan

Prosedur tersimpan persis sama dengan contoh myisam di atas tetapi memilih data dari tabel innodb.

declare v_result_cur cursor for select id from results_innodb where rc = p_rc and df > p_df;

Hasilnya adalah sebagai berikut:

call process_results_innodb(0,60);

runtime 1 = 01:53.407 Query OK (1 mins 53 secs)
runtime 2 = 01:52.088 Query OK (1 mins 52 secs)

call process_results_innodb(1,60);

runtime 1 = 02:01.201 Query OK (2 mins 01 secs)
runtime 2 = 01:49.737 Query OK (1 mins 50 secs)

counter
========
23000002 (23 million rows processed in each case)

kira-kira 2-3 menit lebih cepat daripada implementasi mesin myisam! (innodb FTW)

Membagi dan Menaklukkan

Memproses hasil dalam prosedur tersimpan sisi server yang menggunakan kursor mungkin bukan solusi yang optimal terutama karena MySQL tidak memiliki dukungan untuk hal-hal seperti array dan struktur data kompleks yang tersedia dalam bahasa 3GL seperti C# dll atau bahkan di database lain seperti sebagai Oracle PL/SQL.

Jadi idenya di sini adalah untuk mengembalikan kumpulan data ke lapisan aplikasi (C# apa pun) yang kemudian dapat menambahkan hasilnya ke struktur data berbasis kumpulan dan kemudian memproses data secara internal.

Prosedur tersimpan

Prosedur tersimpan membutuhkan 3 parameter rc, df_low dan df_high yang memungkinkan Anda memilih rentang data sebagai berikut:

call list_results_innodb(0,1,1); -- df 1
call list_results_innodb(0,1,10); -- df between 1 and 10
call list_results_innodb(0,60,120); -- df between 60 and 120 etc...

jelas semakin tinggi rentang df semakin banyak data yang akan Anda ekstrak.

drop procedure if exists list_results_innodb;

delimiter #

create procedure list_results_innodb
(
in p_rc tinyint unsigned,
in p_df_low int unsigned,
in p_df_high int unsigned
)
begin
    select rc, df, id from results_innodb where rc = p_rc and df between p_df_low and p_df_high;
end #

delimiter ; 

Saya juga mengetuk versi myisam juga yang identik kecuali untuk tabel yang digunakan.

call list_results_1mregr_c_ew_f(0,1,1);
call list_results_1mregr_c_ew_f(0,1,10);
call list_results_1mregr_c_ew_f(0,60,120);

Berdasarkan contoh kursor di atas, saya berharap versi innodb mengungguli versi myisam.

Saya mengembangkan cepat dan kotor aplikasi C# multi-utas yang akan memanggil prosedur tersimpan dan menambahkan hasilnya ke koleksi untuk pemrosesan kueri pos. Anda tidak harus menggunakan utas, pendekatan kueri batch yang sama dapat dilakukan secara berurutan tanpa banyak kehilangan kinerja.

Setiap utas (QueryThread) memilih rentang data df, mengulang kumpulan hasil, dan menambahkan setiap hasil (baris) ke kumpulan hasil.

class Program
    {
        static void Main(string[] args)
        {
            const int MAX_THREADS = 12; 
            const int MAX_RC = 120;

            List<AutoResetEvent> signals = new List<AutoResetEvent>();
            ResultDictionary results = new ResultDictionary(); // thread safe collection

            DateTime startTime = DateTime.Now;
            int step = (int)Math.Ceiling((double)MAX_RC / MAX_THREADS) -1; 

            int start = 1, end = 0;
            for (int i = 0; i < MAX_THREADS; i++){
                end = (i == MAX_THREADS - 1) ? MAX_RC : end + step;
                signals.Add(new AutoResetEvent(false));

                QueryThread st = new QueryThread(i,signals[i],results,0,start,end);
                start = end + 1;
            }
            WaitHandle.WaitAll(signals.ToArray());
            TimeSpan runTime = DateTime.Now - startTime;

            Console.WriteLine("{0} results fetched and looped in {1} secs\nPress any key", results.Count, runTime.ToString());
            Console.ReadKey();
        }
    }

Runtime diamati sebagai berikut:

Thread 04 done - 31580517
Thread 06 done - 44313475
Thread 07 done - 45776055
Thread 03 done - 46292196
Thread 00 done - 47008566
Thread 10 done - 47910554
Thread 02 done - 48194632
Thread 09 done - 48201782
Thread 05 done - 48253744
Thread 08 done - 48332639
Thread 01 done - 48496235
Thread 11 done - 50000000
50000000 results fetched and looped in 00:00:55.5731786 secs
Press any key

Jadi 50 juta baris diambil dan ditambahkan ke koleksi dalam waktu kurang dari 60 detik.

Saya mencoba hal yang sama menggunakan prosedur tersimpan myisam yang membutuhkan waktu 2 menit untuk menyelesaikannya.

50000000 results fetched and looped in 00:01:59.2144880 secs

Pindah ke innodb

Dalam sistem saya yang disederhanakan, tabel myisam tidak berkinerja terlalu buruk sehingga mungkin tidak layak untuk dimigrasikan ke innodb. Jika Anda memutuskan untuk menyalin data hasil Anda ke tabel innodb, lakukan sebagai berikut:

start transaction;

insert into results_innodb 
 select <fields...> from results_1mregr_c_ew_f order by <innodb primary key>;

commit;

Memesan hasil oleh innodb PK sebelum memasukkan dan membungkus semuanya dalam suatu transaksi akan mempercepat segalanya.

Saya harap beberapa di antaranya terbukti bermanfaat.

Semoga berhasil




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara Menonaktifkan Pemeriksaan Kunci Asing di MySQL

  2. Bagaimana mencegah duplikat nama pengguna saat orang mendaftar?

  3. onbeforeprint() dan onafterprint() setara untuk browser non IE

  4. Apa perbedaan antara utf8_general_ci dan utf8_unicode_ci?

  5. Membangun kembali Budak Replikasi MySQL 8.0 Menggunakan Plugin Klon