ефективний дизайн таблиці та індексу mysql для 35 мільйонів рядків + таблиця з 200+ відповідними стовпцями (подвійний), будь-яку комбінацію яких можна запитати


17

Я шукаю поради щодо дизайну таблиць / індексів для наступної ситуації:

У мене є велика таблиця (дані історії цін на акції, 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 тощо). Таким чином я майже ніколи не переглянув би весь набір даних.

Тоді знову, я все-таки маю на увазі основні дати та дати, так що, можливо, це не дуже допоможе.

Будь-які думки / коментарі будуть вдячні.


2
Я не розумію, навіщо вам потрібно 200 столів. Одна таблиця (value_name varchar(20), value double)буде мати можливість зберігати всі ( value_nameбудучи f1, f2...)
— a_horse_with_no_name

Спасибі. Причиною я склав їх окремо, було отримати межу в 50 індексів на столі. Я думав про їх розміщення в 5 таблиць, по 40 значень у кожній, але я вставляю 17000 або близько того записує день для кожного, і не знав, якою буде ефективність вставки на таблиці з 40 індексами. зауважимо, що кожна комбінація вкладення, дати отримує свої власні значення f1, f2 ... Ви пропонуєте одну таблицю з (assetid, дата, value_name, значення), з первинним ключем Асtid, дата, можливо, індекс на (value_name, value)? цей стіл матиме 35 міль * 200 = 7 мільярдів рядків, але, можливо, добре розділений би спрацював?
— dyeryn

оновлений пост із моїми переживаннями цього методу
— dyeryn

У мене є остаточне рішення в розробці, я оновлю, коли закінчу. це, по суті, рішення єдиної таблиці, запропоноване тут із конкретним розділенням та логічним шартуванням.
— dyeryn

Чи може допомогти інша система зберігання даних? Замість InnoDb, можливо, спробуйте InfiniDB? Стовпчасті дані, шаблони доступу виглядають як велике пакетне оновлення, читання на основі діапазону та мінімальне обслуговування таблиці.
— безладний

Відповіді:


1

випадково я також розглядаю одну з клієнтських служб, де ми розробили структуру пар ключ-значення для гнучкості, і в даний час таблиця перевищує 1,5B рядків, а ETL є занадто повільним. ну в моєму випадку є багато інших речей, але ви думали над цим дизайном. у вас буде один рядок із усіма наявними значеннями 200 стовпців, який буде перетворений на 200 рядків у дизайні пари Key-Value. Ви отримаєте космічну перевагу за допомогою цього дизайну залежно від даного AssetID та дати, скільки рядків насправді присутні всі 200 f1 - f200 значень? якщо ви скажете, що навіть 30% стовпців мають значення NULL, ніж економія місця. тому що в дизайні пари ключ-значення, якщо значення id NULL, цей рядок не повинен бути в таблиці. але в існуючому дизайні структури стовпців навіть NULL займає місце. (Я не впевнений на 100%, але якщо у вас більше 30 стовпців NULL у таблиці, тоді NULL займає 4 байти). якщо ви бачите цю конструкцію і припускаєте, що всі 35M рядків мають значення у всіх 200 стовпцях, то поточний db відразу стане 200 * 35M = 700M рядків у таблиці. але це буде не так багато місця в таблиці, що ви мали з усіма стовпцями в одній таблиці, оскільки ми просто переносуємо стовпці в ряд. в цій операції транспонування насправді у нас не буде рядків, де значення NULL. тож ви можете фактично запустити запит до цієї таблиці і побачити, скільки є нулів, і оцінити, наскільки ви орієнтуєтесь на розмір таблиці, перш ніж реально реалізувати її. але це буде не так багато місця в таблиці, що ви мали з усіма стовпцями в одній таблиці, оскільки ми просто переносуємо стовпці в ряд. в цій операції транспонування насправді у нас не буде рядків, де значення NULL. тож ви можете фактично запустити запит до цієї таблиці і побачити, скільки є нулів, і оцінити, наскільки ви орієнтуєтесь на розмір таблиці, перш ніж реально реалізувати її. але це буде не так багато місця в таблиці, що ви мали з усіма стовпцями в одній таблиці, оскільки ми просто переносуємо стовпці в ряд. в цій операції транспонування насправді у нас не буде рядків, де значення NULL. тож ви можете фактично запустити запит до цієї таблиці і побачити, скільки є нулів, і оцінити, наскільки ви орієнтуєтесь на розмір таблиці, перш ніж реально реалізувати її.

Друга перевага - це ефективність читання. як ви вже згадували, що новим способом запиту даних є будь-яка комбінація цього стовпця f1 - f200, де пункт. з дизайном пари ключових значень f1 до f200 присутні в одному стовпчику, дозволяють сказати "FildName", а їх значення присутні у другому стовпці, щоб сказати "FieldValue". ви можете мати CLUSTERED індекс у обох стовпцях. вашим запитом буде СПІЛЬНОСТЬ цих вибраних.

ДЕ (FiledName = 'f1' та FieldValue МЕЖ 5 І 6)

СОЮЗ

(FiledName = 'f2' та FieldValue МЕЖ 8 ТА 10)

тощо ...

Я дам вам кілька номерів продуктивності з фактичного сервера prod. у нас є 75 цінових стовпчиків для кожної ціни.


1

У роботі з подібними даними, де вам потрібно вставити багато рядків, а також вам потрібні справді хороші аналітичні запити (я припускаю, що це так і тут), ви можете виявити, що стовпчастий RDBMS є гарним вмістом . Погляньте на Infobright CE і InfiniDB CE (обидва стовпчикові пристрої зберігання даних підключені до MySQL), а також Vertica CE (більше PostgreSQL-подібні замість MySQL-подібних) ... всі ці видання спільноти безкоштовні (хоча Vertica не є з відкритим кодом, він масштабує до 3 вузлів і 1 Тб даних безкоштовно). Зазвичай Columnar RDBMS пропонує "відповіді на великі запити" на 10-100X кращі, ніж на основі рядків, і час завантаження, який на 5-50X кращий. Ви повинні використовувати їх правильно, або вони смердять (не робіть однорядних операцій ... робіть усі операції в об'ємному підході), але правильно використані вони дійсно гойдаються. ;-)

HTH, Дейв Сиск


1
У нас майже мільярд рядків даних типу "clickstream" (що не відрізняється від даних про тикер запасів) у 3-вузловій установці Vertica ... ми можемо завантажувати дані, що варті цілих днів, приблизно за 15 секунд, і ми отримуємо час відповіді на запит у діапазон 500 мілісекунд. У вашому випадку це, звичайно, звучить так, як це варто було б подивитися.
— Дейв Сиск

Я можу поручитися за те саме. У моєї останньої компанії у нас був кластер Vertica з 8 вузлів з приблизно однаковою кількістю рядків та простими сукупними запитами за весь набір, що повертався за 1-3 секунди (в середньому). Це було приблизно на 1/4 вартості нашого попереднього кластера Greenplum.
— bma
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.