ефективний дизайн таблиці та індексу 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.