Данные в различных разрешениях

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

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

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

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

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

PS. То, что я нашел, до сих пор инструменты ETL как Talend, могло сделать жизнь легкой.

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

10
задан nimcap 15 January 2010 в 11:15
поделиться

6 ответов

На самом деле этого не должно произойти. Загрузка считается отдельным запросом, который должен выполняться в фоновом режиме независимо от родительской страницы после вызова. Как именно вы запускаете запрос на загрузку? По простой ванильной ссылке или ссылке, которая (неправильно) инициирует ajaxical запрос на запуск загрузки?

В любой путь, вы, по крайней мере, явно хотите иметь возможность возобновить загрузки. В этом случае при загрузке необходимо отправить по крайней мере заголовки ответов Accept-Ranges , ETag и Last-Modified . Затем клиент может запросить возобновление загрузки, отправив заголовки запросов If-Range и Range с соответствующим идентификатором файла и указанным диапазоном байтов, которые можно использовать в комбинации с RandomStartFile для отправки оставшихся байтов. Дополнительную информацию и образец сервлета можно найти в этой статье .

Такова теория. В вашем конкретном случае, это немного сложнее, так как вы копируете файлы на лету. Сначала необходимо записать застежку -молнию во временную папку локальной дисковой файловой системы сервера, а затем выполнить из нее поток и, наконец, удалить файл только после успешного завершения загрузки (т.е. out.close () не вызвал IOException ). Вы можете идентифицировать связанную застежку -молнию с помощью параметра запроса или pathinfo или, возможно, ключа в сессии.

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

Update 2 : После небольшой мысли после прочтения вашего обновления и поведения браузера, это выглядит так, что существует довольно большой промежуток времени между запуском фактического запроса и прибытием заголовков ответа. Я не знаю точных деталей, как вы загружаете файлы, но это выглядит как то, что есть затраты времени на сбор ЗАСТЕЖКИ -МОЛНИИ файлов (может быть, вы загружаете их из сетевой файловой системы или базы данных заранее?) и что вы устанавливаете/посылаете заголовки ответов только после вы собрали все ЗАСТЕЖКИ -МОЛНИИ файлы. Попробуйте установить заголовки и выполнить команду output.flush () перед выполнением дорогостоящей задачи. Таким образом браузер получит заголовки как можно скорее и будет знать, что он может ожидать.

-121--4950823-

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

8
ответ дан 4 December 2019 в 00:24
поделиться

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

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

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

2
ответ дан 4 December 2019 в 00:24
поделиться

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

0
ответ дан 4 December 2019 в 00:24
поделиться

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

[Принятие вас не может изменить базовые таблицы, что те таблицы уже записывают строки времени/даты, были добавлены и что у вас действительно есть разрешение создать объекты в DB].

  1. Создают ПРЕДСТАВЛЕНИЕ (или несколько ПРЕДСТАВЛЕНИЙ), который имеет логическое поле на нем, которое генерирует уникальный 'номер слота', нарубив дату в таблицах. Что-то как:

представление CREATE VIEW AS ВЫБРАТЬ a, b, c, SUBSTR (date_field, x, y) slot_number ОТ ТАБЛИЦА;

пример выше упрощен, вы, вероятно, хотите добавить в большем количестве элементов от date+time.

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

  1. Дополнительно: использовать ПРЕДСТАВЛЕНИЕ для генерации реальной таблицы, как:

    CREATE TABLE frozen_data AS ВЫБРАТЬ * ИЗ ПРЕДСТАВЛЕНИЯ ГДЕ slot_number ='xxx;

, Почему беспокойство с шагом 1? Вы на самом деле не имеете к: просто использование ПРЕДСТАВЛЕНИЯ могло бы сделать вещи немного легче (с точки зрения SQL).

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

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

Ежедневно необходимо было бы сбросить эти таблицы: если вы не можете генерировать таблицы в своем триггере на вашем DB. [вряд ли я думаю].

0
ответ дан 4 December 2019 в 00:24
поделиться

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

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

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

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

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

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

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

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

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

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

0
ответ дан 4 December 2019 в 00:24
поделиться

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

pagevisit2_model_02

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

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

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

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

WHERE Continent = 'South America'

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

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

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
ответ дан 4 December 2019 в 00:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: