Aşağıdaki durum için tablo / dizin tasarımı konusunda tavsiye arıyorum:
Bileşik birincil anahtar (varlık kimliği (int), tarih (tarih)) ile büyük bir tablo (hisse senedi fiyat geçmişi verileri, InnoDB, 35 milyon satır ve büyüyen) var. fiyatlandırma bilgilerine ek olarak, her bir kayda karşılık gelmesi gereken 200 çift değer var.
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;
Başlangıçta güncelleme ve alma kolaylığı için 200 çift sütun doğrudan bu tabloda saklanan ve bu tablo üzerinde yapılan tek sorgu varlık ve tarih (bu dini karşı bu tabloya karşı herhangi bir sorguda dahil olduğu gibi) iyi çalışıyor olmuştu ) ve 200 çift sütun yalnızca okundu. Veritabanı boyutum yaklaşık 45 Gig idi
Ancak, şimdi ben bu tabloyu bu 200 sütun (f1, f2, ... f200 adlı) herhangi bir kombinasyonu ile sorgulamak gerekir gerekir gereksinimi var:
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
tarihsel olarak daha önce bu kadar büyük miktarda veri ile uğraşmak zorunda kalmadım, bu yüzden ilk içgüdüm, bu 200 sütunun her birinde indekslere ihtiyaç duyulduğu ya da büyük tablo taramaları vb. birincil anahtar, değer ve değerleri dizin 200 sütunların her biri için bir tablo gerekli. Ben de bununla gittim.
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;
i doldurdu ve tüm 200 tablolar dizin. Düzenli olarak varlık kimliği ve tarih aralığı üzerinden sorgulandığı ve 200 sütunun tümü seçildiği için ana tabloyu 200 sütunun tümüyle sağlam bıraktım. Ben bu sütunları okuma amacıyla üst tabloda (unindexed) bırakmanın ve daha sonra ek olarak kendi tablolarında (birleştirme filtrelemesi için) dizine eklenmesinin en yüksek performans olacağını düşündüm. Sorgunun yeni formunu açıkladım
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)
Gerçekten istenen sonucum elde edildi, açıklamak bana taranan satırların bu sorgu için çok daha küçük olduğunu gösterir. Ancak bazı istenmeyen yan etkileri ile yara.
1) Veritabanım 45 Gig'den 110 Gig'e gitti. Artık db'yi RAM'de tutamıyorum. (Ancak yolda 256Gig RAM var)
2) Yeni verilerin gece eklenmesi artık bir kez yerine 200 kez yapılmalıdır.
3) yeni 200 masanın bakımı / dolandırmak sadece 1 masanın 200 kat daha uzun sürer. Bir gecede tamamlanamaz.
4) f1 vb. Tablolara karşı yapılan sorguların mutlaka performans göstermesi gerekmez. Örneğin:
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'
Yukarıdaki sorgu, açıklamanın <1000 satıra baktığını gösterirken, tamamlanması 30+ saniye sürebilir. Bunun, dizinlerin belleğe sığmayacak kadar büyük olması nedeniyle olduğunu varsayalım.
Bu çok kötü bir haber olduğundan, daha fazla baktım ve bölümleme buldum. Ana tabloya, her 3 ayda bir bölümlenmiş bölümler uyguladım. Aylık bana mantıklı geldi ama 120'den fazla bölüm aldığınızda performansın düştüğünü okudum. üç ayda bir bölümleme yapmak önümüzdeki 20 yıl boyunca beni bunun altında bırakacak. her bölüm 2 Gig altında biraz. bölümleri açıklamak koştu ve her şey düzgün budama gibi görünüyor, bu yüzden bölümleme en azından analiz / optimize / onarım amaçlı en iyi adım olduğunu hissediyorum.
Bu yazı ile çok zaman geçirdim
http://ftp.nchu.edu.tw/MySQL/tech-resources/articles/testing-partitions-large-db.html
Şu anda benim tablo hala üzerinde birincil anahtar ile bölümlenmiştir. Makale, birincil anahtarların bölümlenmiş bir tabloyu yavaşlatabileceğinden bahseder, ancak bunu işleyebilecek bir makineniz varsa, bölümlenmiş tablodaki birincil anahtarlar daha hızlı olacaktır. Yolda büyük bir makinem olduğunu bilerek (256 G RAM), tuşları açık bıraktım.
gördüğüm gibi, işte benim seçeneklerim
seçenek 1
1) ekstra 200 tabloyu kaldırın ve sorgunun f1, f2 vb değerlerini bulmak için tablo taramaları yapmasına izin verin. benzersiz olmayan dizinler aslında düzgün bir şekilde bölümlenmiş bir tabloda performansa zarar verebilir. kullanıcı sorguyu çalıştırmadan önce bir açıklama çalıştırın ve taranan satır sayısı tanımladığım bir eşiğin üzerindeyse bunları reddet. Kendimi dev veritabanının acılarından kurtarıyorum. Heck, hepsi yakında hafızada olacak.
alt sorusu:
uygun bir bölüm şeması seçmişim gibi görünüyor mu?
seçenek 2
Tüm 200 tabloları aynı 3 aylık şemayı kullanarak bölün. daha küçük satır taramalarının keyfini çıkarın ve kullanıcıların daha büyük sorgular çalıştırmasına izin verin. şimdi en azından bölümlere ayrıldığına göre bakım amacıyla bir seferde 1 bölüm yönetebilirim. Heck, hepsi yakında hafızada olacak. Bunları her gece güncellemek için etkili bir yol geliştirin.
alt sorusu:
Bu f1, f2, f3, f4 ... tablolarında birincil anahtar dizinlerinden kaçınmamın bir nedeni var mı? bana karşı sezgisel görünüyor ama bu boyuttaki veri kümelerine alışkın değilim. Ben varsayıyorum bir demet veritabanını küçültecek
Seçenek 3
Bu alanı geri kazanmak için ana tabloya f1, f2, f3 sütunlarını bırakın. 200 özelliği okumak gerekiyorsa 200 birleşimler yapmak, belki sesler kadar yavaş olmayacak.
Seçenek 4
Hepinizin bunu yapılandırmak için şimdiye kadar düşündüğümden daha iyi bir yolu var.
* NOT: Yakında her bir öğeye bu çift değerlerden başka bir 50-100 ekleyeceğim, bu yüzden geleceğini bilerek tasarlamam gerekiyor.
Her türlü yardım için teşekkürler
Güncelleme # 1 - 3/24/2013
Aşağıda aldığım yorumlarda önerilen fikirle gittim ve aşağıdaki kurulumla yeni bir tablo oluşturdum:
create table 'features'{
assetid int,
date date,
feature varchar(4),
value double
}
Tabloyu 3 ay arayla ayırdım.
Daha önceki 200 tabloyu havaya uçurdum, böylece veritabanım tekrar 45 Gig'e düştü ve bu yeni tabloyu doldurmaya başladı. Bir buçuk gün sonra, tamamlandı ve veritabanım şimdi tombul 220 konserde oturuyor !
Bu 200 değeri ana tablodan kaldırma olasılığına izin verir, çünkü onları bir birleştirmeden alabilirim, ama bu gerçekten sadece bana 25 Gigs ya da öylesine geri verirdi
Varlık, tarih, özellik ve değer üzerinde bir dizin üzerinde birincil bir anahtar oluşturmak istedi ve 9 saat chugging sonra gerçekten bir göçük yapmamıştı ve donmuş gibi görünüyordu bu yüzden bu kısmı öldürdü.
Birkaç bölüm yeniden inşa etti ama çok / herhangi bir alan geri almak gibi görünmüyordu.
Yani bu çözüm muhtemelen ideal olmayacak gibi görünüyor. Satırlar merak ettiğim sütunlardan çok daha fazla yer kaplıyor mu, bu yüzden bu çözüm çok daha fazla yer kaplayabilir mi?
Bu yazıyla karşılaştım:
http://www.chrismoos.com/2010/01/31/mysql-partitioning-tables-with-millions-of-rows
bana bir fikir verdi. Diyor ki:
İlk başta, RANGE bölümünü tarihe göre bölümlendirmeyi düşündüm ve tarihi sorgularımda kullanırken, bir sorgunun çok geniş bir tarih aralığına sahip olması çok yaygın ve bu da tüm bölümleri kolayca kapsayabileceği anlamına geliyor.
Şimdi tarihe göre bölümleme de yapıyorum, ancak bölümlememin etkinliğini azaltacak geniş tarih aralığına göre aramalara da izin vereceğim. Arama yaparken her zaman bir tarih aralığına sahip olacağım, ancak her zaman varlıkların bir listesi de olacak. Belki de benim çözüm ben tipik olarak aranan varlık kimliği aralıkları (ki ben gelebilir, standart listeler, S & P 500, Russell 2000, vb vardır) tanımlamak varlık ve tarihe göre bölümleme olmalıdır. Bu şekilde neredeyse hiçbir zaman tüm veri setine bakmazdım.
Sonra tekrar, ben birincil varlık ve zaten tarih anahtarlı, bu yüzden belki de çok yardımcı olmaz.
Daha fazla düşünce / yorum takdir edilecektir.
(value_name varchar(20), value double)
mağaza her şeyi mümkün olacaktır (value_name
varlıkf1
,f2
...)