Я шукаю поради щодо дизайну таблиць / індексів для наступної ситуації:
У мене є велика таблиця (дані історії цін на акції, InnoDB, 35 мільйонів рядків і зростаюча) зі складеним первинним ключем (assetid (int), дата (дата)). крім інформації про ціни, у мене є 200 подвійних значень, які потрібно відповідати кожному запису.
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;
Я спочатку зберігав 200 подвійних стовпців безпосередньо в цій таблиці для зручності оновлення та пошуку, і це працювало чудово, оскільки єдиний запит, виконаний у цій таблиці, був за вказівкою та датою (вони релігійно включені в будь-який запит проти цієї таблиці ), а 200 подвійних стовпців було лише прочитано. Розмір моєї бази даних був близько 45 гіг
Однак тепер у мене є вимога, де мені потрібно мати можливість запитувати цю таблицю за будь-якою комбінацією цих 200 стовпців (з назвою f1, f2, ... f200), наприклад:
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
Мені раніше не доводилося стикатися з цією великою кількістю даних, тому першим моїм інстинктом було те, що в кожному з цих 200 стовпців потрібні індекси, або я б завершив великі сканування таблиці тощо. Мені це означало, що мені потрібна таблиця для кожного з 200 стовпців з первинним ключем, значенням та індексом значень. Тому я пішов з цим.
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;
я заповнив і проіндексував усі 200 таблиць. Я залишив головну таблицю недоторканою з усіма 200 стовпцями, оскільки регулярно її запитують за в'язаним і діапазоном дат, і всі 200 стовпців вибираються. Я подумав, що залишити ці стовпчики у батьківській таблиці (недекларовані) для цілей читання, а потім додатково індексувати їх у власних таблицях (для фільтрації приєднання) було б найбільш ефективно. Я побіг пояснює нову форму запиту
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)
Дійсно, мій бажаний результат був досягнутий, пояснення показує мені, що відскановані рядки значно менші для цього запиту. Однак я завершив деякі небажані побічні ефекти.
1) моя база даних перейшла з 45 Гіг до 110 Гіг. Я більше не можу тримати db в оперативній пам’яті. (У мене на шляху 256 ГГ ОЗУ)
2) вночі нових даних тепер потрібно робити 200 разів, а не один раз
3) обслуговування / дефрагментація нових 200 столів займає в 200 разів довше, ніж просто 1 стіл. Це не може бути завершено за ніч.
4) запити до таблиць f1 тощо не обов'язково виконуються. наприклад:
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'
Наведений вище запит, хоча пояснення показує, що його перегляд у <1000 рядків може виконати 30+ секунд. Я припускаю, що це тому, що індекси занадто великі, щоб вміститись у пам'яті.
Оскільки це було багато поганих новин, я подивився далі і знайшов перегородку. Я реалізував розділи на головній таблиці, розміщенні на даті кожні 3 місяці. Щомісяця, здавалося, має сенс для мене, але я читав, що як тільки ви отримуєте понад 120 розділів або близько того, продуктивність страждає. розділення щокварталу залишить мене під цим протягом наступних 20 років. кожен розділ трохи менше 2 гіг. Я побіг пояснити розділи, і все, здається, обрізає належним чином, тому незалежно від того, я вважаю, що розділення було гарним кроком, принаймні для аналізу / оптимізації / ремонту.
Я витратив багато часу на цю статтю
http://ftp.nchu.edu.tw/MySQL/tech-resources/articles/testing-partitions-large-db.html
наразі моя таблиця розміщена з первинним ключем, який все ще знаходиться на ній. У статті йдеться про те, що первинні ключі можуть робити розподілену таблицю повільнішою, але якщо у вас є машина, яка може її обробляти, первинні ключі в розділеній таблиці будуть швидшими. Знаючи, що у мене на шляху велика машина (256 Г ОЗУ), я залишив ключі.
так що, як я бачу, ось мої варіанти
Варіант 1
1) видаліть зайві 200 таблиць і нехай запит сканує таблицю, щоб знайти значення f1, f2 тощо. не унікальні індекси можуть насправді пошкодити продуктивність на правильно розподіленій таблиці. запустіть пояснення перед тим, як користувач запустить запит, і відмовить їх, якщо кількість сканованих рядків перевищує деякий визначений я поріг. врятуй собі біль від гігантської бази даних. Чорт забирай, все одно це запам’ятається незабаром.
під питання:
це звучить так, ніби я обрав відповідну схему розділів?
Варіант 2
Розділіть усі 200 таблиць за тією ж схемою на три місяці. користуйтеся меншими скануваннями рядків і дозволяйте користувачам виконувати більші запити. тепер, коли вони розділені принаймні, я можу керувати ними по 1 розділу одночасно з метою обслуговування. Чорт забирай, все одно це запам’ятається незабаром. Розробіть ефективний спосіб оновлювати їх щоночі.
під питання:
Чи бачите ви причину того, що я можу уникати індексів первинного ключа на цих таблицях f1, f2, f3, f4 ..., знаючи, що я завжди маю вкладені дати та дати при запиті? мені здається протилежним інтуїтивним, але я не звик до наборів даних такого розміру. це припустило б зменшити базу даних
Варіант 3
Відкиньте стовпці f1, f2, f3 у головну таблицю, щоб повернути цей пробіл. зробити 200 приєднується, якщо мені потрібно прочитати 200 функцій, можливо, це буде не так повільно, як це звучить.
Варіант 4
Ви всі маєте кращий спосіб структурувати це, ніж я думав досі.
* ПРИМІТКА. Незабаром я додаю ще 50-100 цих подвійних значень до кожного елемента, тому мені потрібно спроектувати, знаючи, що буде.
Дякую за будь-яку допомогу
Оновлення №1 - 24.03.2013
Я пішов з ідеєю, запропонованою в коментарях, які я отримав нижче, і створив одну нову таблицю з наступною настройкою:
create table 'features'{
assetid int,
date date,
feature varchar(4),
value double
}
Я розділив таблицю з інтервалом у 3 місяці.
Я підірвав попередні 200 таблиць, щоб моя база даних повернулася до 45 Gig і почав заповнювати цю нову таблицю. Через півтора дня він завершився, і моя база даних зараз сидить на пухких 220 гігах!
Це дає можливість вилучити ці 200 значень з головної таблиці, оскільки я можу отримати їх за один приєднання, але це справді поверне мені лише 25 Gigs або так, можливо
Я попросив його створити первинний ключ на позначення, дату, функцію та індекс вартості, і після 9 годин забивання він справді не зробив зуб і, здавалося, замерзнув, тому я вбив цю частину.
Я відновив пару перегородок, але, здавалося, не повернув багато / жодного місця.
Тож таке рішення виглядає так, що, ймовірно, не буде ідеальним. Чи займають рядки значно більше місця, ніж стовпці. Цікаво, чи може це рішення займає набагато більше місця?
Я натрапив на цю статтю:
http://www.chrismoos.com/2010/01/31/mysql-partitioning-tables-with-millions-of-rows
це дало мені ідею. Він говорить:
Спочатку я думав про RANGE розділення за датою, і, хоча я використовую дату у своїх запитах, дуже часто для запиту є дуже великий діапазон дат, і це означає, що він може легко охопити всі розділи.
Тепер я розбиваю діапазон за датою, але також дозволятиму здійснювати пошук за великим діапазоном дат, що знизить ефективність мого розділу. У мене завжди буде діапазон дат, коли я шукаю, однак у мене також завжди буде список зборів. Можливо, моїм рішенням має бути розподіл за версією та датою, де я визначаю типово шукані діапазони аситидів (які я можу придумати, є стандартні списки, S&P 500, Russell 2000 тощо). Таким чином я майже ніколи не переглянув би весь набір даних.
Тоді знову, я все-таки маю на увазі основні дати та дати, так що, можливо, це не дуже допоможе.
Будь-які думки / коментарі будуть вдячні.
(value_name varchar(20), value double)
буде мати можливість зберігати всі (value_name
будучиf1
,f2
...)