Наилучшая практика для поддержания денормализованной схемы в актуальном состоянии? - PullRequest
4 голосов
/ 13 мая 2009

Я создаю игру с очками за мелочи, поэтому у меня есть такая схема:

create table points (
  id int,
  points int,
  reason varchar(10)
)

и получить количество баллов у пользователя тривиально:

select sum(points) as total from points where id = ?

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

create table pointtotal (
  id int,
  totalpoints int
)

как лучше синхронизировать их? Я пытаюсь обновить pointtotal при каждом изменении? Запускаю ли я ежедневный скрипт?

(Предположим, у меня есть правильные ключи - они были опущены для краткости)

Edit:

Вот некоторые характеристики, которые я пропустил, но они должны быть полезны:

Вставки / обновления в очки не так часто Есть большое количество записей и большое количество запросов - ключи, как видите, были довольно тривиальными.

Ответы [ 8 ]

8 голосов
/ 13 мая 2009

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

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

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

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


Запрос "select sum(points) as total from points where id = ?" должен не занимать 2 секунды, даже если у вас огромное количество строк и много запросов.

Если у вас есть индекс покрытия , определенный для (id, points), тогда запрос может выдать результат, вообще не считывая данные из таблицы; он может вычислить итоговое значение путем считывания значений из самого индекса. Используйте EXPLAIN для анализа вашего запроса и найдите примечание «Использование индекса» в столбце «Extra».

CREATE TABLE Points (
  id     INT,
  points INT,
  reason VARCHAR(10),
  KEY    id (id,points)
);

EXPLAIN SELECT SUM(points) AS total FROM Points WHERE id = 1;

+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref   | rows | Extra                    |
+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | points | ref  | id            | id   | 5       | const |    9 | Using where; Using index | 
+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+
2 голосов
/ 13 мая 2009

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

При этом с размером вашей записи у вас должен быть либо очень медленный сервер, либо очень большое количество записей, потому что такая маленькая запись с индексированным полем на id должна очень быстро суммироваться для вас - однако я Я придерживаюсь мнения, что если вы можете улучшить время отклика пользователя даже на несколько секунд, то нет причин не использовать сводные таблицы, даже если объект DB очищает объект.

1 голос
/ 16 мая 2009

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

1 голос
/ 16 мая 2009

В этом случае вы можете пойти любым путем, потому что это не очень сложно.

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

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

1 голос
/ 14 мая 2009

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

1 голос
/ 13 мая 2009

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

1 голос
/ 13 мая 2009

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

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

SELECT totalpoint FROM point ORDER BY id DESC LIMIT 1;
0 голосов
/ 27 мая 2009
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...