База данных - Разработка таблицы «События»

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

У меня есть четыре типа пользователей, смоделированных вНаследование таблицы классов структура, в основной «пользовательской» таблице храню данные, общие для всех пользователей (id, username, passwordнесколькоflags...) вместе с некоторымиTIMESTAMP поля (date_created, date_updated, date_activated, date_lastLogin...).

Чтобы процитировать совет № 16 из статьи Nettuts +, упомянутой выше:

Пример 2: У вас есть поле «last_login» в вашей таблице. Он обновляется каждый раз, когда пользователь входит на сайт. Но каждое обновление таблицы приводит к сбросу кэша запросов для этой таблицы. Вы можете поместить это поле в другую таблицу, чтобы сводить обновления таблицы пользователей к минимуму.

Теперь это становится еще сложнее, мне нужно отслеживать некоторые статистические данные пользователей, такие как

Как многоуникальный раз профиль пользователя был просмотренКак многоуникальный разобъявление отконкретный тип пользователя был нажатКак многоуникальный разсообщение отконкретный тип пользователя был замечени так далее...

В моей полностью нормализованной базе данных это добавляет до 8-10 дополнительных таблиц, это не много, но я хотел бы, чтобы все было просто, если бы я мог, поэтому я придумал следующее "events" Таблица:

|------|----------------|----------------|---------------------|-----------|
| ID   | TABLE          | EVENT          | DATE                | IP        | 
|------|----------------|----------------|---------------------|-----------|
| 1    | user           | login          | 2010-04-19 00:30:00 | 127.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 1    | user           | login          | 2010-04-19 02:30:00 | 127.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | created        | 2010-04-19 00:31:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | activated      | 2010-04-19 02:34:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | approved       | 2010-04-19 09:30:00 | 217.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | login          | 2010-04-19 12:00:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | created        | 2010-04-19 12:30:00 | 127.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | impressed      | 2010-04-19 12:31:00 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:01 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:02 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:03 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:04 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 15   | user_ads       | clicked        | 2010-04-19 12:31:05 | 127.0.0.2 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | blocked        | 2010-04-20 03:19:00 | 217.0.0.1 |
|------|----------------|----------------|---------------------|-----------|
| 2    | user           | deleted        | 2010-04-20 03:20:00 | 217.0.0.1 |
|------|----------------|----------------|---------------------|-----------|

В основномID относится к первичному ключу (id) поле вTABLE Стол, я считаю, все остальное должно быть довольно просто. Одна вещь, которая мне понравилась в этом дизайне, заключается в том, что я могу отслеживать все пользовательские логины, а не только последний, и, таким образом, генерировать некоторые интересные метрики с этими данными.

Из-за растущей природыevents Таблица Я также подумал о некоторых оптимизациях, таких как:

# 9: Поскольку существует только конечное число таблиц и конечное (и заранее определенное) число событий,TABLE а такжеEVENTS столбцы могут быть настроены какENUMс вместоVARCHARs, чтобы сэкономить место.# 14: ХранитьIPкакUNSIGNED INTs сINET_ATON() вместоVARCHARs.хранитьDATEкакTIMESTAMPs вместоDATETIMEs.ИспользоватьARCHIVE (илиCSV?) двигатель вместоInnoDB / MyISAM.ТолькоINSERTс иSELECTПоддерживаются, и данные сжимаются на лету.

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

Плюсы:Возможность хранить более подробные данные (например, логины).Не надо дизайн (и код для) почти десяток дополнительных таблиц (даты и статистика).Сокращает количество столбцов в таблице и разделяет изменчивые данные.Минусы:Нереляционный (все еще не так плохо, как EAV):SELECT * FROM events WHERE id = 2 AND table = 'user' ORDER BY date DESC();6 байтов на событие (ID, TABLE а такжеEVENT).

Я более склонен придерживаться этого подхода, поскольку плюсы, кажется, намного перевешивают минусы, но я все еще немного неохотно ...Я что-то пропустил? Что вы думаете об этом?

Спасибо!

@coolgeek:

Одна вещь, которую я делаю немного по-другому, - это ведение таблицы entity_type и использование ее идентификатора в столбце object_type (в вашем случае, столбец 'TABLE'). Вы хотели бы сделать то же самое с таблицей event_type.

Просто чтобы прояснить, вы имеете в виду, что я должен добавить дополнительную таблицу, которая отображает, какие события разрешены в таблице, и использовать PK этой таблицы в таблице событий вместо того, чтобы иметьTABLE / EVENT пара?

@Бен:

Это все статистические данные, полученные из существующих данных, не так ли?

Дополнительные таблицы в основном связаны со статистикой, но я не имею данных, некоторые примеры:

user_ad_stats                          user_post_stats
-------------                          ---------------
user_ad_id (FK)                        user_post_id (FK)
ip                                     ip
date                                   date
type (impressed, clicked)

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

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

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

Я думаю, что вам не хватает леса из-за деревьев, так сказать.

Предикатом для вашей таблицы будет «Идентификатор пользователя от IP-IP во время ДАТА СОБЫТИЯ В ТАБЛИЦУ», что кажется разумным, но есть проблемы.

То, что я имел в виду под «не так плохо, как EAV», заключается в том, что все записи следуют линейной структуре и их довольно просто запрашивать, иерархической структуры нет, поэтому все запросы можно выполнить с помощью простогоSELECT.

Что касается вашего второго заявления, я думаю, вы меня не так поняли; IP-адрес не обязательно связан с пользователем. Структура таблицы должна выглядеть примерно так:

Айпи адрес (IP) сделал что-то (EVENT) к ПК (ID) таблицы (TABLE) в назначенную дату (DATE).

Например, в последней строке моего примера выше следует прочитать, что IP 217.0.0.1 (некоторый администратор) удалил пользователя № 2 (последний известный IP-адрес 127.0.0.2) в 2010-04-20 03:20:00 ,

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

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

Еще одна вещь, которая имеет значение в этом аргументе, заключается в том, что я буду хранить гораздо больше событий, и каждое событие будет более чем удвоено по сравнению с оригинальным дизайном, поэтому имеет смысл использоватьARCHIVE механизм хранения здесь, единственное, что он не поддерживаетFKс (ниUPDATEс илиDELETEс).

Ответы на вопрос(3)

Ваш ответ на вопрос