Данные в разных разрешениях - PullRequest
10 голосов
/ 07 января 2010

У меня есть две таблицы, записи в эти таблицы постоянно вставляются из внешнего источника. Допустим, эти таблицы хранят статистику взаимодействия пользователей. Когда пользователь нажимает кнопку, сведения об этом щелчке (пользователь, время нажатия и т. Д.) Записываются в одну из таблиц. Когда пользователь щелкает мышью по этой кнопке, запись добавляется с деталями в другую таблицу.

Если много пользователей постоянно взаимодействуют с системой, будет сгенерировано много данных, и эти таблицы будут сильно расти.

Когда я хочу посмотреть на данные, я хочу видеть их в часовом или дневном разрешении.

Существует ли способ или наилучшая практика для непрерывного суммирования данных постепенно (по мере сбора данных) в требуемом разрешении?

Или есть лучший подход к такой проблеме?

PS. Я обнаружил, что инструменты ETL, такие как Talend, могут облегчить жизнь.

Обновление: в настоящее время я использую MySQL, но мне интересны лучшие практики независимо от БД, среды и т. Д.

Ответы [ 6 ]

8 голосов
/ 15 января 2010

Обычный способ сделать это в приложении хранилища данных с малой задержкой - это иметь многораздельную таблицу с ведущим разделом, содержащим что-то, что можно быстро обновить (т. Е. Без необходимости пересчитывать агрегаты на лету), но с засыпкой конечных разделов. с агрегатами. Другими словами, ведущий раздел может использовать схему хранения, отличную от конечных разделов.

Большинство коммерческих и некоторые платформы СУБД с открытым исходным кодом (например, PostgreSQL) могут поддерживать многораздельные таблицы, которые могут быть использованы для того или иного типа вещей. Как вы заполняете базу данных из своих журналов, оставлено для читателя в качестве упражнения.

По сути, структура этого типа системы выглядит следующим образом:

  • Ваша таблица разбита на несколько сортировка даты или значения даты и времени, разделены на час, день или что-то зерно кажется уместным. Журнал записи добавляются в эту таблицу.

  • Поскольку временное окно сдвигается с раздел, периодическая работа индексы или суммирует и преобразует в его «замороженное» состояние. Например, Работа на Oracle может создать растровое изображение индексы на этом разделе или обновить материализованное представление для включения резюме данные для этого раздела.

  • Позже вы можете удалить старые данные, подвести итог или объединить разделы вместе.

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

Точный характер этого процесса варьируется в зависимости от платформы СУБД.

Например, разбиение таблиц на SQL Server не так уж и хорошо, но это можно сделать с помощью служб Analysis Services (OLAP-сервер, который Microsoft связывает с SQL Server). Это делается путем настройки ведущего раздела как чистого ROLAP (сервер OLAP просто выдает запрос к базовой базе данных), а затем перестраивает конечные разделы как MOLAP (сервер OLAP создает свои собственные специализированные структуры данных, включая постоянные сводки, известные как «агрегаты»). ). Сервисы анализа могут сделать это полностью прозрачно для пользователя. Он может перестроить раздел в фоновом режиме, пока старый ROLAP еще виден пользователю. Когда сборка завершена, она переставляется в разделе; куб доступен все время без перерыва в обслуживании пользователя.

Oracle позволяет независимо обновлять структуры разделов, чтобы можно было создавать индексы или разделы на основе материализованного представления. С помощью переписывания запросов оптимизатор запросов в Oracle может решить, что агрегированные цифры, рассчитанные по базовой таблице фактов, можно получить из материализованного представления. Запрос будет считывать агрегированные данные из материализованного представления, где разделы доступны, и из переднего раздела, где их нет.

PostgreSQL может быть в состоянии сделать что-то подобное, но я никогда не задумывался о реализации на нем систем такого типа.

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

Редактировать: В зависимости от формата файлов журнала или от того, какие опции ведения журнала вам доступны, существуют различные способы загрузки данных в систему. Некоторые варианты:

  • Напишите скрипт, используя ваш любимый язык программирования, который считывает данные, анализирует соответствующие биты и вставляет их в базу данных. Это может выполняться довольно часто, но вам нужно каким-то образом отслеживать, где вы находитесь в файле. Будьте осторожны с блокировками, особенно в Windows. Семантика блокировки файлов по умолчанию в Unix / Linux позволяет вам сделать это (так работает tail -f), но поведение по умолчанию в Windows отличается; Обе системы должны быть написаны так, чтобы они хорошо играли друг с другом.

  • В системе unix-oid вы можете записывать свои журналы в канал и выполнять процесс, аналогичный описанному выше, при чтении из канала. Это будет иметь самую низкую задержку из всех, но сбои в считывателе могут заблокировать ваше приложение.

  • Напишите интерфейс ведения журнала для вашего приложения, который непосредственно заполняет базу данных, а не выписывает файлы журнала.

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

В большинстве этих сценариев отслеживание того, где вы были, становится проблемой. Опрос файла, чтобы определить изменения, может быть невероятно дорогим, поэтому вам может понадобиться настроить регистратор так, чтобы он работал так, чтобы он прекрасно сочетался с вашей программой чтения журналов.

  • Один из вариантов - изменить регистратор, чтобы он начинал запись в другой файл каждый период (скажем, каждые несколько минут). Периодически запускайте программу чтения журнала и загружайте новые файлы, которые еще не были обработаны. Прочитайте старые файлы. Чтобы это работало, схема именования файлов должна основываться на времени, чтобы читатель знал, какой файл выбрать. Работа с файлами, которые все еще используются приложением, является более трудоемкой (вам нужно будет следить за тем, сколько было прочитано), поэтому вам следует читать файлы только до последнего периода.

  • Другой вариант - переместить файл и прочитать его. Это лучше всего работает на файловых системах, которые ведут себя как Unix, но должны работать на NTFS. Вы перемещаете файл, а затем читаете его на досуге. Однако для этого необходимо, чтобы регистратор открыл файл в режиме создания / добавления, записал в него и затем закрыл его - не оставляя его открытым и заблокированным. Это определенно поведение Unix - операция перемещения должна быть атомарной. В Windows вам, возможно, придется стоять над регистратором, чтобы сделать эту работу.

2 голосов
/ 16 января 2010

Когда дело доходит до нарезки и агрегации данных (по времени или чему-то еще), схема «звезда» (звезда «Кимбалл») является довольно простым, но мощным решением. Предположим, что для каждого клика мы храним время (с точностью до секунды), информацию о пользователе, идентификатор кнопки и местоположение пользователя. Чтобы включить легкую нарезку и нарезку кубиками, я начну с предварительно загруженных таблиц поиска для свойств объектов, которые редко изменяются - так называемые таблицы измерений в мире DW.

pagevisit2_model_02

Таблица dimDate имеет одну строку для каждого дня с количеством атрибутов (полей), которые описывают конкретный день. Таблица может быть предварительно загружена на несколько лет вперед и должна обновляться один раз в день, если она содержит поля типа DaysAgo, WeeksAgo, MonthsAgo, YearsAgo; в противном случае это может быть «загрузить и забыть». dimDate позволяет легко разрезать по дате атрибуты, такие как

WHERE [YEAR] = 2009 AND DayOfWeek = 'Sunday'

За десять лет данных в таблице всего ~ 3650 строк.

Таблица dimGeography предварительно загружена с интересующими географическими регионами - количество строк зависит от "географического разрешения", требуемого в отчетах, оно позволяет нарезать данные как

WHERE Continent = 'South America'

После загрузки он редко изменяется.

Для каждой кнопки сайта есть одна строка в таблице dimButton, поэтому запрос может иметь

WHERE PageURL = 'http://…/somepage.php'

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

Для записи нажатий кнопок я добавлю таблицу factClick.

pagevisit2_model_01

Таблица factClick имеет одну строку для каждого нажатия кнопки определенного пользователя в определенный момент времени. Я использовал TimeStamp (второе разрешение), ButtonKey и UserKey в составном первичном ключе, чтобы отфильтровывать клики быстрее, чем раз в секунду от конкретного пользователя. Обратите внимание на поле Hour, оно содержит часовую часть TimeStamp, целое число в диапазоне 0-23, чтобы можно было легко разрезать в час, например,

WHERE [HOUR] BETWEEN 7 AND 9

Итак, теперь мы должны рассмотреть:

  • Как загрузить таблицу? Периодически - возможно, каждый час или каждые несколько минут - из блога с использованием инструмента ETL или решения с малой задержкой с использованием какого-либо процесса потоковой передачи событий.
  • Как долго хранить информацию в таблице?

Независимо от того, хранит ли таблица информацию только один день или несколько лет - ее следует разбивать; ConcernedOfTunbridgeW объяснил разбиение в своем ответе, поэтому я пропущу его здесь.

Теперь несколько примеров нарезки и нарезки кубиками для разных атрибутов (включая день и час)

Чтобы упростить запросы, я добавлю представление, чтобы выровнять модель:

/* To simplify queries flatten the model */ 
CREATE VIEW vClicks 
AS 
SELECT * 
FROM factClick AS f 
JOIN dimDate AS d ON d.DateKey = f.DateKey 
JOIN dimButton AS b ON b.ButtonKey = f.ButtonKey 
JOIN dimUser AS u ON u.UserKey = f.UserKey 
JOIN dimGeography AS g ON g.GeographyKey = f.GeographyKey

Пример запроса

/* 
Count number of times specific users clicked any button  
today between 7 and 9 AM (7:00 - 9:59)
*/ 
SELECT  [Email] 
       ,COUNT(*) AS [Counter] 
FROM    vClicks 
WHERE   [DaysAgo] = 0 
        AND [Hour] BETWEEN 7 AND 9 
        AND [Email] IN ('dude45@somemail.com', 'bob46@bobmail.com') 
GROUP BY [Email] 
ORDER BY [Email]

Предположим, меня интересуют данные для User = ALL. dimUser - это большая таблица, поэтому я сделаю представление без нее, чтобы ускорить запросы.

/* 
Because dimUser can be large table it is good 
to have a view without it, to speed-up queries 
when user info is not required 
*/ 
CREATE VIEW vClicksNoUsr 
AS 
SELECT * 
FROM factClick AS f 
JOIN dimDate AS d ON d.DateKey = f.DateKey 
JOIN dimButton AS b ON b.ButtonKey = f.ButtonKey 
JOIN dimGeography AS g ON g.GeographyKey = f.GeographyKey

Пример запроса

/* 
Count number of times a button was clicked on a specific page 
today and yesterday, for each hour. 
*/ 
SELECT  [FullDate] 
       ,[Hour] 
       ,COUNT(*) AS [Counter] 
FROM    vClicksNoUsr 
WHERE   [DaysAgo] IN ( 0, 1 ) 
        AND PageURL = 'http://...MyPage' 
GROUP BY [FullDate], [Hour] 
ORDER BY [FullDate] DESC, [Hour] DESC



Предположим, что для агрегатов нам не нужно хранить конкретную информацию о пользователе, а интересуются только дата, час, кнопка и география. Каждая строка в таблице factClickAgg имеет счетчик для каждого часа, когда была нажата определенная кнопка из определенной географической области.

pagevisit2_model_03

Таблицу factClickAgg можно загружать ежечасно или даже в конце каждого дня - в зависимости от требований к отчетности и аналитике. Например, допустим, что таблица загружается в конце каждого дня (после полуночи), я могу использовать что-то вроде:

/* At the end of each day (after midnight) aggregate data. */ 
INSERT  INTO factClickAgg 
        SELECT  DateKey 
               ,[Hour] 
               ,ButtonKey 
               ,GeographyKey 
               ,COUNT(*) AS [ClickCount] 
        FROM    vClicksNoUsr 
        WHERE   [DaysAgo] = 1 
        GROUP BY DateKey 
               ,[Hour] 
               ,ButtonKey 
               ,GeographyKey

Чтобы упростить запросы, я создам представление для выравнивания модели:

/* To simplify queries for aggregated data */ 
CREATE VIEW vClicksAggregate 
AS 
SELECT * 
FROM factClickAgg AS f 
JOIN dimDate AS d ON d.DateKey = f.DateKey 
JOIN dimButton AS b ON b.ButtonKey = f.ButtonKey 
JOIN dimGeography AS g ON g.GeographyKey = f.GeographyKey

Теперь я могу запрашивать агрегированные данные, например, по дням:

/* 
Number of times a specific buttons was clicked 
in year 2009, by day 
*/ 
SELECT  FullDate 
       ,SUM(ClickCount) AS [Counter] 
FROM    vClicksAggregate 
WHERE   ButtonName = 'MyBtn_1' 
        AND [Year] = 2009 
GROUP BY FullDate 
ORDER BY FullDate

Или с несколькими дополнительными опциями

/* 
Number of times specific buttons were clicked 
in year 2008, on Saturdays, between 9:00 and 11:59 AM 
by users from Africa 
*/ 

SELECT  SUM(ClickCount) AS [Counter] 
FROM    vClicksAggregate 
WHERE   [Year] = 2008 
        AND [DayOfWeek] = 'Saturday' 
        AND [Hour] BETWEEN 9 AND 11 
        AND Continent = 'Africa' 
        AND ButtonName IN ( 'MyBtn_1', 'MyBtn_2', 'MyBtn_3' )
2 голосов
/ 07 января 2010

Взгляните на RRDTool . Это круговая база данных. Вы определяете метрики, которые хотите захватить, но также можете определять разрешение, в котором вы сохраняете его.

Например, вы можете указать в течение последнего часа, вы сохраняете каждую секунду ценность информации; в течение последних 24 часов - каждую минуту; за последнюю неделю, каждый час и т. д.

Он широко используется для сбора статистики в таких системах, как Ganglia и Cacti .

0 голосов
/ 15 января 2010

Предложение, которое не было дано (пока), может заключаться в использовании couchDB или аналогичных концепций базы данных, которые имеют дело с неструктурированными данными.

Подождите! Прежде чем прыгать на меня в ужасе, позвольте мне закончить.

CouchDB собирает неструктурированные данные (JSON & c); цитируя технический обзор с сайта,

Для решения этой проблемы добавления структура обратно к неструктурированной и полуструктурированные данные, CouchDB интегрирует модель представления Виды являются метод агрегирования и отчетности по документы в базе данных, и являются построены по требованию для агрегирования, объединения и отчет по базе данных документов. Просмотры построены динамически и не влияют базовый документ, вы можете иметь столько разных представлений представления одних и тех же данных, которые вам нравятся.

Просмотр определений строго виртуален и отображать только документы из текущий экземпляр базы данных, делая они отделены от данных, которые они дисплей и совместим с Репликация. Представления CouchDB определены внутри специальных проектных документов и может копировать через базу данных экземпляры, как обычные документы, так что не только данные копируются в CouchDB, но все приложение проекты тоже копируют.

Исходя из ваших требований, могу сказать, вам нужно

  • для надежного сбора большого количества данных
  • приоритет отдается скорости / надежности, а не структурированию данных, как только они попадают в систему, и не поддержанию / проверке структурных свойств того, что вы собираете (даже если вы пропустите 1 мс пользовательских данных, это может быть не так большая проблема)
  • вам нужны структурированные данные, когда они приходят из БД

Лично я бы сделал что-то вроде:

  • кэшировать собранные данные на клиенте (ах) и сохранять их пакетами на couchdb
  • , в зависимости от рабочей нагрузки, синхронизируйте кластер дБ (опять же, для этого был разработан couchdb)
  • каждый интервал позволяет серверу генерировать представление о том, что вам нужно (то есть каждый час и т. Д.), В то время как другие (и) продолжают сбор данных
  • сохранить такие (теперь структурированные) представления в надлежащую базу данных для манипулирования и игры с инструментами SQL, или что-то еще

Последний пункт - только пример. Я понятия не имею, что вы планируете делать с этим.

0 голосов
/ 15 января 2010

Быстрые и грязные предложения.

[При условии, что вы не можете изменить базовые таблицы, что в этих таблицах уже записаны строки времени / даты, которые были добавлены, и что у вас есть разрешение на создание объектов в БД].

  1. Создайте VIEW (или пару VIEWS) с логическим полем, которое генерирует уникальный «номер слота» путем выбора даты в таблицах. Что-то вроде:

CREATE VIEW посмотреть как ВЫБЕРИТЕ a, b, c, SUBSTR (поле даты, x, y) номер_слота ОТ ТАБЛИЦА;

Упомянутый выше пример упрощен, вы, вероятно, хотите добавить больше элементов из даты + времени.

[например, скажем, дата «2010-01-01 10: 20: 23,111», вы можете сгенерировать ключ как «2010-01-01 10:00»: таким образом, ваше разрешение составляет один час].

  1. Дополнительно: используйте VIEW для создания реальной таблицы, например:

    CREATE TABLE frozen_data КАК ВЫБРАТЬ * ОТ ВИДА ГДЕ Slot_Number = 'ххх;

Зачем беспокоиться с шагом 1? На самом деле вам не нужно: простое использование VIEW может немного упростить ситуацию (с точки зрения SQL).

Зачем беспокоиться с шагом 2? Просто способ (возможно) снижения нагрузки на уже занятые таблицы: если вы можете динамически генерировать DDL, вы можете создавать отдельные таблицы с копиями «слотов» данных: с которыми вы затем можете работать.

ИЛИ вы можете создать группу столов: по одному в час дня. Создайте триггер для заполнения вторичных таблиц: логика триггера может сегрегировать в какую таблицу записывается.

Ежедневно вам придется сбрасывать эти таблицы: если только вы не можете создавать таблицы в своем триггере на своей БД. [вряд ли я думаю].

0 голосов
/ 14 января 2010

Вы можете использовать историческую базу данных, такую ​​как PI или Historian. Это может быть больше денег, чем вы хотите потратить на этот проект, поэтому вы можете поискать одну из бесплатных альтернатив, таких как пакет базы данных реального времени и истории .

...