desain tabel / indeks mysql efektif untuk 35 juta baris + tabel, dengan 200+ kolom yang sesuai (dua kali lipat), kombinasi apa pun yang dapat ditanyakan

17

Saya mencari saran tentang desain tabel / indeks untuk situasi berikut:

Saya memiliki tabel besar (data riwayat harga saham, InnoDB, 35 juta baris dan terus bertambah) dengan kunci primer majemuk (asetid (int), tanggal (tanggal)). selain informasi harga, saya memiliki 200 nilai ganda yang harus sesuai dengan setiap catatan.

CREATE TABLE `mytable` (
`assetid` int(11) NOT NULL,
`date` date NOT NULL,
`close` double NOT NULL,
`f1` double DEFAULT NULL,   
`f2` double DEFAULT NULL,
`f3` double DEFAULT NULL,   
`f4` double DEFAULT NULL,
 ... skip a few 
`f200` double DEFAULT NULL, 
PRIMARY KEY (`assetid`, `date`)) ENGINE=`InnoDB` DEFAULT CHARACTER SET latin1 COLLATE
    latin1_swedish_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0 
    PARTITION BY RANGE COLUMNS(`date`) PARTITIONS 51;

saya awalnya menyimpan 200 kolom ganda secara langsung dalam tabel ini untuk kemudahan pembaruan dan pengambilan, dan ini telah berfungsi dengan baik, karena satu-satunya kueri yang dilakukan pada tabel ini adalah oleh assetid dan tanggal (ini secara religius dimasukkan dalam permintaan apa pun terhadap tabel ini ), dan 200 kolom ganda hanya dibaca. Ukuran basis data saya sekitar 45 Gig

Namun, sekarang saya memiliki persyaratan di mana saya harus dapat menanyakan tabel ini dengan kombinasi dari 200 kolom ini (dinamai f1, f2, ... f200), misalnya:

select from mytable 
where assetid in (1,2,3,4,5,6,7,....)
and date > '2010-1-1' and date < '2013-4-5'
and f1 > -0.23 and f1 < 0.9
and f117 > 0.012 and f117 < .877
etc,etc

saya tidak secara historis harus berurusan dengan jumlah data yang besar ini sebelumnya, jadi insting pertama saya adalah bahwa indeks diperlukan pada masing-masing dari 200 kolom ini, atau saya akan berakhir dengan scan tabel besar, dll. Bagi saya ini berarti bahwa saya membutuhkan tabel untuk masing-masing 200 kolom dengan kunci utama, nilai, dan indeks nilai-nilai. Jadi saya pergi dengan itu.

CREATE TABLE `f1` (
`assetid` int(11) NOT NULL DEFAULT '0',
`date` date NOT NULL DEFAULT '0000-00-00',
`value` double NOT NULL DEFAULT '0',
PRIMARY KEY (`assetid`, `date`),
INDEX `val` (`value`)
) ENGINE=`InnoDB` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0;

saya mengisi dan mengindeks semua 200 tabel. Saya membiarkan tabel utama tetap utuh dengan semua 200 kolom, seperti yang biasa dipertanyakan tentang rentang tanggal dan asetid dan semua 200 kolom dipilih. Saya pikir meninggalkan kolom-kolom itu di tabel induk (tidak diindeks) untuk tujuan baca, dan kemudian membuat mereka diindeks di tabel mereka sendiri (untuk penyaringan bergabung) akan menjadi yang paling performant. Saya berlari menjelaskan pada formulir baru permintaan

select count(p.assetid) as total 
from mytable p 
inner join f1 f1 on f1.assetid = p.assetid and f1.date = p.date
inner join f2 f2 on f2.assetid = p.assetid and f2.date = p.date 
where p.assetid in(1,2,3,4,5,6,7)
and p.date >= '2011-01-01' and p.date < '2013-03-14' 
and(f1.value >= 0.96 and f1.value <= 0.97 and f2.value >= 0.96 and f2.value <= 0.97) 

Memang hasil yang saya inginkan tercapai, jelaskan menunjukkan kepada saya bahwa baris yang dipindai jauh lebih kecil untuk permintaan ini. Namun saya akhirnya dengan beberapa efek samping yang tidak diinginkan.

1) database saya berubah dari 45 Gig menjadi 110 Gig. Saya tidak bisa lagi menyimpan db di RAM. (Saya memiliki 256Gig RAM di jalan namun)

2) memasukkan data baru setiap malam sekarang harus dilakukan 200 kali, bukan sekali

3) pemeliharaan / defrag dari 200 tabel baru memakan waktu 200 kali lebih lama dari hanya 1 tabel. Itu tidak bisa diselesaikan dalam semalam.

4) kueri terhadap f1, dll tabel tidak selalu berkinerja. sebagai contoh:

 select min(value) from f1 
 where assetid in (1,2,3,4,5,6,7) 
 and date >= '2013-3-18' and date < '2013-3-19'

kueri di atas, sementara menjelaskan menunjukkan bahwa itu terlihat di <1000 baris, bisa memakan waktu 30 + detik untuk menyelesaikan. Saya berasumsi ini karena indeks terlalu besar untuk muat di memori.

Karena itu banyak berita buruk, saya mencari lebih jauh dan menemukan partisi. Saya menerapkan partisi pada tabel utama, dipartisi pada tanggal setiap 3 bulan. Bulanan sepertinya masuk akal bagi saya tetapi saya telah membaca bahwa setelah Anda mendapatkan lebih dari 120 partisi atau lebih, kinerja menderita. mempartisi setiap triwulan akan membuat saya merasa seperti itu selama 20 tahun ke depan. setiap partisi sedikit di bawah 2 Gig. Saya menjalankan menjelaskan partisi dan semuanya tampaknya pemangkasan dengan benar, jadi terlepas saya merasa partisi adalah langkah yang baik, paling tidak untuk menganalisis / mengoptimalkan / memperbaiki tujuan.

Saya menghabiskan banyak waktu dengan artikel ini

http://ftp.nchu.edu.tw/MySQL/tech-resources/articles/testing-partitions-large-db.html

meja saya saat ini dipartisi dengan kunci primer yang masih ada di sana. Artikel itu menyebutkan bahwa kunci utama dapat membuat tabel dipartisi lebih lambat, tetapi jika Anda memiliki mesin yang bisa mengatasinya, kunci utama pada tabel dipartisi akan lebih cepat. Mengetahui saya memiliki mesin besar di jalan (256 G RAM), saya meninggalkan tombol.

jadi seperti yang saya lihat, berikut adalah opsi saya

Pilihan 1

1) hapus 200 tabel tambahan dan biarkan query do table scan untuk menemukan nilai f1, f2 dll. indeks non-unik sebenarnya dapat merusak kinerja pada tabel yang dipartisi dengan benar. menjalankan penjelasan sebelum pengguna menjalankan kueri dan menolaknya jika jumlah baris yang dipindai melebihi batas yang saya tentukan. selamatkan diri saya dari sakitnya database raksasa. Heck, semuanya akan segera tersimpan dalam memori.

sub pertanyaan:

apakah ini terdengar seperti saya telah memilih skema partisi yang sesuai?

pilihan 2

Partisi semua 200 tabel menggunakan skema 3 bulan yang sama. nikmati pemindaian baris yang lebih kecil dan izinkan pengguna menjalankan kueri yang lebih besar. sekarang mereka dipartisi setidaknya saya bisa mengelolanya 1 partisi sekaligus untuk keperluan pemeliharaan. Heck, semuanya akan segera tersimpan dalam memori. Kembangkan cara yang efisien untuk memperbaruinya setiap malam.

sub pertanyaan:

Apakah Anda melihat alasan bahwa saya dapat menghindari indeks kunci utama pada tabel f1, f2, f3, f4 ini, mengetahui bahwa saya selalu memiliki aset dan tanggal saat kueri? tampaknya kontra intuitif bagi saya tetapi saya tidak terbiasa dengan set data ukuran ini. itu akan mengecilkan banyak database yang saya asumsikan

Opsi 3

Letakkan kolom f1, f2, f3 di tabel master untuk mendapatkan kembali ruang itu. lakukan 200 bergabung jika saya perlu membaca 200 fitur, mungkin tidak akan selambat kedengarannya.

Opsi 4

Anda semua memiliki cara yang lebih baik untuk menyusun ini daripada yang saya pikirkan sejauh ini.

* CATATAN: Saya akan segera menambahkan 50-100 nilai ganda ini untuk setiap item, jadi saya perlu mendesain mengetahui bahwa akan datang.

Terima kasih atas bantuan Anda

Pembaruan # 1 - 3/24/2013

Saya pergi dengan ide yang disarankan dalam komentar yang saya dapatkan di bawah dan membuat satu tabel baru dengan pengaturan berikut:

create table 'features'{
  assetid int,
  date    date,
  feature varchar(4),
  value   double
}

Saya mempartisi tabel dalam interval 3 bulan.

Saya meniup 200 tabel sebelumnya sehingga database saya kembali ke 45 Gig dan mulai mengisi tabel baru ini. Satu setengah hari kemudian, itu selesai, dan database saya sekarang duduk di Gigs 220 Gigs!

Itu memungkinkan kemungkinan menghapus 200 nilai-nilai ini dari tabel master, karena saya bisa mendapatkannya dari satu bergabung, tapi itu benar-benar hanya akan memberi saya kembali 25 Gigs atau lebih mungkin

Saya memintanya untuk membuat kunci utama pada assetid, tanggal, fitur dan indeks pada nilai, dan setelah 9 jam menenggak itu benar-benar tidak membuat penyok dan sepertinya membeku sehingga saya membunuh bagian itu.

Saya membangun kembali beberapa partisi tetapi sepertinya tidak mendapatkan kembali banyak ruang.

Jadi solusi itu sepertinya tidak akan ideal. Apakah baris memakan lebih banyak ruang secara signifikan daripada kolom yang saya bayangkan, mungkinkah mengapa solusi ini mengambil lebih banyak ruang?

Saya menemukan artikel ini:

http://www.chrismoos.com/2010/01/31/mysql-partitioning-tables-with-millions-of-rows

itu memberi saya ide. Ia mengatakan:

Pada awalnya, saya memikirkan tentang partisi RANGE berdasarkan tanggal, dan sementara saya menggunakan tanggal di kueri saya, sangat umum untuk kueri memiliki rentang tanggal yang sangat besar, dan itu berarti ia dapat dengan mudah menjangkau semua partisi.

Sekarang saya juga mempartisi kisaran tanggal, tetapi juga akan memungkinkan pencarian berdasarkan rentang tanggal yang besar, yang akan mengurangi efektivitas partisi saya. Saya akan selalu memiliki rentang tanggal ketika saya mencari, namun saya juga akan selalu memiliki daftar aset. Mungkin solusi saya adalah dengan mempartisi dengan asetid dan tanggal, di mana saya mengidentifikasi rentang asetid yang dicari secara khusus (yang dapat saya temukan, ada daftar standar, S&P 500, Russell 2000, dll). Dengan cara ini saya hampir tidak akan pernah melihat seluruh kumpulan data.

Lagipula, saya kunci utama pada asetid dan berkencan, jadi mungkin itu tidak banyak membantu.

Setiap pemikiran / komentar lagi akan dihargai.

pewarna
sumber
2
Saya gagal melihat mengapa Anda membutuhkan 200 tabel. Sebuah meja tunggal dengan (value_name varchar(20), value double)akan mampu menyimpan segala sesuatu ( value_namemenjadi f1, f2, ...)
a_horse_with_no_name
Terima kasih. alasan saya menempatkan mereka secara individual adalah untuk mendapatkan dengan batas 50 indeks di atas meja. Saya telah berpikir tentang menempatkan mereka ke dalam 5 tabel, masing-masing 40 nilai, tetapi saya memasukkan 17000 atau lebih catatan sehari untuk masing-masing dan tidak tahu seperti apa kinerja insert pada tabel dengan 40 indeks. perhatikan bahwa setiap kombinasi asetid, tanggal mendapatkan nilai f1, f2 ... sendiri. Apakah Anda menyarankan satu tabel dengan (assetid, date, value_name, value), dengan primary key assetid, date, mungkin index on (value_name, value)? tabel itu akan memiliki 35 juta * 200 = 7 miliar baris tetapi mungkin dipartisi dengan baik akan berhasil?
dyeryn
pos terbaru dengan pengalaman saya mencoba metode ini
dyeryn
saya punya solusi akhir dalam pengembangan, saya akan memperbarui ketika saya selesai. itu pada dasarnya solusi tabel tunggal yang diusulkan di sini dengan partisi khusus dan sharding logis.
dyeryn
Mungkinkah bantuan mesin penyimpanan berbeda? Alih-alih InnoDb mungkin mencoba InfiniDB? Data kolom, pola akses terlihat seperti pembaruan kumpulan besar, pembacaan berbasis rentang, dan pemeliharaan tabel minimal.
berantakan

Jawaban:

1

kebetulan saya juga melihat ke salah satu dukungan klien di mana kami merancang struktur pasangan kunci-nilai untuk fleksibilitas dan saat ini tabel lebih dari 1.5B baris dan ETL terlalu lambat. baik ada banyak hal lain dalam kasus saya tetapi apakah Anda sudah memikirkan desain itu. Anda akan memiliki satu baris dengan semua nilai sekarang 200 kolom, baris itu akan dikonversi menjadi 200 baris dalam desain pasangan Key-Value. Anda akan mendapatkan keuntungan ruang dengan desain ini tergantung pada untuk AssetID dan Tanggal yang diberikan berapa banyak baris yang benar-benar hadir dengan semua nilai 200 f1 hingga f200? jika Anda mengatakan bahkan 30% kolom od memiliki nilai NULL dari itu adalah penghematan ruang Anda. karena dalam desain pasangan kunci-nilai jika nilai id NULL baris itu tidak perlu dalam tabel. tetapi dalam desain struktur kolom yang ada bahkan NULL mengambil ruang. (Saya tidak 100% yakin tetapi jika Anda memiliki lebih dari 30 kolom NULL dalam tabel maka NULL mengambil 4bytes). jika Anda melihat desain ini dan menganggap bahwa semua baris 35M memiliki nilai di semua 200 kolom maka Anda saat ini db akan menjadi 200 * 35M = 700M baris dalam tabel segera. tetapi itu tidak akan jauh tinggi di ruang tabel apa yang Anda miliki dengan semua kolom dalam tabel tunggal karena kami baru saja memindahkan kolom ke baris. dalam operasi transpos ini sebenarnya kita tidak akan memiliki baris yang nilainya NULL. jadi Anda benar-benar dapat menjalankan kueri terhadap tabel ini dan melihat berapa banyak nol yang ada dan memperkirakan ukuran tabel target Anda sebelum Anda benar-benar mengimplementasikannya. tetapi itu tidak akan jauh tinggi di ruang tabel apa yang Anda miliki dengan semua kolom dalam tabel tunggal karena kami baru saja memindahkan kolom ke baris. dalam operasi transpos ini sebenarnya kita tidak akan memiliki baris yang nilainya NULL. jadi Anda benar-benar dapat menjalankan kueri terhadap tabel ini dan melihat berapa banyak nol yang ada dan memperkirakan ukuran tabel target Anda sebelum Anda benar-benar mengimplementasikannya. tetapi itu tidak akan jauh tinggi di ruang tabel apa yang Anda miliki dengan semua kolom dalam tabel tunggal karena kami baru saja memindahkan kolom ke baris. dalam operasi transpos ini sebenarnya kita tidak akan memiliki baris yang nilainya NULL. jadi Anda benar-benar dapat menjalankan kueri terhadap tabel ini dan melihat berapa banyak nol yang ada dan memperkirakan ukuran tabel target Anda sebelum Anda benar-benar mengimplementasikannya.

Keuntungan kedua adalah kinerja membaca. seperti yang Anda sebutkan bahwa cara baru untuk menanyakan data adalah kombinasi apa pun dari kolom f1 ke f200 ini di mana klausa. dengan pasangan kunci desain nilai f1 ke f200 hadir dalam satu kolom katakanlah "FildName" dan nilainya ada di kolom kedua katakanlah "FieldValue". Anda dapat memiliki indeks CLUSTERED di kedua kolom. permintaan Anda akan menjadi UNION dari Pilihan tersebut.

WHERE (FiledName = 'f1' dan FieldValue ANTARA 5 DAN 6)

PERSATUAN

(FiledName = 'f2' dan FieldValue ANTARA 8 DAN 10)

dll .....

Saya akan memberi Anda beberapa nomor kinerja dari server server yang sebenarnya. kami memiliki 75 kolom harga untuk setiap TIKER keamanan.

Anup Shah
sumber
1

Dalam berurusan dengan data seperti ini di mana Anda perlu memasukkan banyak baris dan Anda juga membutuhkan kinerja kueri analitik yang sangat baik (saya membuat asumsi bahwa ini yang terjadi di sini), Anda mungkin menemukan bahwa RDBMS kolumnar cocok dengan baik . Lihatlah Infobright CE dan InfiniDB CE (kedua mesin penyimpan kolom dicolokkan ke MySQL), dan Vertica CE juga (lebih mirip PostgreSQL daripada suka MySQL) ... semua Edisi Komunitas ini gratis (walaupun Vertica tidak open source, skala ke 3 node dan 1TB data gratis). RDBMS kolom biasanya menawarkan waktu respons "kueri besar" yang 10-100X lebih baik daripada berbasis baris, dan waktu muat yang 5-50X lebih baik. Anda harus menggunakannya dengan benar atau mereka bau (jangan melakukan operasi baris tunggal ... melakukan semua operasi dalam pendekatan massal), tetapi digunakan dengan benar mereka benar-benar bergoyang. ;-)

HTH, Dave Sisk

Dave Sisk
sumber
1
Kami memiliki hampir satu miliar baris data tipe clickstream (tidak berbeda dengan data ticker saham) dalam instalasi Vertica 3 simpul ... kami dapat memuat data sepanjang hari dalam waktu sekitar 15 detik, dan kami mendapatkan waktu respons kueri dalam kisaran 500 milidetik. Dalam kasus Anda, tentu ini terdengar layak untuk dilihat.
Dave Sisk
Saya bisa menjamin hal yang sama. Di perusahaan terakhir saya, kami memiliki 8 simpul Vertica cluster dengan sekitar jumlah baris yang sama dan permintaan agregat sederhana-ish atas seluruh set yang dikembalikan dalam 1-3 detik (rata-rata). Itu juga sekitar 1/4 biaya cluster Greenplum kami sebelumnya.
bma