@koriander дал ответ SQL, но что касается того, как Facebook это делает, вы уже частично ответили на это; они используют высоко денормализованные данные и кэширование. Кроме того, они реализуют атомные счетчики, кратные списки в памяти для выполнения обходов графика, и они, безусловно, не используют понятия реляционных баз данных (например, JOIN), поскольку они не масштабируются. Даже кластеры MySQL, которые они запускают, по существу являются просто парами ключ/значение, которые доступны только при промахе в уровне кэша.
Вместо RDBS, я мог бы предложить базу данных графа для ваших целей, как neo4j
Удачи.
EDIT:
Вы действительно придется играть с Neo4j, если вы заинтересованы в его использовании. Вы можете найти или не найти его проще, исходя из фона SQL, но он, безусловно, обеспечит более мощный и, скорее всего, быстрее, запросы для выполнения обходов графика.
Вот несколько примеров запросов Cypher, которые могут быть вам полезны.
Граф, как много людей, как пост:
START post=node({postId})
MATCH post<-[:like]-user
RETURN count(*)
(на самом деле вы должны использовать атомный счетчик, вместо этого, если это то, что вы собираетесь быть запрашивая много)
Получить три человека, которые любили постсо следующими ограничениями:
- Первый
likingUser
всегда будет настоящим пользователем, если ему понравится сообщение.
- Если друзьям нынешнего пользователя понравился пост, они появятся перед любыми друзьями.
START post=node({postId}), user=node({currentUserId})
MATCH path = post<-[:like]-likingUser-[r?:friend*0..1]-user
RETURN likingUser, count(r) as rc, length(path) as len
ORDER BY rc desc, len asc
LIMIT 3
Я попытаюсь объяснить выше запрос ... если я могу.
- Start, захватывая два узла, в
post
и текущий user
- Совпадение по всем пользователям, которые как пост (
likingUser
)
- Кроме того, тест есть ли путь длины 0 или 1, который соединяет
likingUser
через Дружба отношение к текущему user
(путь длины 0 указывает, что likingUser==user
).
- Теперь, закажите его первым, независимо от того, имеет отношение
r
(он будет существовать, если likingUser
является другом с user
или если likingUser==user
). Таким образом, count(r)
будет либо 0, либо 1 для каждого результата. Поскольку мы предпочитаем результаты, где count(r)==1
, мы отсортируем его по убыванию.
- Затем выполните второстепенную сортировку, которая заставляет текущий
user
в начало списка, если он был частью набора результатов. Мы делаем это, проверяя длину path
. Когда user==likingUser
, длина пути будет короче, чем когда user
является другом likingUser
, поэтому мы можем использовать length(path)
, чтобы заставить user
вверх, сортируя в порядке возрастания.
- Наконец, мы ограничиваем результаты только тремя результатами.
Надеюсь, это имеет смысл. В качестве дополнительной заметки вы можете получить лучшую производительность, отделив свои запросы.Например, один запрос, чтобы узнать, нравится ли пользователю сообщение, а затем другой, чтобы получить до трех друзей, которым понравился пост, и, наконец, еще один, чтобы получить до трех не-друзей, которым нравится сообщение. Я говорю, что это может быть быстрее, потому что каждый запрос может замыкаться после получения трех результатов, тогда как большой один запрос, который я написал, должен учитывать все возможности, а затем сортировать их. Поэтому просто имейте в виду, что только потому, что вы можете объединить несколько вопросов в один запрос, он может фактически выполнять хуже, чем несколько запросов.
Спасибо за ответ, но я думаю, что вы неправильно поняли - возможно, моя вина. В настоящее время я запускаю один запрос, чтобы получить все сообщения. В цикле for я запускаю отдельный запрос для каждого сообщения, чтобы получить всю другую информацию. Так что, если есть 50 сообщений, это в общей сложности 51 запрос. Мне было интересно, есть ли способ сконденсировать все в один запрос (получить все сообщения, а также все связанные данные для каждого сообщения). Кажется, что ваш ответ по-прежнему запрашивает цикл for. – BDuelz
Я вижу, это тоже выполнимо, но немного сложнее. Не могли бы вы создать sqlfiddle с образцами данных для тестирования? – koriander
Есть у него - http://sqlfiddle.com/#!2/9e703 – BDuelz