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.
(value_name varchar(20), value double)
akan mampu menyimpan segala sesuatu (value_name
menjadif1
,f2
, ...)