После прочтения советов от этой отличной Nettuts + статьи. Я придумал схему таблицы, которая будет разделять сильно изменчивые данные из других таблиц, подвергнутых тяжелым чтениям, и в то же время уменьшают количество таблиц, необходимых во всей схеме базы данных, однако я не уверен, что это хорошая идея, поскольку она не соответствует правилам нормализации и Я хотел бы услышать ваш совет, вот общая идея:
У меня есть четыре типа пользователей, смоделированных в структуре Class Table Inheritance, в основной "пользовательской" таблице хранятся данные, общие для всех (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
вместо изVARCHAR
, чтобы сохранить некоторое пространство. - # 14. Сохраните
IP
какUNSIGNED INT
s с помощьюINET_ATON()
вместоVARCHAR
s. - Сохранить
DATE
какTIMESTAMP
s вместоDATETIME
s. - Используйте
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 (в вашем случае, столбец "ТАБЛИЦА" ). Вы хотели бы делать то же самое с event_type таблица.
Чтобы быть понятным, вы имеете в виду, что я должен добавить дополнительную таблицу, которая отображает, какие события разрешены в таблице, и использовать PK этой таблицы в таблице событий вместо пары TABLE
/EVENT
?
@ben:
Это все статистические данные, полученные из существующие данные, не так ли?
Дополнительные таблицы в основном связаны со статистикой, но я их еще не существует, некоторые примеры:
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 EVENTed to TABLE", который кажется разумно, но есть проблемы.
То, что я подразумевал для "не так плохо, как EAV", состоит в том, что все записи следуют линейной структуре, и их довольно легко запросить, нет иерархической структуры, поэтому все запросы могут выполняться с помощью простого SELECT
.
Что касается вашего второго заявления, я думаю, вы поняли меня неправильно здесь; IP-адрес не обязательно связан с пользователем. Структура таблицы должна читать примерно следующее:
IP-адрес (
IP
) сделал что-то (EVENT
) к PK (id
) table (TABLE
) на дату (DATE
).
Например, в последней строке моего примера выше следует прочитать, что IP 217.0.0.1 (некоторый администратор), удалил пользователя # 2 (последний известный IP-адрес 127.0.0.2) в 2010-04-20 03: 20:00.
Вы все равно можете присоединяться, скажем, к пользовательским событиям для пользователей, но вы не можете реализовать ограничение внешнего ключа.
Действительно, это моя главная забота. Однако я не совсем уверен, что может пойти не так с этим дизайном, что не может пойти не так, как с традиционным реляционным дизайном. Я могу заметить некоторые оговорки, но до тех пор, пока приложение, работающее с базой данных, знает, что он делает, я думаю, проблем не должно быть.
Еще одна вещь, которая учитывается в этом аргументе, заключается в том, что я буду хранить гораздо больше событий, и каждое событие будет более чем в два раза по сравнению с оригинальным дизайном, имеет смысл использовать механизм хранения ARCHIVE
здесь, единственный Дело в том, что оно не поддерживает FK
(ни UPDATE
, ни DELETE
s).