Структура базы данных Вконтакте

Доброго времени суток. Вот задумался, интересно, а как организована в соц. сетях (facebook, VK) БД связей между пользователями? Казалось бы, все просто: создал базу friendship , в нее 3 поля |user_id|recipient_id|status| Где
user_id — id пользователя
recipient_id — id профиля, который имеет какое-либо отношение к пользователю user_id
И status , куда можно занести «отношение» этих 2-х пользователей (0-подал заявку в друзья, ждем подтверждения, 1-дружим, 2-в черном списке). Казалось бы, и все, но это мне кажется совершенно нерациональным, так как рост данных в БД примерно можно будет рассчитывать по формуле r = x^2 , где r — кол-во строк, а х — кол-во пользователей. Совершенно нерациональный подход. А как бы сделали Вы? Дело в том, что появилась необходимость сделать нечто похожее по принципу функционирования, но, боюсь, что Мускул повесится от нагрузки. Заранее спасибо за помощь и ваши варианты 🙂

Отслеживать
8,647 17 17 золотых знаков 72 72 серебряных знака 180 180 бронзовых знаков
задан 26 окт 2011 в 14:59
Станислав Комар Станислав Комар
1,987 16 16 серебряных знаков 28 28 бронзовых знаков

Учим Базы Данных за 1 час! #От Профессионала

У Вас отношение между Id стремится к каждый с каждым ? Иначе откуда квадрат ? Мне кажется в соц сетях наоборот у большинства Id (из миллионов) десятки (а не миллионы) связей. Собственно это вопрос о представлении матрицы. Разряженная или нет.

26 окт 2011 в 15:20

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

Вопрос о том как «хранить» набор друзей — это, пожалуй, один из самых простых вопросов, на который требуется ответить при разработке подобной архитектуры. 200-300 записей в базе (по одной на каждого добавленного друга) на пользователя — это смешно, если сравнить их, например, с количеством личных сообщений или даже «лайками» у новостей.

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

Вопрос «как наиболее эффективным образом сочетать Hadoop, PHP, memcached, Thrift и MySQL» намного серьезнее вопроса «как бы получше записать друзей в базу данных, чтобы было побыстрее».

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

Готов предположить, что ваша задача спокойно решается с помощью стандартных средств и производительности MySQL . Не думайте о производительности раньше времени, оптимизируйте только hotspot’ы , и то если производительность по каким-либо причинам не устраивает.

Если интересно, про архитектуру Facebook можно почитать здесь и здесь.

Источник: ru.stackoverflow.com

Занятие 2. Проектирование базы данных. Таблицы и связи. Схема базы данных

Пример создания структуры базы данных (на примере социальной сети)

В этой статье разберем пример создания структуры базы данных для социальной сети.

У нас нет задачи создать полноценную структуру для всех объектов базы, основная идея — показать как выделять объекты и как их связывать, а также показать некоторые тактические моменты по созданию структуры БД.

Описание таблицы БД будет в следующем формате:

Название таблицы — перечень столбцов через запятую.

Во всех таблицах первичный ключ это id int identity(1,1).

Внешние ключи обозначаются как elementID int (суффикс ID подсказывает нам что это внешний ключ на другую таблицу). Это просто наше соглашение для изменования полей.

Шаг 1. Выделение объектов базы данных

Откройте свою страницу в ВК и самостоятельно посмотрите какие объекты можно выделять.

Что есть объект — это некая независимая сущность, информацию о которой мы можем хранить в базе данных.

Я вижу следующие объекты, я сразу их разделю на подгруппы:

  • Пользователь (users)
  • Сообщение на стене (posts)
  • Группа (groups)
  • Участник группы (groupMembers)
  • Сообщение в группе (posts)
  • Альбом (albums)
  • Элемент альбома (albumItems)
  • Сообщение (messages)
  • Комната в чате (rooms)

Этим пока ограничимся (не берем видео, аудио, рекламу и т.д.).

Сразу пишем название будущей таблицы (мы используем практически всегда название во множественном числе).

Шаг 2. Определяем более детально объекты

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

Еще по теме:  Почему Вконтакте не отображается фейс тайм

Особую важность имеют именно связи, т.к. их потом гораздо сложнее менять — важно сразу правильно определить тип связи (один ко одному, один ко многим, много ко многим).

Один ко одному

Случай когда одна строка таблицы однозначно соотносится со строкой в другой таблице. Например, храним мы статьи — в отдельной таблице все мета данные по статье. В другой — только тело самой статьи (для производительности — чтобы основная таблица была относительно небольших размеров). В этом случае 1 строка в первой таблице однозначно соответствует 1 строке во второй таблице.

Один ко многим

Есть у нас сотрудники, у каждого есть определенная должность. Какая связь между должностями и сотрудниками. Можно мыслить следующим образом: Сотрудник может иметь 1 должность. Конкретную должность может занимать несколько сотрудников — значит связь 1 ко многим.

Как она реализуется? С помощью внешнего ключа в таблице humans:

  • posts(id, name) — должности
  • humans (id, fio, postID) — сотрудники.

Много ко многим

Допустим есть у нас студенты и предметы. Один студент изучает множество предметов. Один предмет изучают множество студентов. Это связь много ко многим.

В этом случае необходимо добавление дополнительной таблицы связи, где будут указаны внешние ключи на 2 таблицы:

  • students (id, fio)
  • subjects (id, name)
  • studentSubjects (id, studentID, subjectID) — мы называем таблицу Предметы студента (т.к. это соответствует реальному миру), но бывают и другие названия, например, studentsSubjects (на мой взгляд, более кривое название).

Детализация структуры БД

Теперь вернемся к структуре БД социальной сети:

  • users (id, fio, sex . ) Пользователь — это центральная сущность, у нее может быть куча полей (исходя из профиля и настроек). Здесь мы указываем только самые базовые.
  • messages (id, text, created, userID FK, roomID) Сообщения содержат ссылку на автора сообщения и комнату, где написано это сообщение.
  • roomTypes (id, code, name) — типы комнат. Комнаты могут быть привязаны к каким-то объектам (группа, переписка личная и т.д.)
  • rooms (id, typeID, itemID, created, [some settings for room]) — определяется к чему привязана комната через связку (typeID, itemID), когда создана.
  • roomUsers (id, userID, roomID, created) — кто в комнате (чате) находится. Это связь много ко многим между Пользователями и Комнатами.

Если мы хотим фиксировать кто когда был добавлен и удален в комнате, то делаем дополнительную таблицу

  • roomUsersLog (id, roomID, userID, created, operation), где operation — это либо add либо delete к примеру.

Разберемся с группами

  • groups (id, name, ownerID, desc) — у группы есть владелец (это ссылка на users — один ко многим, у группы может быть только один владелец, а у юзера может быть несколько групп, где он владелец).
  • groupMembers (id, groupID, userID, created, roleID) — какой юзер в какой группе с какой ролью (можно было бы кстати задать владение группой через это поле, тогда можно было бы сделать что у группы может быть несколько владельцев).
  • groupMemberRoles (id, name, code) — роль пользователя в группе (админ, редактор, пользователь). Зачем поле code — для возможного обращения по нему к конкретной роли. Обращение по id из кода — это не очень хороший ход, т.к. при переносе данных из одной базы в другую id могут быть другие и это может сломать работу компонента.

Давайте разбираться с постами

Можно прикрепить посты прямо к группе, но у нас же есть еще личные страницы + в будущем могут еще появиться другие объекты, которым нужна стена.

Поэтому вводим такое понятие как Стена и уже его цепляем к нужному нам объекту. Делается это с целью не плодить кучу однотипных таблиц (посты, комментарии для группы ничем не отличаются от постов и комментариев для личной стены).

  • wallTypes (id, name, code) — тип стены (личная или группы)
  • walls (id, typeID, itemID) — стена задается типом и идентификатором стены (по сути это ссылка на id либо группы, либо юзера. Да, это не чистый внешний ключ (как было бы в случае отдельных таблиц), но зато достигается универсальность в плане хранения данных). Другой вариант мог бы быть хранение groupID и userID (т.е. если groupID не null — тогда это привязка к группе), но в этом случае нам придется менять структуру таблицы при появлении нового типа и что самое плохое — учитывать это изменение где-то в коде.
  • posts (id, userID, created, wallID, text, repostID, likeCount) — это стена группа, ее посты. Автор userID. Связка с группой один ко многим (после принадлежит только одной группе). repostID — это случай, когда наш пост является репостом другого поста. likeCount — количество лайков.
  • postComments (id, userID, created, postID, parentCommentID, likeCount) — указываем кто оставил коммент, когда, на какой пост. Если это комментарий к комментарию, то будет указан родительский комментарий. Также собираем агрегированное количество лайков в отдельном поле.
  • likeTypes (id, name, code) — к чему мы ставим лайк (коммент, пост или что-то еще).
  • likeLog(id, typeID, itemID, value, created, userID) — лог лайков, кто когда поставил лайк на какой объект.
Еще по теме:  Как получить монеты для приложений Вконтакте

Зачем хранить поле likeCount, если эта информация хранится в таблице likeLog? Все дело в производительности. Представьте, нам нужно вывести список постов, а для вывода количества лайков нам необходимо проводить обработку гигантской таблицы likeLog (вообще любую логовую таблицу лучше считать гигантской и обрабатывать очень осторожно в плане выборок).

Когда проектируете связи таблиц, обязательно думайте как это будет использоваться на практике. Например, можно в postComments хранить только для корневых комментов поле postID, а вложенные же можно достать по parentCommentID. На практике это очень плохо будет работать, т.к. вместо того, чтобы просто быстро по postID извлечь все комменты одним махом, вам придется пробегать по всему дереву комментариев в каждом посте. Это большие и ненужные затраты мощностей сервера. Поле postID необходимо ставить в каждом комментарии для быстрого извлечения комментариев на пост.

Разберем галерею фото

Можно рассматривать фото как пост и применить всю структуру данных к картинкам. По сути в этом есть смысл — у фото тоже есть комментарии, тоже есть лайки, есть автор.

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

В нашем случае есть альбомы — это сильно выбивается из концепции Посты. Поэтому давайте сделаем отдельную структуру.

  • albums (id, name, desc, created, userID, ord, coverImageID) — альбомы с названием, когда создан, кто владелец. ord задает порядок вывода. coverImageID задает ссылку на обложку.
  • albumImages (id, name, img, thumb, desc, albumID, created, likeCount, ord) — картинки в альбоме

Для хранения лайков мы можем задействовать таблицу likeLog (в эти моменты как раз и начинаешь ценить универсальный подход с typeID, itemID).

Заметим, что в данной структуре не получится хранить фото без альбома (т.е. albumID задает связь с владельцем фото).

Сами картинки хранятся как пути к файлам на сервере (малое и большое фото — thumb, img). В реальном приложении это может быть отдельная таблица ресурсов, и тогда это будут уже внешние ключи на эту таблицу.

Поле ord типа int задает порядок размещения фото в альбоме.

Кого-то может смутить такая зацикленная связь: albums ссылается на albumImages через coverImageID, а картинки ссылаются на альбомы через albumID. Тут лучше просто рассматривать ключи полностью отдельно.

coverImageID указывает нам на обложку альбома (она могла бы быть потенциально и в другой таблице), а albumID задает к чему цепляется наше фото (образно говоря в какой папке находится).

Нет ли здесь проблемы курицы и яйца? Нет. Мы создаем сначала альбом с coverImageID = NULL. Когда создаем первое фото в альбоме, то идет проверка: если не было еще фото, то у альбома тогда проставим coverImageID.

Либо другая может быть логика — создаем просто фото. Если альбом не был выбран из существующих, то создаем альбом Альбом 1 с coverImageID = NULL, затем создаем фото с новым albumID и после этого устанавливаем coverImageID, равным id новой картинки.

Шаг 3. Самостоятельное дополнение полей и объектов.

Мы, конечно, много чего не учли в этой структуре БД:

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

И это мы говорим только о внешней части. А есть еще и внутренняя:

  • модерация пользователей
  • контроль постов.
  • обработка жалоб и вопросов в техподдержке.
  • аналитика по разным разрезам (это может быть гигантский раздел).
  • множество ролей в закрытой части (модератор, финансист, директор, техподдержка и т.д.).
  • работа с внешними интеграциями
  • платформа разработки
  • документация, внутренняя база знаний, форум
Еще по теме:  Как сделать режим офлайн в ВК

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

Детализируйте их настолько, сколько позволяет вам внешний интерфейс социальной сети.

Заключение

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

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

Источник: falconspace.ru

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Cancel Create

Database / HW_3_Webinar_3 / vk_db_update.sql

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Cannot retrieve contributors at this time
62 lines (47 sloc) 4.6 KB

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents Copy raw contents

Copy raw contents

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters

/* Задача: Написать скрипт, добавляющий в БД vk, которую создали на занятии, 3 новые таблицы (с перечнем полей, указанием индексов и внешних ключей) */
/* Никогда ранее не заходил во вкладку Товары соц.сети Вконтакте, зашел, ужаснулся) решил описать примерную предполагаемую структуру */
DROP DATABASE IF EXISTS ` vk ` ;
CREATE DATABASE ` vk `;
USE ` vk ` ;
DROP TABLE IF EXISTS ` goods_categories ` ;
CREATE TABLE ` goods_categories ` (
` category_id ` SERIAL PRIMARY KEY , — Уникальный ID категории
` category_name ` VARCHAR ( 255 ) UNIQUE, — Название категории товаров
` created_at ` DATETIME DEFAULT NOW(), — Дата создания
` updated_at ` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , — Дата обновления
INDEX category_name_idx(category_name) — Задумался, но решил оставить индекс для нужд поиска по категориям, под вопросом решение
) COMMENT = ‘ Таблица категорий товаров ‘ ;
DROP TABLE IF EXISTS ` goods_description ` ;
CREATE TABLE ` goods_description ` (
` goods_id ` SERIAL PRIMARY KEY , — Уникальный ID товара
` goods_name ` VARCHAR ( 255 ), — Название товара
` goods_photo ` VARCHAR ( 255 ) DEFAULT NULL , — Изображение товара
` goods_description ` TEXT , — Описание товара
` goods_price ` DECIMAL ( 10 , 2 ), — Стоимость товара
` goods_category ` BIGINT UNSIGNED NOT NULL , — Категория товара
` user_posted_id ` BIGINT UNSIGNED NOT NULL , — Пользователь предлагающий товар/услугу
` created_at ` DATETIME DEFAULT NOW(), — Дата создания
` updated_at ` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , — Дата обновления
/* Индексацию решил провести по этим полям для ускорения вывода наиболее свежих товаров */
INDEX goods_category_idx(goods_category),
INDEX goods_updated_at_idx(updated_at),
FOREIGN KEY fk_goods_category(goods_category) REFERENCES goods_categories(category_id),
FOREIGN KEY (user_posted_id) REFERENCES users(id)
/* Так же хотел попробовать связь с таблицами `photos` и `photo_albums` на случай загрузки изображений товара в пользовательский фотоальбом,
* но совсем туго пошло с возможными вариантами решения данной задачи, отбросил, непонимаю как сделать, задумался и о целесообразности))) */
) COMMENT = ‘ Таблица с описанием товара ‘ ;
/* Возникла идея описать таблицу новостной ленты, вместе с ней затянуло в такие тополяяя))) так и не додумался, запутался окончательно от куда ноги растут, очень много связей
* с разными таблицами будет на мой взгляд, решил пойти по более простому пути, сомнения только в том, решается ли это при помощи БД?, можно и программно реализовать,
* без ответа */
DROP TABLE IF EXISTS ` activity_status ` ;
CREATE TABLE ` activity status ` (
` active_user_id ` SERIAL PRIMARY KEY NOT NULL ,
` activity status ` ENUM( ‘ Online ‘ , ‘ Offline ‘ ),
` created_at ` DATETIME DEFAULT NOW(), — Дата создания, для отслеживания времени последней активности
/* Дата обновления, для смены статуса в режиме реального времени, например графического отображения внутри приложения */
` updated_at ` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
INDEX active_user_id_idx(active_user_id),
/* Предположительно должно ускорить графический отклик и отображение */
INDEX updated_at_idx(updated_at),
FOREIGN KEY fk_active_user_id(active_user_id) REFERENCES users(id)
) COMMENT = ‘ Статус активности пользователей ‘ ;
  • Copy lines
  • Copy permalink
  • View git blame
  • Reference in new issue

Источник: github.com

Рейтинг
( Пока оценок нет )
Загрузка ...