?

Log in

No account? Create an account

Previous Entry | Next Entry

У нас имеется вот такая практическая задача по хранению как бы журналов, которую мы что-то никак не решим. Может, она давно уже решена, и мы зря мучаемся?

Имеется некоторое число агентов, числом порядка десятков. Каждый агент вступает, скажем, в разговор, и в процессе записывает некоторый журнал, текстовую информацию. Для каждой строки мы записываем время, целое число — код события и, собственно, текст, до 1000 символов (в среднем 150). За 2 минуты эксперимента агент выдает где-то таких строк 200-300. Кроме того, записывается код агента и код разговора. Информация хранится 2 месяца, затем ее можно стирать. Собственно, вот и все — один последовательный файл на всех или одна таблица в базе данных.

В части запросов к хранилищу: нам хочется получать весь журнал одного агента (один агент производит единицы или десятки разговоров, и исчезает навсегда), одного разговора или запрашивать информацию по тому самому коду события. Некоторые из кодов «более важны» (они известны заранее, и их коды отрицательные); более важные записи всегда просматриваются человеком, ежедневно, несколько раз в день. Однако, запросы по другим кодам тоже приходится делать, но реже.

Сейчас это все записывается в таблицу в БД. К сожалению, вставлять записи мы успеваем (BULK INSERT), а вот удалять уже не всегда получается, DELETE просто не успевает их удалять с нужной скоростью, и эта зараза растет. Сейчас она живет на отдельном диске в 1ТБ на машине с 2 зеонами и 32 ГБ оперативной памяти. Мы добавим SSD для tempdb, но это, пожалуй, все, что мы смогли придумать по части железа. Сервер MS SQL Server 2008 на OS Windows Server 2008 R2. Все это как бы работает, только медленно немного: запрос на один журнал агента выполняется минуту-другую, а, например, всех отрицательных («важных») кодов за сегодня — минут от 8 до 25, в зависимости от погоды в Атлантике и личного расположения сервера к запрашивающему (других факторов не пока выявлено). Файлы занимают где-то 200-300 ГБ на диске, если сложить индексы, данные и database logs вместе. И пухнет потихоньку: мы и машин с агентами добавляем, и, как я уже говорил, не за два месяца там данных, а уже за 3, и все больше делается.

Надо сказать, что, когда мы эту страхолюдию запускали, в больших базах данных мы не очень понимали, но теперь понимаем куда больше — многие простые решения уже сделаны. Это чтобы показать уровень владения предметом: совсем не специалисты, изучивших всю эту напасть на своем горбу.

Возможно, существуют более эффективные решения, чем СУБД, учитывая простоту хранимых данных (нет модификации, только вставка/удаление; нет реляций, одна-единственная таблица), но мы их не нашли. Не думаю, что с такой проблемой столкнулись мы одни. Если знаете, как такие вещи решают — научите витающих в облаках теоретиков, как с ними справляются, а?

Доб. Кто-нибудь имел дело с Apachе Cassandra или Hypertable? Это то, что мы ищем, или нет?

Comments

( 31 comments — Leave a comment )
old_radist
Sep. 7th, 2010 09:20 am (UTC)
1) Do not use one table for all!

2) InMemory DB, f.e. TimesTen von Oracle
fregimus
Sep. 7th, 2010 09:32 am (UTC)
Спасибо. 1 — думали об этом. Проблема с удалением старых записей решится, но на производительности запросов это не скажется? Тех, которые не за один день. 2 — там ведь в самом деле 200 ГБ данных, где ж столько памяти взять.
pilot_pirx
Sep. 7th, 2010 09:32 am (UTC)
Приходит в голову пара общеупотребительных вариантов:
1. Partitioning - у Oracle это встроенная возможность, про SQL Server надо уточнять, наверняка тоже что-то похожее есть.
2. Как вариант предыдущего - "буферная" таблица или база: создаем 2 таблицы с одинаковыми атрибутами. В первой храним "оперативную" информацию(например данные за последний час), при этом не строим на ней никаких индексов(отсутствие индексов увеличит скорость добавления-удаления записей), условий целостности и т.п. Из "буферной" таблицы регулярно переносим информацию в "основную" таблицу - на ней можно строить индексы в соответствии с теми запросами, которые нужно делать для анализа данных.
fregimus
Sep. 7th, 2010 09:36 am (UTC)
Partitioning есть — можно разбить на многие «сегменты», но их тогда надо хранить на разных дисках, да? Кажется, что это решение не совсем радикальное, ведь дисков в корпус можно насовать только ограниченное количество. В этом конкретном еще одно, кажется, осталось — 1U. Насчет буферной таблицы — все как раз наоборот: проблем со вставкой никаких как раз нет, даже с индексами; проблемы только с запросами и с удалением старых записей, так что не понятно, как это поможет.
(no subject) - pilot_pirx - Sep. 7th, 2010 11:33 am (UTC) - Expand
(no subject) - fregimus - Sep. 7th, 2010 04:55 pm (UTC) - Expand
mirang
Sep. 7th, 2010 09:36 am (UTC)
Создайте по таблице на месяц. и грохайте их потом целиком, когда гарантировано истечет срок хранений. Чуть усложнится выборка отчета на одного агента, придется из двух таблиц выбирать, но это не так сложно.
fregimus
Sep. 7th, 2010 10:19 am (UTC)
Да, уже думали, спасибо. Это решит хотя бы проблему с удалением. Елси не найдем в корне иного решения, то сделаем хотя бы так.
aamonster
Sep. 7th, 2010 09:44 am (UTC)
Как это сделать не через СУБД - понятно. Но это - шанс в будущем наступить на грабли (не хватит функционала, будет что-то прикручено, потом ещё что-то, потом сменятся разработчики, и однажды окажется, что поддерживающий всё это человек не разбирается в системе и всё сломает).

Так что, может, повыяснять возможности разных СУБД?
fregimus
Sep. 7th, 2010 10:20 am (UTC)
А как сделать? Мне вот не понятно совсем.

Повыяснять — не знаю, как, ески только их все не перепробовать. Потому я пост этот и затеял.
(no subject) - aamonster - Sep. 7th, 2010 11:01 am (UTC) - Expand
(no subject) - fregimus - Sep. 7th, 2010 05:17 pm (UTC) - Expand
vorotylo
Sep. 7th, 2010 10:31 am (UTC)

Я бы попробовал сделать это на файловой системе.

Директории — <agent_id>. В них файлы — <conversation_id> с записью ``разговора''.

agent_id/
    conversation_id:
      timestamp:event_code:message

Пример базы:

$ ls -F convlogs
agent-1/  agent-2/  agent-3/
$ cd agent-1
$ ls
conv-1  conv-2  conv-3  conv-4  conv-5
$ head -n3 conv-1
2010-09-07 13:12:07|0|Hello, world!
2010-09-07 13:13:05|-1|This message is important.
2010-09-07 13:14:17|13|http://fregimus.livejournal.com/116885.html

Сценарии использования:

  • Весь журнал одного агента
    $ cat agent-007/*

  • Журнал одного разговора
    $ cat */conv-42

  • Информация по важным событиям
    $ awk -F\| '$2 ~ /^-/ {print}' */*

    То же с выводом agent_id/conversation_id
    $ grep -rEH '^[^|]+\|-' *

  • Удаление старых журналов
    $ find /path/to/convlogs -mtime 90 -delete
fregimus
Sep. 7th, 2010 05:13 pm (UTC)
Поиск по 200 ГБ текста не очень быстрый выйдет.
(no subject) - al_zatv - Sep. 7th, 2010 09:50 pm (UTC) - Expand
(Anonymous)
Sep. 7th, 2010 11:06 am (UTC)
В порядке бреда: а если настроить еще один сервер, и делать на него репликацию? Тогда мастер работает на вставку/удаление, а весь поиск на слейве?
fregimus
Sep. 7th, 2010 05:16 pm (UTC)
Можно попробовать, спасибо. Хочется найти оптимальное решение (подсказали слово: NoSQL), потому что, суда по времени поиска, в консерватории не все ладно — видимо, универсальность СУБД работает здесь против нас. Но если оставаться с SQL, тоже вариант.
(Anonymous)
Sep. 7th, 2010 11:18 am (UTC)
Quick fix
Сделайте эту таблицу с кластерным индексом по дате вставки. Удаление по дате станет быстрее и, возможно, полностью решит проблему. Partitioning, конечно, гораздо лучше соответствует задаче, но он сложнее в реализации. Там вместо удаления записей нужно будет просто отключать таблицы за период времени (за месяц) и drop'ать их, после чего создавать новую таблицу для нового месяца и подключать к общей.
fregimus
Sep. 7th, 2010 05:12 pm (UTC)
Re: Quick fix
Спасибо за идею, дык только сделали уже, с самого начала. Иначе совсем удалять не может.

Несколько таблиц — пока держим в уме.
_nik_
Sep. 7th, 2010 03:41 pm (UTC)
Почему бы не попробовать NoSQL подход? Скажем, memcache или redis? Кстати, и тот и другой умеют автоматически удалять устаревшие данные.
_nik_
Sep. 7th, 2010 04:10 pm (UTC)
не, memcache не пройдёт по объёму данных, а вот redis умеет выгружать данные на диск, с ним может и получиться
(no subject) - fregimus - Sep. 7th, 2010 05:10 pm (UTC) - Expand
(no subject) - _nik_ - Sep. 8th, 2010 05:20 am (UTC) - Expand
(no subject) - fregimus - Sep. 8th, 2010 05:34 am (UTC) - Expand
(no subject) - _navi_ - Sep. 8th, 2010 09:32 pm (UTC) - Expand
(no subject) - fregimus - Sep. 8th, 2010 11:25 pm (UTC) - Expand
X.A.R. [shmidt.net]
Sep. 7th, 2010 11:31 pm (UTC)
Смотря, как часто надо делать запросы и как часто один и тот же запрос выполняется. Если часто повторяются, можно в memcached кешировать.
Ещё, раз так удачно, что всего одна табличка, можно её разрезать на несколько частей и хранить на разных машинах (писать, например, в случайную манишу), при запросе склеивая результаты с разных машин.
Вообще, по ситуации посмотреть, чего где можно кешировать.
fregimus
Sep. 8th, 2010 05:35 am (UTC)
К сожалению, я не могу описать Вам всю ситуацию — у меня знаний для этого нет. Но все равно спасибо.
fat_crocodile
Sep. 8th, 2010 07:38 am (UTC)
fregimus
Sep. 8th, 2010 08:18 am (UTC)
Спасибо!
fukanchik
Sep. 10th, 2010 06:44 am (UTC)
Я не админ баз данных а программист.
Мои решения (в общем-то классические и хорошо известные):
1. Завести две таблицы с абсолютно одинаковой структурой: f1 и f2. Ваша программа будет в один период времени лить в f1. Затем переключается на f2, а f1 дропается. Дропнуть таблицу гораздо быстрее чем удалить из неё записи. Посе этого f1 пересоздаётся. По истечении периода времени или заполнения таблицы f2 программа переключается обратно на f1, f2 дропается и вся процедура повторяется бесконечно.
2. Завести две таблицы (возпожно с одинаковой структурой а возможно и нет): 1-я просто таблица с самым быстрым вариантом хранения который поддерживает СУБД. В ней нет никаких индексов и максимально упрощённая структура. В неё программа вставляет данные и вставка проходит быстро и ничего не нагружает. Вторая таблица - оптимизирована для выполнения запросов - в ней есть индексы, выбран соответствующий способ хранения на диске и т.п. Более того - в эту вторую таблицу может быть занесена не просто копия информации, а, возможно какие-то агрегации, или например искусственные столбцы для облегчения сортировки и поиска. Теперь в зависимости от потребности в оперативных данных мы выбираем период сливания данных в основную таблицу и по расписанию отдельный компонент сливает данные из первой таблицы во вторую. Пользователи делают запросы исключительно во вторую таблицу.
3. Комбинация 1 и 2. Лучший вариант на мой взгляд.
4. Во многих случаях использование сервера SQL не оправдано и стоит использовать что-то вроде Berkeley DB. Возможно это ваш случай.
( 31 comments — Leave a comment )