Задавайте вопросы, мы ответим
Вы не зашли.
Есть таблица ~1-2 Гб, ~10 млн. записей, несколько десятков инсертов в секунду.
Пусть col_key - столбец, на котором есть ключ, col_data - где нет.
SELECT col_key FROM tbl WHERE col_key = N занимает сотые секунды (записей выбирается обычно несколько тысяч). Тут понятно, что сервер пользуется индексом и сразу оттуда берет.
SELECT col_data FROM tbl WHERE col_key = N занимает полминуты.
Тоже понятно, что лезет на диск и ищет там. Но чего ж так долго-то?
Можно ли как-то ускорить или уже предел?
Растягивать индекс на col_data не хочется, потому что это текстовое поле длиной обычно 10-30 символов, а перестраивать при каждом инсерте (если строить не на всю длину столбца, все равно будет тогда при выборе данных из этого столбца лезть за ними на диск).
Неактивен
Исамовская табличка, небось? Несколько тысяч сиков — это достаточно много.
Впрочем, тебе разве нужны все несколько тысяч? LIMIT нужно хвостиком сделать.
Ну и как вариант — сделать InnoDB (тем самым перевалив данные в ОЗУ), ну
или partitioning по col_key, тоже очень хорошо тут отработает.
Неактивен
Не, табличка InnoDB.
Впрочем, тебе разве нужны все несколько тысяч? LIMIT нужно хвостиком сделать.
К сожалению, задача ставится так, что именно все несколько тысяч
Перечитал темы про организацию логов, да и другие некоторые. Хочу поделиться размышлениями.
I. MERGE/VIEW не подходит по следующим причинам:
1. Нужна уникальность ключа (даже нескольких, на самом деле). Я так понимаю, нормальных средств, позволяющих поддерживать уникальность на всем пространстве MERGE, не имеется.
2. С MERGE медленнее чтение по ключу. А чтение тоже периодически случается, причем иногда - по всей таблице (заказчик захотел динамическую статистику за сутки; в основном, конечно, данные добавляются и удаляются кусками, но встречаются и такие запросы, которые приходится делать на всю таблицу)
3. Все-таки в таблицу десятки инсертов в секунду. Несколько раз в минуту бывают и селекты. Как-то страшновато использовать MyISAM в таких условиях имхо.. (хотя лочиться будет только последняя таблица, все же не круто..)
II. Partitioning.
Основной вопрос здесь такой: как влияет paritioning на поиск по остальным ключам (кроме того, по которому разбиение)?
У меня часто задействуются другие ключи. Не будет ли там ситуации, такой же, как в MERGE, когда надо подряд все ключи во всех разделах обыскивать?
III. Еще вот такая идея: а что, если эту всю таблицу загнать в память? (т.е. сделать ей ENGINE = MEMORY)?..
И, например, снять часть ключей?
Насколько поиск по неотсортированной памяти медленнее поиска по отсортированной памяти?
Неактивен
А поглядеть можно на базку как-то? Очень странный эффект. Ну то есть
сходу в голову приходит только «col_data — большущий blob».
И еще вопрос. У тебя табличка 10кк строк, insertы по 10rps, т.е. всей таблице
2 недели жизни. А что с ней будет через 2 месяца? Или там что-то еще ее
меняет?
Неактивен
А поглядеть можно на базку как-то?
Милости просим (не хотел эфир просто засорять)
Соответственно, col_key - это siteid, col_data - это refhost.
CREATE TABLE `brevno_ext_sessions` (
`id` bigint(10) unsigned NOT NULL auto_increment,
`siteid` int(10) unsigned NOT NULL,
`uuid` char(36) NOT NULL,
`DATE` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`useragent` varchar(255) NOT NULL,
`refhost` varchar(255) NOT NULL,
`refuri` varchar(2048) NOT NULL,
`GET` varchar(2048) NOT NULL default '',
PRIMARY KEY (`id`),
KEY `uuid` (`uuid`),
KEY `DATE` (`DATE`),
KEY `siteid` (`siteid`)
) ENGINE=InnoDB AUTO_INCREMENT=5543800 DEFAULT CHARSET=latin1
Это сессии нескольких тысяч маленьких сайтов за сутки (т.е. записей где-то один-два миллиона; потом будет побольше).
Запрос вида SELECT refhost FROM brevno_ext_sessions WHERE siteid = N как раз и занимает полминуты.
(еще там вторая есть тоже большая, насчет неё уже жаловался:
http://sqlinfo.ru/forum/viewtopic.php?id=1316
так что если будет дежа вю - не удивляйся )
И еще вопрос. У тебя табличка 10кк строк, insertы по 10rps, т.е. всей таблице
2 недели жизни. А что с ней будет через 2 месяца? Или там что-то еще ее
меняет?
Каждую минуту запускается скрипт и удаляет записи так, чтобы оставались только за последние сутки.
Неактивен
Меня также не перестает интересовать следующее: как влияет paritioning на поиск по остальным ключам?
Неактивен
Уууу! Много независимых мыслей:
Считай, что это MERGE с возможностью PRUNING. Это очень близко к действительности.
По поводу запроса — я думал, что по site у тебя по крайней мере PK стоит Когда ты
делаешь SELECT siteid FROM brevno_ext_sessions WHERE siteid = 1 — у тебя работает
USING INDEX, более того, ему достаточно посчитать количество строк по индексу.
Когда ты тянешь данные, ему нужно сделать куда больше работы — найти по индексу
PK соответствующий, а по нему уже вытащить данные. Подозреваю, что «короткий»
запрос читает с диска 1-2 страницы последовательно (т.к. сортирован), а «длинный»
хорошо и жестко бегает в поисках этих 1000 страниц данных.
В MEMORY эта хреновина не влезет (см на varchar(2048) несколько раз).
Если uuid — это md5, то binary(16) гораздо лучше, чем char(36).
Подозреваю, что все запросы у тебя все-таки per-site, попробуй покластеризовать по
HASH(siteid) с каким-нибудь невменяемым количеством кусков типа 1000.
А кто вообще придумал хранить независимые данные по тысячам сайтов в одной табличке?
Неактивен
Подозреваю, что все запросы у тебя все-таки per-site, попробуй покластеризовать по
HASH(siteid) с каким-нибудь невменяемым количеством кусков типа 1000.
Это хорошо бы, да. Но тут есть одна загвоздка, которая состоит именно в "суточной" природе таблицы: записи должны храниться строго за сутки - следить за этим можно только с помощью значения колонки DATE, удаляя записи с WHERE по этой колонке (тут даже не важно, по какой конкретно - главное, что не по siteid). Операции пересчета (когда из необработанной статистики делается суммарная) также делаются за определенные временные интервалы и потому опять же пользуются колонкой DATE (поэтому, кстати, тысячи сайтов в одной таблице и хранятся - все равно везде лазить по DATE; а еще иметь постоянно разрастающееся количество таблиц как-то неуютно немного)
Я так понимаю, partitioning хорош в тех случаях, когда поиск по таблице базируется на значении какой-то одной колонки - по ней и разбивается таблица.
Если же используются несколько ключей, то это может и замедлить работу, поскольку приходится искать в каждом partition'е отдельно.
Так что я даже и не знаю, что тут придумать.. :о
Прямо хоть делай две таблицы - одну по DATE разбивай, вторую - по siteid...
Тут же сразу вопрос: DATE у меня будет постоянно плыть, медленно увеличиваясь. Соответственно, рано или поздно (скорее, рано) структура разделов будет становиться морально устаревшей, придется периодически делать ALTER TABLE.
Так вот, вопрос: ALTER TABLE на partitioned-таблице делается по той же процедуре, что и на обычной? Меня это интересует потому, что если делается ПОЛНАЯ копия таблицы, то это значит полчаса (а то и больше) простоя на каждую такую операцию. Чтобы иметь значительный выигрыш в скорости поиска по разбивающему ключу, надо разбить хотя бы на несколько десятков разделов. Поскольку все разделы живут сутки, получается, что набор разделов надо будет обновлять раз в час, ну в два. Понятно, что полчаса простоя на два часа работы - это не очень .
Неактивен
Кстати. Мне тут пришла мысль: всё, что мы тут обсуждали - это, конечно, хорошо, полезно и интересно
Но че-то мы отошли от проблемы. Мне ведь нужно вытащить строковую колонку, которая не в ключе. По-моему, partitioning мне бы тут не помог, т.к. у меня медленная стадия - не поиск по ключу, а выбор данных с диска, который будет так же медленной происходить, как и для обычной таблицы.
(или нет?)
Неактивен
У тебя медленная стадия — беганье за тысячей данных рандомно по таблице.
Если эта тысяча данных лежит в последовательных страницах, получается
последовательное чтение, что в разы быстрее. Медленно не чтение, медленны
сики по диску. Разбиение таблицы на тысячу маленьких сделает так, что в
одной «физической» табличке будут лежать единицы сайтов, тем самым
обеспечивая последовательное чтение.
Вчера вечером также подумал, что, возможно, замета PK с id на (site_id, uuid)
(+ выкинуть id + выкинуть ключ по uuid + переписать запросы так, чтобы везде
работал site_id) значительно улучшит работу таблицы. Не смотря на увеличение
размера PK.
Неактивен
Вчера вечером также подумал, что, возможно, замета PK с id на (site_id, uuid)
(+ выкинуть id + выкинуть ключ по uuid + переписать запросы так, чтобы везде
работал site_id) значительно улучшит работу таблицы. Не смотря на увеличение
размера PK.
Да, это идея хорошая. Учитывая, что я еще и uuid сделал BINARY(4) вместо CHAR(36) (60 символов, латинские буквы и цифры, могут дать 12 млн. комбинаций; учитвая, что сессия живет сутки и проверяется отдельно для каждого siteid, вероятность пересечения ключа получается все равно оч. малая), а PK у меня был не 4 байта, а 8, то ваще намного лучше станет.
Только вот InnoDB все равно неявный PK создает ведь.. Имеет ли смысл удалять при этом явный, если он все равно создастся?
Неактивен
Нет, лучше всегда иметь явный, и лучше всегда извлекать из кластеризации
максимальную выгоду. Я предложил как раз пойти вразрез с принципом «в
InnoDB должен быть короткий PK» и сделать более длинный PK, но более правильный.
Ну и параллельно избавиться от ненужных вторичных индексов, чтобы табличка
не начала тормозить (таки принцип то нарушили).
Неактивен
Че-то я затупил... PK - это ж необязательно autoincrement
Теперь все понятно
Неактивен
У меня появилась идея, как обуздать-таки эту таблицу.
Основная проблема у меня заключалась в том, что используются два основных ключа - (siteid, sessionkey) - для уникальности сессии и DATE - для всего остального (т.к. все другие операции основаны на дате записи).
В общем, идея такая: вместо одной таблицы сделать две, одну - для сессий:
Неактивен
Честно говоря, не понял, чего ты собираешься добиться, но второй таблице явно не
хватает ключевых полей — sessionkey там должен быть
Неактивен
Я собираюсь разбить одну таблицу (ту, которая описана в головном сообщении) на две (см. моё предыдущее сообщение), запись будет идти одновременно в обе этих таблицы. При этом уникальность ключа сессии будет проверяться по таблице sessionkeys. Во-второй таблице столбец sessionkey вообще не нужен, т.к. вставка туда будет проходить только после проверки пары (siteid, sessionkey) по первой.
Неактивен
Я сдуру решил, что эти данные во второй таблице для чего-то нужны (судя по названию —
там хранятся данные сессии). Если их вытаскивать из таблицы не нужно (основываясь на
uid сессии), то никаких проблем, конечно, нету
В любом случае, тут будет какая-то доля эксперимента — будет ли так быстрее, или нет.
Неактивен
Еще парочка вопросов:
1. По минутам фиг у меня получится разбить, т.к. разрешается не больше 1024 разделов на таблицу, включая subpartition'ы.
Чем, помимо этого, ограничивается сверху число разделов?
Как я понимаю, для операций по ключу чем больше разделов - тем лучше. Почему бы всегда не делать 1024 раздела в таком случае (если нет операций по другим ключам)?
1a. Он нормально отнесется к RANGE-разбиению по UNIX_TIMESTAMP(DATE) ?
2. Как у разделов обстоят дела в JOIN'ами по разбивающему ключу? Все нормально, нет никаких подводных камней?
Неактивен
Да, очень неприятное ограничение. Но — можно пересобрать сервер с новым значением
Предлагаю 1а и 2 проверить самостоятельно. Я никаких проблем не вижу
Неактивен
Т.е., грубо говоря, разделов много не бывает?
Они какие-нибудь ресурсы потребляют вообще? Какие нибудь там файловые дескрипторы и т.п., когда смотришь, и бац - у тебя что-то закончилось, и потом оказывается, что это из-за partitioning'а.
(это я все никак не могу понять, стоит ли мне делать макс. количество разделов или это где-то аукнется)
Неактивен
Если вопрос разряда «стоит ли перекомпилировать» — нет, не стоит. Попробуй сначала на 1024.
Если будет прирост производительности, но недостаточный — тогда уже пересобирать.
Неактивен
Понятно
(я вообще не имел в виду перекомпиляцию; я в том смысле спрашивал, что не приводит ли увеличение количества разделов к потере производительности за счет траты ресурсов на обслуживание каждого раздела)
Неактивен
Ресурс на обслуживание == лишние открытые таблицы (i.e. файлхэндлы). Не так много, чтобы было плохо :-)
Неактивен
А они все время висят открытые (все 1024 штуки) или висит один общий на таблицу, а на конкретные разделы открываются только когда их трогают?
Неактивен
Открываются все сразу, и закрываются все сразу.
Неактивен