Медленное обновление против медленного выбора - PullRequest
2 голосов
/ 13 января 2010

Это вопрос о компромиссах.

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

Чтобы сделать эту работу. У нас есть 3 таблицы Users (id, name), FriendLists (userId, friendUserId), Уведомления (?).

Теперь давайте предположим, что у каждого пользователя есть приблизительно 50 друзей в его списке друзей. Я столкнулся с дилеммой - как реализовать таблицу уведомлений.


1-й вариант

CREATE TABLE Notifications
(
toUserId bigint NOT NULL,
[identity] bigint IDENTITY(1,1) NOT NULL,
fromUserId bigint NOT NULL,
data varchar(256) NOT NULL,
CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED (toUserId, [identity])
)

Отправка уведомлений:

-- Get all friends of @fromUserId.
WITH Friends AS
   (SELECT FriendLists.friendUserId
 FROM FriendLists
 WHERE userId = @fromUserId)
-- Send updates to all friends.
SELECT
 friendUserId as toUserId,
 @fromUserId as fromUserId,
 @data as data
INTO Notifications
FROM Friends

В этом случае для каждого изменения статуса мы создаем 50 записей (при условии 50 друзей). Это плохо. Однако хорошим моментом является то, что извлекать уведомления для определенного пользователя очень быстро, поскольку у нас есть кластеризованный индекс для toUserId.

2-й вариант

CREATE TABLE Notifications
(
toUserId bigint NOT NULL,
[identity] bigint IDENTITY(1,1) NOT NULL,
fromUserId bigint NOT NULL,
data varchar(256) NOT NULL,
CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED ([identity])
)
CREATE NONCLUSTERED INDEX [IX_toUserId] ON Notifications (toUserId ASC)

Отправка уведомлений:

-- Get all friends of @fromUserId.
WITH Friends AS
   (SELECT FriendLists.friendUserId
 FROM FriendLists
 WHERE userId = @fromUserId)
-- Send updates to all friends.
INSERT INTO Notifications(toUserId, fromUserId, data)
    VALUES(friendUserId, @fromUserId, @data)

Здесь мы вставляем только одну запись для каждого обновления статуса. Это хорошо. Плохо то, что получение уведомлений будет медленнее, так как записи не кластеризуются с помощью toUserId.


Получение уведомлений одинаково для обоих методов:

SELECT TOP(50) fromUserId, [identity], data
FROM Notifications
WHERE toUserId  = @toUserId

Так что вы думаете по этому поводу?

Ответы [ 3 ]

3 голосов
/ 13 января 2010

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

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

Так что я бы определенно выбрал структуру, оптимизированную для чтения.

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

1 голос
/ 13 января 2010

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

Сказав это, я бы использовал столбец идентификаторов в качестве вашего кластеризованного индекса и создал бы некластеризованный индекс для toUserId и, возможно, столбец datetime. Включив столбец datetime, вы сможете более эффективно запрашивать последние данные.

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

Для очень больших баз данных я передам слово гуру SQL, которые могут поговорить о стратегиях разделения (меньшие, более управляемые таблицы для более новых данных, большие / сильно индексированные таблицы для более старых данных) и решениях репликации.

1 голос
/ 13 января 2010

Обновления очень медленные по сравнению с SELECT ... на несколько порядков. Кроме того, по мере масштабирования вашего сайта вы будете кэшировать все свои выборки в памяти, поэтому скорость выбора будет тривиальной.

...