48

Click here to load reader

Реляционный слой модели: паттерны и антипаттерны

  • View
    1.380

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Реляционный слой модели: паттерны и антипаттерны

Реляционный слой модели:паттерны и антипаттерны

Interlabs

11 ноября 2013

1 / 48

Page 2: Реляционный слой модели: паттерны и антипаттерны

Основная проблема

• cовместимость между ОО и БД — за счет сложного ORM• много кода и кoнфигурирования на стороне приложения• content repository — отдельная длинная история

Сложно, ресурсоемко, неудобно.2 / 48

Page 3: Реляционный слой модели: паттерны и антипаттерны

Что мы хотим?

• реляционная БД как хранилище по умолчанию• максимально простой слой ORM• расширяемость на уровне базы и модели приложения• возможность организации (подобия) контент-репозитория• работа непосредственно с БД, если необходимо• использование преимуществ реляционных СУБД• собственный код в слое модели, только если необходимо• возможность определять в коде поведение сущностеймодели, если необходимо

3 / 48

Page 4: Реляционный слой модели: паттерны и антипаттерны

Разумный компромисс— универсальность компонентов, + более простая реализация

Слой модели можно упростить, если не решатьвсе проблемы на стороне ORM.

4 / 48

Page 5: Реляционный слой модели: паттерны и антипаттерны

Схема имен и метаданные

Page 6: Реляционный слой модели: паттерны и антипаттерны

Именование объектов базыАнтипаттерн «эстетическая схема именования», разные схемы

имен в базе и модели, необходимо постоянноепреобразование, имена таблиц из имен классов.

6 / 48

Page 7: Реляционный слой модели: паттерны и антипаттерны

Именование объектов базы• минимизируем различия между базой и моделью• расширяемость через иерархическую схему имен• класс сущности — деталь реализации, не отражен в базе

7 / 48

Page 8: Реляционный слой модели: паттерны и антипаттерны

Схема именВ модульной CMS необходима расширяемость на уровне базы,нужны пространства имен.

Типы Таблицы

blog.blog blog_blogblog.post blog_postblog.post.content blog_post_contentauth.user auth_usercommerce.group commerge_group

Классы модели — реализация и не отражаются в базе. Незабываем об ограничениях по длине имени.

8 / 48

Page 9: Реляционный слой модели: паттерны и антипаттерны

Описание структуры

Антипаттерн аннотации, рефлексия на стороне модели/ORM.

• cargo-культ Java без нормальной поддержки на уровнеязыка

• ресурсоемкий парсинг аннотаций, необходимостькеширования и автогенерации кода

• рефлексия: привязка схемы данных к реализации, низкаяпроизводительность

• дополнительные телодвижения для использованияметаданных на клиенте

9 / 48

Page 10: Реляционный слой модели: паттерны и антипаттерны

Описание структуры

Решение хранение информации о структуре базы вотдельном json-файле

• простой, легко читаемый и редактируемый формат• из нескольких файлов для различных модулей легкопостроить композицию (array_replace_recursive())

• может использоваться как на сервере (PHP) так и наклиенте (JavaScript)

10 / 48

Page 11: Реляционный слой модели: паттерны и антипаттерны

Идентификаторысущностей

Page 12: Реляционный слой модели: паттерны и антипаттерны

Автоинкрементный idПрост в использовании, эффективный кластерный индекс, но:

• непереносим между разными копиями базы→ проблемыс миграцией контента

• свой для каждой таблицы→ сложнее реализоватьуниверсальные отношения между разными типами данных

• генерируется после записи в таблицу — часто неудобнодля реализации редактирования

• «утекание» значений id при ON DUPLICATE KEY• плохо масштабируется

В ряде случаев можно считать антипаттерном

12 / 48

Page 13: Реляционный слой модели: паттерны и антипаттерны

Уникальный глобальный id

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

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

• упрощает масштабирование

13 / 48

Page 14: Реляционный слой модели: паттерны и антипаттерны

Проблемы глобального id

Использование глобального id не так просто, ведь он:

• должен быть глобальным и уникальным• должен быть упорядоченным, иначе проблемы скластерными индексами

• должен занимать как можно меньше места• должен относительно легко генерироваться• должен корректно обрабатываться и в базе, и на клиенте

В идеале — встроенный тип данных в базе, на практике — увы.

14 / 48

Page 15: Реляционный слой модели: паттерны и антипаттерны

Выбор глобального id

• строковый GUID — много места, неупорядочен• модифицированный строковый GUID — можноупорядочить, но все равно много места

• бинарный модифицированный GUID — возможныйвариант, но неудобно работать с базой в ручном режиме(нечитаемые идентификаторы)

• целочисленный 64-разрядный идентификатор — наиболеепримлемый вариант

id BIGINT(20) NOT NULL

15 / 48

Page 16: Реляционный слой модели: паттерны и антипаттерны

64-разрядный id

• тип данных BIGINT, большой, но приемлем• начиная с 5.1 — встроенная функция UUID_SHORT()• в более ранних версиях можно написать аналог• не зависит от разрядности платформы на стороне сервера• достаточно уникален (см. документацию1) и упорядочен• можно генерировать триггером (но скорее всего не нужно)

SELECT UUID_SHORT();

1http://dev.mysql.com/doc/refman/5.1/en/miscellaneous-functions.html16 / 48

Page 17: Реляционный слой модели: паттерны и антипаттерны

Работа с 64-разрядным id

• отсутствует поддержка 64-разрядного int в PHP на 32bit• на клиенте всегда работаем со строковым представлением• PHP преобразует строки в числа для индексов массивов,но поведение отличается на 32/64bit, помним об этом

• в идеале — генерация на клиенте, но приходитсяотдельной операцией на сервер из проблем с рарядностью

• тем не менее у нас всегда есть id-записи до ее помещенияв базу данных

• можем использовать id еще до выполнения записи в базу• идентификатор в sphinx совпадает с id записи

17 / 48

Page 18: Реляционный слой модели: паттерны и антипаттерны

Структура базы

Page 19: Реляционный слой модели: паттерны и антипаттерны

Типовые проблемы ORMС точки зрения структуры таблиц и результатов запросовobject-relational mismatch чаще всего проявляется в видеследующих проблем:

• частичная загрузка сущности для снижения потребленияресурсов

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

• прозвольная компоновка атрибутов разных сущностей прииспользовании JOIN

• отображение записей на иерархию классов сущностей

19 / 48

Page 20: Реляционный слой модели: паттерны и антипаттерны

Частичная подгрузкаАнтипаттерн: частичная загрузка объекта для снижения

потреления ресурсов

• состояние объекта в ООП не может быть частичным• всегда загружать объект целиком — накладно• явно указывать список атрибутов для загрузки — неудобно• во многих ORM использование частичной загрузки нерекомендуется

Отказываемся от частичной подгрузки

20 / 48

Page 21: Реляционный слой модели: паттерны и антипаттерны

Дополнительные атрибутыАнтипаттерн: динамические COUNT() с GROUP BY, результаты

JOIN с сущностями другого типа.

• непонятно, где размещать результат с т.з. системы типовсущностей (разумные варианты2 обычно не типизированы)

• JOIN сложно описывать в рамках объектого API• использование JOIN и агрегатных вычислений каждый раз,когда нужно вывести результат — очень неэффективно

Отказываемся от загрузки дополнительныхатрибутов и использования JOIN

2https://code.google.com/p/dapper-dot-net/21 / 48

Page 22: Реляционный слой модели: паттерны и антипаттерны

Иерархия классовНужно хранить иерархию классов сущностей с отличающимисяатрибутами. Три классических способа:

• одна sparse table для всех классов• отдельная таблица для каждого класса• общая таблица для общих атрибутов и дополнительныетаблицы для отличающихся

Антипаттерн: сложная логика ORM, использование JOIN приработе с несколькими таблицами, неоптимальныйsparse table

Меньше наследования, больше композиции22 / 48

Page 23: Реляционный слой модели: паттерны и антипаттерны

Структура основанная на композиции• один тип сущности — одна таблица• данные таблицы всегда загружаются целиком (SELECT *)• необходимость частичной загрузки — повод использоватькомпозицию

• иерархии классов для сущностей — редко и скорее дляповедения

• JOIN — либо вообще не используются, либо используютсяво VIEW

• с точки зрения ORM JOIN не существует• агрегатные вычисления — либо VIEW либо денормализация

Денормализация наше все

23 / 48

Page 24: Реляционный слой модели: паттерны и антипаттерны

Композиция: пример

24 / 48

Page 25: Реляционный слой модели: паттерны и антипаттерны

Объектная модельМинимальная объектная модель по умолчанию, без написаниякода:

$blog->title // атрибут$blog->author // ссылочная сущность (1:1)$blog->author->profile->bio // атрибут композиции$blog->details->about // атрибут композиции

$blog->posts() // связанные сущности (1:N)

$post->comments->replies() // связанные сущности$post->comments->numOfReplies // денормализованый атрибут$post->blog // связанная сущность$post->content->body // атрибут композиции

25 / 48

Page 26: Реляционный слой модели: паттерны и антипаттерны

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

Антипаттерн: JOIN и агрегатные функции по любому поводу.

• производим вычисления только при изменении данных• сохраняем вычисленные значения в специальных полях• единый id может облегчить задачу• обновление значений — через триггеры илипериодические процессы

Выигрываем в простоте ORM (не нужно поддерживатьвычисляемые поля, дополнительные атрибуты, GROUP BY и т.д.)и в производительности.

26 / 48

Page 27: Реляционный слой модели: паттерны и антипаттерны

Entity Attribute Value (E.A.V.)Антипаттерн произвольная схема поверх фиксированной

схемы SQL, самый печально известныйантипаттерн

Возможность менять структуру данных, не меняя структурыбазы, но какой ценой:

• невозможность работать с базой без клиентского кода• отсутствие constraints• невозможность использования индексов• неэффективное хранение различных типов данных• неоднородность подхода — часть данных в традиционнойсхеме, часть — в E.A.V.

Примеры: инфоблоки Битрикс, каталоги товаров Magento27 / 48

Page 28: Реляционный слой модели: паттерны и антипаттерны

Альтернатива E.A.V.

• если допускается изменение структуры базы: композицияотдельных таблиц под разные наборы атрибутов

• если нет — лучше отказаться от реляционной базы• глобальный идентификатор + декомпозиция упрощаютзадачу

• изменение структуры данных меняет структуру базы, ноэто меньшее зло

• использование композиции таблиц не означаетиспользование JOIN!

Пример сходной идеологии — типы данных в Drupal (но с JOIN).

28 / 48

Page 29: Реляционный слой модели: паттерны и антипаттерны

Сериализованные значенияАнтипаттерн хранение сложного сериализованного значения в

TEXT-поле

Соблазн использования большой но редко окупается:

• для поиска необходимо извлечение отдельных значений всамодельный индекс

• сложно работать непосредственно с базой, особенно дляспецифичных форматов (PHP serialize)

• лучше использовать стандартные форматы (JSON, YAML) итолько для данных, по которым гарантированно непроводится поиск

• интересный вариант — индексация JSON в Sphinx

29 / 48

Page 30: Реляционный слой модели: паттерны и антипаттерны

Отношения междусущностями

Page 31: Реляционный слой модели: паттерны и антипаттерны

1→ 1

$post = $model->from(’blog.post’)->find($id);print $post->blog->title;

• объектная модель — динамические свойства объекта• foreign key на уровне базы

31 / 48

Page 32: Реляционный слой модели: паттерны и антипаттерны

1→ N

$blog = $model->from(’blog.blog’)->find($id);foreach ($blog->posts() as $post) {

print $post->title;}

• объектная модель — динамический вызов• foreign key на уровне базы

32 / 48

Page 33: Реляционный слой модели: паттерны и антипаттерны

Проблемы 1→ NАнтипаттерн: реализация в виде коллекции сущностей,

заполняемая ORM при загрузке объекта илипрокси-объектом

В блоге 1000 записей, мы действительно хотим подгружать все,пусть даже лениво?

• мы никогда не работаем с полной коллекцией связанныхсущностей

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

33 / 48

Page 34: Реляционный слой модели: паттерны и антипаттерны

1→ N: еще примеры

$blog = $model->from(’blog.blog’)->find($id);

foreach ($blog->posts()->sort([’pubDate’ => -1])->limit(20) as $post

) {foreach ($post->comments() as $comment) {

print $comment->author->email;}

}

Выборка $post->comments() ограничена в определенииотношения в конфигурации модели.

34 / 48

Page 35: Реляционный слой модели: паттерны и антипаттерны

1 = 1: декомпозиция

foreach ($blog->posts() as $post) {print $post->content->body;

}

• объектная модель — динамическое свойство• хорошая альтернатива частичной загрузке 35 / 48

Page 36: Реляционный слой модели: паттерны и антипаттерны

N→ M

• c точки зрения API те же проблемы, что и 1→ N, толькохуже.

• классический вариант с таблицей с двумя ссылочнымиполями — проблема с кластерным индексом.

• часто отношение содержит дополнительные атрибуты,проще всего рассматривать как два отношения 1→ N.

• можно ввести дополнительные read-only типы дляупрощения клиентского кода, реализованные через VIEW.

36 / 48

Page 37: Реляционный слой модели: паттерны и антипаттерны

N→ M: пример

37 / 48

Page 38: Реляционный слой модели: паттерны и антипаттерны

Иерархии

Антипаттерн ajacency list, дополнительные плагины к ORM

Иерархии очень важны, особенно для хранения контента.Должна быть полноценная поддержка на уровне ORM.

• используем автоматически поддерживаемые closuretables3

• на уровне API parent — динамическое свойство,children() — метод возвращаемой коллекции

3http://technobytz.com/closure_table_store_hierarchical_data.html38 / 48

Page 39: Реляционный слой модели: паттерны и антипаттерны

Иерархии: примерВыборка всех дочерних сущностей производится однимединственным запросом.

function tree($children, $origin) {foreach ($children as $group) {

printf("%s %sn", $group->id, $group->name);tree($children->children($group), $group);

}}

$group = $mode->from(’commerce.group’)->find($id);tree($group->children(), $group);

39 / 48

Page 40: Реляционный слой модели: паттерны и антипаттерны

Уровень модели

Page 41: Реляционный слой модели: паттерны и антипаттерны

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

поведения, фактически — структур данных

Но в ряде случаев этого достаточно, особенно для веб-сайта!

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

• можно использовать ORM для вывода контента вообще несоздавая классов сущностей.

foreach ($model->from(’blog.blog’)->limit(5) as $blog) {foreach ($blog->posts()->limit(10) as $post) {print $post->content->body;

}}

41 / 48

Page 42: Реляционный слой модели: паттерны и антипаттерны

Язык запросовАнтипаттерн реализация собственного языка запросов через

DSL или аналог SQL в ORM.

• нельзя объять необъятное, достаточно реализоватьнебольшое подмножество для основных операций

• фильтрация по значениям атрибутов, сортировка,ограничение объема выборки

• используем массив для описания запроса• легко и быстро создать и модифицировать• можно реализовать композицию запросов• можно преобразовывать в JSON и использовать на разныхуровнях приложения

• можно прописывать в конфигурации42 / 48

Page 43: Реляционный слой модели: паттерны и антипаттерны

Определение запроса$posts = $model->from(’blog.post’)->query(

[ "filter" => ["pubDate" => [ ">" => $from ],"state" => [ "=" => STATE::PUBLISHED ]

],"sort" => [

"pubDate" => -1,],"limit" => 10

]);

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

43 / 48

Page 44: Реляционный слой модели: паттерны и антипаттерны

Проблема 1:N запросовАнтипаттерн: генерация отдельного запроса для каждой

выбранной сущности для полученияассоциированной сущности. JOIN дляминимизации количества запросов.

• JOIN — медленно и плохо масштабируемо, большоеколичество избыточных данных

• выборка по значениям идентификаторов — гораздолучшая идея

NotORM гениальная идея4, плохая реализация5

4https://www.facebook.com/jakubvrana/posts/4153596751514305http://www.notorm.com

44 / 48

Page 45: Реляционный слой модели: паттерны и антипаттерны

Выборка по набору id// SELECT * FROM blog_blog LIMIT 10foreach ($model->from(’blog.blog’) as $blog) {

// SELECT * FROM auth_user WHERE id IN (id1,id2, ...)// id1, id2, id3 — значения поля authorprint $blog->author->email;

// SELECT * FROM blog_post WHERE blog IN (id1, id2, id3) LIMIT 20// id1, id2, id3 — идентификаторы блогов в первой выборке

// SELECT * FROM blog_post_content WHERE id IN (id1, id2, i3);// id1, id2, id3 — идентификаторы постов из предыдущего запросаforeach ($blog->posts()->limit(20) as $post) {print $post->content->body;

}}

4 вида сущностей — 4 запроса независимо от количества сущностей.45 / 48

Page 46: Реляционный слой модели: паттерны и антипаттерны

Выборка по набору id

• используется PRIMARY или FOREIGN KEY• не передается лишних данных, как часто получается вслучае JOIN

• проще оптимизировать, чем JOIN• нет JOIN — можно хранить различные типы в разных базах• или на разных серверах• или использовать индексы Sphinx (тот же паттерн доступа)• или использовать кеш

46 / 48

Page 47: Реляционный слой модели: паттерны и антипаттерны

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

• ORM и слой модели гораздо проще, если пойти имнавстречу со стороны базы

• триггеры и view могут упрощают клиентский код• денормализация — это очень важно• глобальный id — очень полезно• выборка по набору id — тоже• классы модели — единицы поведения сущностей, а неструктуры данных

47 / 48

Page 48: Реляционный слой модели: паттерны и антипаттерны

Что читатьСсылки из слайдов, а также:

• SQL Antipatterns Strike Back6 — типовые антипаттерны реляционных БД• Models for Hierarchical Data7 — работа с деревьями в SQL• Trees and Hierarchies in SQL for Smarties8 — книга по иерархическимструктурам в SQL

• Nuxeo VCS Architecture9 — архитектура контентного репозитория набазе реляционной СУБД

• Data Normalization, Denormalization and the Forces of Darkness10 —статья про денормализацию

6http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back7http://www.slideshare.net/billkarwin/models-for-hierarchical-data8http://amzn.to/HMRuja9http://doc.nuxeo.com/display/public/NXDOC/VCS+Architecture10http://fastanimals.com/melissa/WhitePapers/

NormalizationDenormalizationWhitePaper.pdf48 / 48