После прочтения советов от этой замечательной Nettuts + статьи я придумал схему таблиц, которая отделяет очень изменчивые данные от других таблиц, подвергнутых интенсивному чтению и в то же время время уменьшить количество таблиц, необходимых во всей схеме базы данных, однако я не уверен, что это хорошая идея, поскольку она не соответствует правилам нормализации, и я хотел бы услышать ваш совет, вот общая идея:
У меня есть четыре типа пользователей, смоделированных в структуре Наследование таблиц классов , в основной "пользовательской" таблице я храню данные, общие для всех пользователей (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
s VARCHAR
с, чтобы сэкономить место.
- # 14 : сохранение
IP
s как UNSIGNED INT
s с INET_ATON()
вместо VARCHAR
s.
- Сохранить
DATE
s как TIMESTAMP
s вместо DATETIME
s.
- Вместо
InnoDB
/ MyISAM
используйте двигатель ARCHIVE
( или CSV
? ).
- Поддерживаются только
INSERT
с и SELECT
с, а данные сжимаются на лету.
В целом, каждое событие будет занимать только 14 (несжатых) байтов, что, наверное, хорошо для моего трафика.
Плюсы:
- Возможность хранить более подробные данные (например, логины).
- Не нужно проектировать ( и код для ) почти дюжину дополнительных таблиц (даты и статистика).
- Сокращает несколько столбцов на таблицу и разделяет изменчивые данные.
Минусы:
- Нереляционный (все еще не так плохо, как EAV):
SELECT * FROM events WHERE id = 2 AND table = 'user' ORDER BY date DESC();
- 6 байт на событие (
ID
, TABLE
и EVENT
).
Я более склонен придерживаться этого подхода, поскольку плюсы, кажется, намного перевешивают минусы, но я все еще немного неохотно ... Я что-то упустил? Что вы думаете об этом?
Спасибо!
@ coolgeek:
Одна вещь, которую я делаю немного
иначе поддерживать
и введите его идентификатор в
столбец object_type (в вашем случае
столбец «ТАБЛИЦА»). Вы бы хотели
делатьто же самое с 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 на момент DATE
СОБЫТИЕ В СТОЛ ", который кажется
разумно, но есть проблемы.
То, что я имел в виду под «не так плохо, как EAV», это то, что все записи следуют линейной структуре и их довольно просто запрашивать, иерархической структуры нет, поэтому все запросы можно выполнить с помощью простого SELECT
.
Относительно вашего второго утверждения, я думаю, вы меня не так поняли; IP-адрес не обязательно связан с пользователем. Структура таблицы должна выглядеть примерно так:
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
с).