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

17

Я ищу совет по дизайну таблицы / индекса для следующей ситуации:

У меня есть большая таблица (данные истории цен акций, InnoDB, 35 миллионов строк и растет) с составным первичным ключом (сборка (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 Гиг. Я больше не могу держать БД в оперативной памяти. (У меня есть 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 G RAM), я оставил ключи включенными.

так что, как я вижу, вот мои варианты

Опция 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 гигабайтам и начала заполнять эту новую таблицу. Полтора дня спустя, это закончено, и моя база данных теперь сидит на пухлых 220 Гигах!

Это позволяет удалить эти 200 значений из мастер-таблицы, так как я могу получить их за одно соединение, но это на самом деле вернет мне только 25 гигабайт или около того.

Я попросил его создать первичный ключ на ассемблете, дате, функции и индексе стоимости, и после 9 часов работы с ним он действительно не оставил следов и, казалось, завис, поэтому я убил эту часть.

Я перестроил пару разделов, но, похоже, они не занимали много места.

Так что это решение выглядит так, как будто оно не будет идеальным. Интересно, занимают ли строки значительно больше места, чем столбцы? Может быть, поэтому это решение заняло гораздо больше места?

Я наткнулся на эту статью:

http://www.chrismoos.com/2010/01/31/mysql-partitioning-tables-with-millions-of-rows

это дало мне идею. Это говорит:

Сначала я думал о RANGE-разбиении по дате, и, хотя я использую дату в своих запросах, очень часто запрос имеет очень большой диапазон дат, и это означает, что он может легко охватывать все разделы.

Теперь я также делю диапазоны по датам, но также буду разрешать поиск по большому диапазону дат, что снизит эффективность моего разбиения. У меня всегда будет диапазон дат при поиске, однако у меня также всегда будет список сборок. Возможно, мое решение должно состоять в том, чтобы разделить по ассембле и дате, где я определяю типично искомые диапазоны ассемблера (которые я могу придумать, есть стандартные списки, S & P 500, Рассел 2000 и т. Д.). Таким образом, я почти никогда не смотрю на весь набор данных.

Опять же, я в любом случае пользуюсь первичным ключом на ассемблете и дате, так что, возможно, это не сильно поможет.

Любые другие мысли / комментарии будут оценены.

dyeryn
источник
2
Я не понимаю, зачем тебе 200 столов. Одна таблица (value_name varchar(20), value double)будет иметь возможность хранить все ( value_nameбудучи f1, f2...)
a_horse_with_no_name
Благодарю. причина, по которой я поместил их по отдельности, заключалась в том, чтобы получить ограничение в 50 индексов на таблицу. Я думал о том, чтобы поместить их в 5 таблиц, по 40 значений в каждой, но я вставляю около 17000 записей в день для каждой и не знал, какова будет производительность вставки в таблицу с 40 индексами. обратите внимание, что каждая комбинация ассемблированного, даты получает свои собственные значения f1, f2 .... Предлагаете ли вы одну таблицу с (ассемблер, дата, имя-значения, значение), с первичным ключом, датой, возможно, индексом (значение-имя, значение)? эта таблица будет иметь 35 миллионов * 200 = 7 миллиардов строк, но, может быть, разделение будет работать хорошо?
dyeryn
обновленный пост с моим опытом, пробующим этот метод
dyeryn
У меня есть окончательное решение в разработке, я буду обновлять, когда я закончу. по сути, это единственное табличное решение, предложенное здесь с определенным разделением и логическим разделением.
dyeryn
Может ли помочь другой механизм хранения? Вместо InnoDb, может быть, попробовать InfiniDB? Столбчатые данные, шаблоны доступа выглядят как большое пакетное обновление, чтение на основе диапазона и минимальное обслуживание таблиц.
грязный

Ответы:

1

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

Второе преимущество - производительность чтения. как вы упомянули, новым способом запроса данных является любая комбинация этого столбца f1 - f200 в предложении where. с парой ключей-значений f1-f200 присутствуют в одном столбце, скажем, "FildName", а их значения присутствуют во втором столбце, скажем, "FieldValue". у вас может быть индекс CLUSTERED для обоих столбцов. Ваш запрос будет СОЮЗОМ из этих Избранных.

ГДЕ (FiledName = 'f1' и FieldValue МЕЖДУ 5 И 6)

UNION

(FiledName = 'f2' и FieldValue МЕЖДУ 8 И 10)

и т.д.....

Я дам вам некоторые номера производительности с реального сервера Prod. у нас есть 75 ценовых столбцов для каждой ценной бумаги.

Ануп Шах
источник
1

При работе с данными такого типа, когда вам нужно вставить много строк, а также вам нужна действительно хорошая производительность аналитических запросов (я предполагаю, что это именно так), вы можете обнаружить, что столбчатая RDBMS хорошо подходит , Взгляните на Infobright CE и InfiniDB CE (оба механизма столбчатых хранилищ подключены к MySQL), а также Vertica CE (больше похожий на PostgreSQL вместо MySQL) ... все эти выпуски Community бесплатны (хотя Vertica не является с открытым исходным кодом, он масштабируется до 3 узлов и 1 ТБ данных бесплатно). Столбчатые СУБД обычно предлагают время отклика «большого запроса», которое в 10-100 раз лучше, чем на основе строк, и время загрузки, которое в 5-50 раз лучше. Вы должны использовать их правильно, или они воняют (не выполняйте однорядные операции ... выполняйте все операции массовым способом), но при правильном использовании они действительно потрясающие. ;-)

HTH, Дейв Сиск

Дейв Сиск
источник
1
В установке Vertica с 3 узлами имеется почти миллиард строк данных типа потока кликов (ничем не отличающихся от данных биржевого тикера) ... мы можем загрузить данные за целые дни за 15 секунд и получить время ответа на запрос в диапазон 500 миллисекунд. В вашем случае это, безусловно, звучит так, как будто стоит посмотреть.
Дейв Сиск
Я могу поручиться за то же самое. В моей последней компании у нас был кластер Vertica с 8 узлами с примерно одинаковым числом строк и простые агрегированные запросы по всему набору, возвращаемые в среднем за 1-3 секунды. Это было примерно на 1/4 стоимости нашего более раннего кластера Greenplum.
BMA