Каков наилучший способ хранения списка / дерева многопоточных сообщений в SQL? - PullRequest
1 голос
/ 27 марта 2009

Я ищу лучший способ хранить набор «постов», а также комментарии к этим постам в SQL. Представьте себе дизайн, похожий на «Стену» на Facebook, где пользователи могут писать посты на своей стене, а другие пользователи могут комментировать эти посты. Мне нужно иметь возможность отображать все сообщения на стене, а также комментарии.

Когда я только начинал, у меня была такая таблица:

CREATE Table wallposts
(
 id uuid NOT NULL,
 posted timestamp NOT NULL,
 userid uuid NOT NULL,
 posterid uuid NOT NULL,
 parentid uuid NOT NULL,
 comment text NOT NULL
)

идентификатор уникален, parentid будет нулевым в исходных сообщениях и будет указывать на идентификатор, если строка является комментарием к существующему сообщению. Достаточно просто и очень быстро, чтобы вставить новые данные. Тем не менее, делая выбор, который вернул бы меня:

POST 1
COMMENT 1
COMMENT 2
POST 2
COMMENT 1
COMMENT 2

Независимо от того, в каком порядке строки существовали в базе данных, оказалось чрезвычайно сложно. Я, очевидно, не могу просто заказать по дате, так как кто-то может прокомментировать пост 1 после того, как пост 2 был опубликован. Если я сделаю LEFT JOIN, чтобы получить родительский пост во всех строках, а затем отсортировать сначала по этой дате, все исходные посты сгруппируются, так как они будут иметь значение null.

Тогда я получил эту идею:

CREATE TABLE wallposts
(
 id uuid NOT NULL,
 threadposted timestamp,
 posted timestamp,
 ...
 comment text
)

На оригинальном посте нить размещена и размещена будет одинаково. Для комментария отметка времени будет временем, когда исходное сообщение было опубликовано, а "отправлено" - временем публикации комментария в этой теме. Теперь я могу просто сделать:

select * from wallposts order by threadposted, posted;

Это прекрасно работает, однако меня раздражает одна вещь. Если два человека создают сообщение одновременно, комментарии к двум сообщениям будут объединены, поскольку они будут иметь одинаковую метку времени. Я мог бы использовать «галочки» вместо datetime, но все равно точность составляет всего 1/1000 секунды. Я также мог бы установить уникальное ограничение для размещения и публикации в потоке, что делает вставки немного дороже, но если бы у меня было несколько серверов баз данных в ферме, вероятность коллизии все еще была. В любом случае, я чуть было не пошел вперед, так как шансы на это чрезвычайно малы, но я хотел посмотреть, смогу ли я съесть свой пирог и при этом иметь его тоже. Главным образом для моего собственного образовательного любопытства.

Третье решение - хранить эти данные в форме графика. Каждый узел будет иметь указатель v-left и v-right. Я мог бы заказать "слева", который будет проходить по дереву в нужном мне порядке. Однако каждый раз, когда кто-то вставляет комментарий, мне приходится заново балансировать все дерево. Это создало бы кучу блокировок строк и всевозможных проблем, если бы сайт был очень занят. Кроме того, это своего рода крайность, которая также вызывает проблемы с репликацией. Поэтому я быстро бросил эту идею.

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

Итак, вот что я в конце концов сделал. Я запрашиваю все сообщения, упорядоченные по введенной дате. На промежуточном уровне программного обеспечения я перебираю набор записей и создаю «стопку» исходных сообщений, каждый узел в стеке указывает на связанный список комментариев. Когда я сталкиваюсь с оригинальным сообщением, я помещаю новый узел в стек, и когда я сталкиваюсь с комментарием, я добавляю узел в связанный список. Я организовал это в памяти, чтобы я мог пройти набор записей один раз и получить O (n). После создания представления стены в памяти я снова просматриваю эту структуру данных и выписываю HTML. Это прекрасно работает и имеет супер-быстрые вставки и супер-быстрое выделение, и никаких странных проблем с блокировкой строк; однако это немного тяжелее на моем уровне представления и требует, чтобы я построил в памяти представление стены пользователя, чтобы перемещать вещи так, чтобы это было в правильном порядке. Тем не менее, я считаю, что это лучший подход, который я нашел до сих пор.

Я подумал, что я бы посоветовался с другими экспертами по SQL, чтобы выяснить, есть ли лучший способ сделать это, используя какие-то странные СОЕДИНЕНИЯ или СОЮЗЫ или что-то, что все равно будет работать с миллионами пользователей.

Ответы [ 4 ]

1 голос
/ 27 марта 2009

Я думаю, что вам лучше использовать более простую модель с "ParentID" в комментариях, чтобы можно было вкладывать комментарии. Я не думаю, что обычно хорошей практикой является использование datetime в качестве ключей, особенно в этом случае, когда вам на самом деле это не нужно, и идентификатора будет достаточно. Вот базовый пример, который может работать:

Post
----
ID (PK)
Timestamp
UserID (FK)
Text 

Comment
-------
ID (PK)
Timestamp
PostID (FK)
ParentCommentID (FK nullable) -- allows for nested comments
Text
0 голосов
/ 27 марта 2009

Если мы придерживаемся вашего дизайна таблицы ... Я думаю, вам понадобится какое-то специальное значение в столбце parentid, чтобы отделить исходные записи от комментариев (возможно, просто NULL, если вы измените определение этого столбца на nullable). Тогда самостоятельное объединение будет работать. Примерно так:

SELECT  posts.comment as [Original Post],
comments.comment as Comment 
FROM   wallposts AS posts
LEFT OUTER JOIN wallposts AS comments 
ON posts.id=comments.parentID
WHERE posts.parentID IS NULL
ORDER BY posts.posted, comments.posted

Результирующий набор показывает Исходное сообщение перед каждым комментарием и имеет правильный порядок.

(Это было сделано с использованием SQL Server, поэтому я не уверен, работает ли он в вашей среде.)

0 голосов
/ 27 марта 2009

Хотите ли вы, чтобы люди могли комментировать другие комментарии, т. Е. Имеет ли дерево бесконечную глубину?

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

SELECT posts.id,
       posts.posted AS posted_at,
       posts.userid AS posted_by,
       posts.posterid,
       posts.comment AS post_text,
       comments.posted AS commented_at,
       comments.userid AS commented_by,
       comments.comment AS comment_text
FROM   wallposts AS posts
LEFT OUTER JOIN wallposts AS comments ON comments.parent_id = posts.id
ORDER BY posts.posted, comments.posted

Этот метод самосоединения просто присоединяет таблицу к себе, используя псевдонимы таблиц для определения объединений.

0 голосов
/ 27 марта 2009

Вы должны посмотреть на "вложенные множества". Они позволяют очень легко получить иерархию с помощью одного запроса. Вот статья о них

Если вы используете SQL Server 2008, он имеет встроенную поддержку для него через тип "ierarchyID ".

Вставки и обновления стоят дороже и сложнее, если у вас нет встроенной поддержки), но запросы гораздо быстрее и проще.

EDIT: Блин, пропустил ту часть, где ты уже знал об этом. (проверял с мобильного телефона).

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