3. Tagging
• Keyword assigned to an object
• Chosen informally and personally by item's creator or viewer
• Assigned by the viewer + unlimited = Folksonomy
• Assigned by the creator + limited = Taxonomy
30. Postgres nested tables
…………
moves field values out-of-line
…………
wider than TOAST_TUPLE_THRESHOLD bytes (normally 2 kB)
…………
in a chunk which stored as a separate row in the TOAST table belonging to the owning table
33. Ingestion
0 500 1000 1500 2000 2500
Relational
Denormalized
Complex data type
Full text search
Insert time, seconds
34. Database size
0 200 400 600 800 1000 1200 1400
Relational
Denormalized
Complex data type
Full text search
Volume
Index size, MB Data size, MB Size total, MB
36. Search by Id, tag list retrieval
0 0.0005 0.001 0.0015 0.002 0.0025 0.003 0.0035
Relational
Denormalized
Complex data type
Full text search
Speed with hot cache, seconds
39. Search by a single tag, tag list retrieval
0 0.001 0.002 0.003 0.004 0.005 0.006
Relational
Denormalized
Complex data type
Full text search
Speed with hot cache, seconds
45. Search by multiple tags, tag list retrieval
0 0.5 1 1.5 2 2.5
Relational
Denormalized
Complex data type
Full text search
Advanced relational
Advanced denormalized
Speed with hot cache, seconds
73. Index magic
• Space saving: 40%
• Ingestion degradation: 1%
• Search improvement: 4%
74. Final architecture
• Async in UI = hide performance issues
• Denormalized tagging model
• Compressed B-tree indexes – search by tags
• Full text search indexes – name and description
Average search time
0.4 second
82. Proper search
FTS
inside DB
+
FTS model
Relational/denormalized/FTS
model
• Approach 1 • Approach 2
FTS server
(Lucene, Sphinx,
Elastic, Solr,
Xapian, etc)
Application
server
Application
server
83. Apache Solr
• True open source (under Apache)
• Built over Lucene
• Multi-language support
• Various client APIs
• Versatile query language
• Dedicated language for faceted navigation
• Extremely scalable with auto-scale framework
• Full of additional features
91. Full text search performance
0 0.005 0.01 0.015 0.02 0.025 0.03 0.035 0.04 0.045
Retrieval by id
Retrieval by single tag
Retrieval by multiple tag
Speed, seconds
Solr FTS hot cache DB FTS hot cache
93. Conclusion
1. Try and measure
2. Trust in your DBMS unique features
3. Understand how DBMS features created internally
4. No conditions + no faceted navigation = denormalized RDBMS
5. Conditional queries by tags + faceted navigation = FTS server
6. Search by tags <> analytic by tags
94. THANK YOU
WE ARE HIRING!
Alexander Tokarev
Database expert
DataArt
shtock@mail.ru
https://github.com/shtock
Editor's Notes
Добрый день. Меня зовут Александр и я решаю в компании DataArt вопросы, связанные с архитектурой приложений, особенно в части баз данных.
Я не думаю, что сегодня будет много оракл-специфичных вещей, но возможно вы откроете что-нибудь новое для себя.
Сегодня мы обсудим такие простые и одновременно сложные вопросы, как
поиск по тегам,
какие он можно создать вызовы как в части базы данных, так и архитектуры приложения в целом
Как же должна в итоге выглядеть база данных
И как оптимально осущестлять навигацию по тегированным объектам.
Я расскажу каким получился наш движок в условиях наших ограничений.
После сеции вопросов и ответов я дам ссылки на гитхаб и исходные данные
Что такое тэг на первый взгляд понято:
Это некое ключевое слов присвоенное к тегируемому объекты
Чаще всего теги задаются неформально либо автором, либо тем, кто просматривает тегируемый объек
Если теги неограничены и присваютрся автором, это называется фолксономия (от слова folk - народ)
Если же набор ограничен и присвоен создателем, то это таксономия
Собственно как теги представлены визуальнои в вашем приложении и каков их типовой сценарий использования это и определяет архитектуру возможного решения.
Это может быть как стандартное облако тегов, так и весьма аскетичное в стиле StackOverflow
Собственно от щелчком на теги которого мы переходим все объекты с данным тегом и получаем подобие faceted навигации в правом угле
И можем уже провалиться в конкретный объект с набором тегов
Более сложной формой представления, которую иногда в ядре строят на тегах является фацетная навигация, где теги круппируются в фацеты и по тегам выводится статистика. Соответсено результат поиска постепенно сужается при выборе нужных тегов. Так как для фацетов используется AND, а для тегов используется OR, это приводит к определённым нюансам, которые мы рассмотрим позже.
Почему же теггинг это хорошо?
1. Потому что он отражает словарь пользовател1
2. Решение гибкое – сам добавил, сам удалил
3. Многомерно по-сути и поэтому тегируемый объект и
Гибкость приводит и к ожидаемым проблемам:
Ошибки написания, множественное число, составные слова
Неоднозначное присвоение тегов, теги понятные только их автору
Использование для описание одного и того же различных синонимиов, акронимов и так далее
Таким образом, гибкость приводит нас к ожидаемым проблемам1. производительности
2. Тяжести запросов3. Неконтролируемому увеличению базы данных
4. Дополнительное обслуживание базы данных тегов
На самом деле программист должен сделать в своей жизни ряд вещей:
Написать Hello World
Посадить дерево
Сделать свой tagging search
Как сделать его более быстрым сейчас и рассмотрим
Скорее всего, первая попытка сделать движок поиска по тегам и фацетную нафигацию на нём выйдет, но будет весьма незрела.
Чтобы оценить её мне кажется как никогда более подходит всем известная вам картинка
Собственно пингвинами мы и будем мерить зрелость нашего решения
Если подойти очень обще, то чаще всего архитектурные решения для поиска по тегам и фацетной навигации делятся на 2 вида:
Теги хранятся в реляционной базе данных в виде таблиц и общение с ними происходит на языке SQL
Теги находятся в полнотекстовой базе данных
Прежде чем мы начнём рассматривать различные модели данных и наш движок надо определиться с тестовыми данными.
Я изначала подумал, что неправильно будет создавать абстрактные тестовые данные и воспользовался очевидным источником, который рекомендую и вам для различных экспериментов.
Это StackOverflow
Обратите внимание, что тегов на пост максимум 5 – это явно сознательное ограничение платформы
Сайт Stackoverflow имеет прекрасный интерфейс для выгрузки данных, в котором можно писать sql к 22 таблицам StackOverflow и сохранять запросы за исключением того, что после 100000 строк надо вводить капчу.
Помните, что я вначале рассказывал, что наличие тегов приводит к некоим проблемам. Например, используются синонимы. Когда я подготавлива тестовые данные в StackOverflow я нашёл ту самую таблицу синонимов.
Попробуем посмотреть на модели БД с точки зрения их зрелости. Первая модель – самая очевидная и нормализованная.
Есть таблица тэгов, тегируемых документов и связь между ними.
Я бы сказал это начало с точки зрения нашей penguin maturity model.
Когда рано или поздно возникает проблема производительности большинство переходит к денормализованной модели с не менее очевидной структурой.
Тэги привязаны сразу же к документу без промежуточной таблицы.
Тем не менее при увеличении набора данных данных даже такой денормализации становится недостаточно и тут обычно все пытаются уже использовать фишки СУБД
Какие? Абсолютно понятные – возможность хранить составные типы данных в одной строке с данными исходного документа.
Наиболее опасный момент данного решение состоит в том, чтобы понять как это решение устроено изнутри и не является ли такой сложный тип замаскированными таблицами.
Давайте посмотрим на эти типы данных в Oracle
Теперь посмотрим, что в оракле
Для начала создаём теги и массив из них
А далее указываем, что данный массив должен находиться в таблице
Есть некий синтаксический сахар, который наталкивает на мысли о дополнительной проверке
Проверяем не является ли TAGS_TAB обыкновенной таблицей и вроде бы данных нет, НО!
На первый взгляд всё хорошо, ибо таблицы нет, но…
Однако когда мы смотрим в таблицу объектов и индексов, то видно, что всё же таблица существует, но её не видно простыми средствами.
Столбцы её видны в другом месте, что показывает, что в целом этот вариант аналогичен варианту с денормализацией и разница проявится в будущем только в запросах
Создадим тогда обычный уникальный индекс для ускорения поиска, ибо у Оракла нет аналогов GIN и начнём замеры
Однако когда мы смотрим в таблицу индексов, то видно, что всё же таблица существует, но её не видно простыми средствами.
Столбцы её видны в другом месте, что показывает, что в целом этот вариант аналогичен варианту с денормализацией и разница проявится в будущем только в запросах
Создадим тогда обычный уникальный индекс для ускорения поиска, ибо у Оракла нет аналогов GIN и начнём замеры
В оракле тем не менее существует тип данных, который позволяет хранить массивы в рамках одной строки без вспомогательных объектов. Как вы думаете, что это за тип?
Это vararray
Обратите внимание, что я указал, что он будет храниться как lob.
Собственно если я его не буду указывать как lob, то он будет храниться в блоке данных до 4000 строк, но после – всё равно как LOB. Из этого и вытекает главный недостаток.
Добавляем данные, индексируем и
Получаем ошибку
Собственно поэтому дальше этот тип данных я рассматривать не буду
Посмотрим как это сделано в постгресс.
Создаём таблицу с типом данных Массив, заполняем её данными идентицируем файл, в котором она находится
Создаём таблицу с типом данных Массив, заполняем её данными идентицируем файл, в котором она находится
Собственно вот наш файл, причём размером 8 килобайт – стандартный размер страницы в пострессе и мы видим, что с Postgress ситуация несколько лучше – массивы хранятся в том же блоке данных, что и основная таблиц, однако есть нюанс
Если почитать документацию Postgres, то можно увидеть, что inline не настоящий Но я слабо верю, что тегов может стать так много, что значения выйдут за 2 килобайта
Но самое главное, что постгресс позволяет индексировать массивы с использованием GIN (Generalized Inverted Index) индекса, который является обычным инвертированым индексом.
Следующим шагом видится использование полнотекстовых средств в СУБД. Теги можно сохранить в разном формате. Например, с разделителями в подобии XML или в JSON.
Определившись с данными моделями, посмотрим на их количественные характеристики
Самая первая характеристика – время вставки. Как мы видим по скорости выигрывает значительно реляционная модель. В целом ничего удивительно. Id тегов разово зачитаны в кэш, а дальше идёт lookup их id и вставка.
Худший вариант на удивление не полнотекстовый, а со сложными типами данных, но я подозреваю, что это нюансы Оракла, а проверить на постгрес я не успевал. Скорее всего ситуация будет иная.
Кстати, как вы думаете, какая модель данных будет занимать больше места?
Посмотрим, что у нас получается с точки зрения места.
Важно обратить внимание, что в целом модели с точки зрения места примерно равны. Огромной разницы не наблюдается. Разница наблюдается только в пропорции Данные/Индекс. Также стоит обратить внимание, что в целом 8 миллионов тегов занимают более чем скромный объем места, что говорит об уместности inmemory баз данных для данной задачи.
Собственно запросы поиска по ID по каждой модели абсолютно понятны
Собственно тут более позновательны результаты, так как в случае денормализованных/нормализованных моделе возвращается набор строк, а полнотекстовой/сложных типов – одну строку
Запомните это – все запросы помещаются на одну страницу
В целом я показал время до и после прогрева кэшей СУБД, но в целом я бы сконцентрировался после прогрева. Тут явным фаворитом является полнотекстовая модель, так как не требует вообще никаких дополнительных действий для извлечения информации.
Я даже специально разместил их на разных диаграммах, так как порядки скоростей очень сильно отличаются
Из-за нюансов того, как в оракле организованы сложные типы данных, по которым можно индексировать очевидно, что денормализованная и модель со сложными типами данных совпадают.
На одном теге уже начинает проявляться разница. Само собой, она уже сильно зависит от СУБД.
В целом, возможны различные формы написания запроса в денормализованной и нормализованной форме для одного тэга, например с exists или in, но уже по количеству строк видно, что ситуация весьма тяжела
Собственно для сложных типов данных и для полнотекстового поиска запросы очень читаемы
Вообще то, возможно следовало усложнить данный тест искав по тегу с более чем средним количество, менее чем средним, да ещё и добавив бы пейджинг с выводом первой страницы, и, например, третьей, но я не думаю, что от этого много что изменится с точки зрения пропорций.
Как всегда полнотекстовый поиск показывает весьма неплохие результаты.
Итак, начинается самое интересное.
2 тега в поиске. Обратите внимание, что это AND – мы оч.ень плавно подходим к идее faceted search, поэтому очевидный на первый взгляд OR не подходит
Уже два запроса не помещаются на слайд.
Собственно с денормализованным подобная же штука
Однако, если у вас в команде завёлся редкий зверь, знающий SQL, больше чем ORM, то у вас появится что-то гораздо менее читаемое, но эффективное. Тут вся магия показана на count, так как нам надо имитировать and условие не джоиня одну таблицу множество раз
Нормальный оптимизатор буквально вытягивает этот план и запрос работает более чем быстро
Более того, этот запрос очень удобен для кодогенерации
В целом такой же трюк проделывается с денормализованной таблицей
Полнотекстовые запросы и запросы со сложными типами данных очень простые.
Полнотекстовый в оракле очень выразительный – мы условие пишем прямо внутри запроса
Чем больше тегов в условиях, тем грустнее и грустнее поиску на реляционной модели данных. В общем как мы видим, деградация порядка 50 раз, тогда как у полнотекстового растёт пропорциональн количеству тегов в запросе.
Собственно особо cloud tag никто на лету не собирает – его предрасчитывают, но мы попробуем для общего развития
Для реляционной модели можно выкинуть табличку Document, так как она в целом нам не нужна для расчёта количества
Денормализованная как всегда очевидна
В целом, эти запросы можно сократить до следующих, так как таблица document не участвует в списке полей для вывода
Что интересно, на самом деле Оракл и выполняет эти запросы в виде 2. Это видно из плана выполнения. Подозреваю, что и в других СУБД с адекватным оптимизатором будет происходить тоже самое.
Как вы думаете, почему оракл делает так и как испортить это?
Что интересно, на самом деле Оракл и выполняет эти запросы в виде 2. Это видно из плана выполнения. Подозреваю, что и в других СУБД с адекватным оптимизатором будет происходить тоже самое.
Как вы думаете, почему оракл делает так и как испортить это?
С точки зрения полнотекстовых чуть сложнее, ибо придётся их либо раскрыть, либо попарсить json
Давайте что-ли перед переходом к выводам по cloud tag посмотрим что же было популярно в те года.
Каковы ваши ставки – какие теги будут на первых трёх местах?
Итак, с небольшим отрывом лидирует C#!
Ожидаемо по тегам реляционные модели быстрее чем полнотекстовые
Итак, подытожим. Если вам надо искать в реляционной базе медленно, но вы можете смириться с медленной вставкой, то ваш выбор – полнотекстовые индексы, но будьте готовы к их глюкам. Хотите жизни +- без проблем старое доброе денормализованное решение – чёткий середнячёк.
Казалось бы, пора завершить, но где же рассказ как сделано у нас и о том, как же надо?
Итак, расскажу как сделан поиск в одном из наших программных продуктов. Это архитектура система полуавтоматизированной обработки финансовой документации. Архитектура системы стандартна для Enterprise. Клиент, балансировщик, 2 сервера приложений для расчёта бизнес-логики, база данных Oracle и её резервный экземляр.
Мы очень хотели дополнить архитектуру сервером полнотекстового поиска, но Заказчик отказался, поэтому было принято делать решение по поиску на стороне базы данных.
Итак, какие у нас сейчас цифры. Посмотрим на них уже в знакомом формате.
Обратите внимание, если на stackoverflow мы за 4 года набрали 3 миллиона, то мы за 2 года набрали 3, т.е. Интенсивность тегирования у нас где-то в 2 раза больше. Важно понимать, что в нашей системе у нас гибрид между таксономией и фолксономией. При создании объектов создаётся ряд тегов в зависимости от типа объекта, а далее пользователи могу его тегировать сами исходя из бизнес-ситуаций либо произвольными тегами, либо набором значений.
Если обобщить все показатели у нас где-то в 3 раза больше чем у StackOverflow
В целом, система в части теггинга устроена архитектурно очевидно. Есть веб-сервис, которым пользуются другие сервисы, BPM система и собственно. Всего у нас в системе около 40 сервисов, при этом 5 из них пользуются теггинг сервисом в части поиска и наверное 10 в части вставки.
В целом все сервисы кроме UI создают очень простые запросы к теггинг сервису. В основном это выборки по условию AND, где задействовано 3-5 тегов и они работают очень быстро в целом не создавая никаких проблем.
Таких простых запросов у нас где-то 3000 в секунду, в то время как 7000 это запросы от UI.
Как я уже говорил ранее UI большей частью определяет архитектуру решения по теггинг сёрчу. Собственно все тонкие штуки, которые мы использовали появились из-за постоянного усложнения UI хотелками пользователей. К сожалению, я не могу показать скриншот из реального приложения по понятным причинам, но сейчас интерфейс схематически выглядит примерно так.
Собственно Search template применяюся по-умолчанию к любому поиску, в текстовое поле можно вбить текст, по вхождению которого фильтруются теги. Также в верхней панели можно выбрать набор пользователей, которые меняли теги и набрать теги, которые если присутствуют в объекте, но объекты надо исключить.
Слева находится собственно окно фацентной навигации
Посмотрим как эволюционировала база данных, чтобы поддержать такой интерфейс
Мы изначала думали, что пойдём как нормальные люди с хранением тегов в JSON, их парсингом и полнотекстовым поиском как в модели 4, поэтому первый этап решения был такой.
В целом оно проработало в таком режиме 2 месяца и никаких проблем не было. Однако, через 2 месяца бизнес сказал, что хочет иметь типизированые тэги
потому что столкнулся с проблемами неправильных именований и удобством использования.
Казалось бы в чем проблема, но начались проблемы, связанные с полнотекстовыми индексами по JSON в Oracle 12.1
При дополнительной фильтрации по типам не всегда стал учитываться индекс
Соответственно мы добавили таблицу типов и продолжили хранить джейсон, ибо было много типов тегов, где даже в рамках одного типа использовался набор тегов
Запросы со стороны клиентского приложения становились более сложными и разработчики попросили сделать view, в котором теги были бы уже распаршенены, так как возможностей ораклового полнотекстового индекса в 12.1 не хватало. В целом, индексы продолжали работать, но уже на 20% запросов не подхватывались.
Скажем так, на оракл 12.2 таких бы проблем не было, но возможности обновить версию субд не было.
Собственно чтобы решить проблему того, что запросы отпрабатывают по секунде мы решили отложить их оптимизацию на более поздний срок, но статистику по faceted navigation пересчитывать асинхронно после того, как пользователь выбрал фацеты для поиска, а не одновременно с поиском. Как результат через 1 секунду появляются результаты поиска, а новые цифры с учётом выбранного появляются секунды через 3.
Мы обратили внимание, что началась снижаться скорость вставки и иногда стали подтормаживать запросы. В целом скорости хватало, пока деградация по вставке не достигла 20% и очень часто стал медленно работать сам теггинг сёрч.
Заглянув в базу данных мы увидели набор функционально-ориентированных индексов, причём ряд из них был ещё и Bitmap-индексами, которые весьма драматично влияют на вставку в OLTP-системах.
Эти индексы были построены для выпаршивания определённых тэгов и аналитки по ним, причём запросы создавал Looker более чем сложные
Вскрылось, что на тэгах делается множество отчётов BI-командой
Поэтому мы сделали для аналитиков денормализованные датамарты в нашем хранилище, в который данные перемещали etl-джобами
А потом наши пользователи захотели смотреть историю по тегам, причем распределение сейчас таково, что 20% тегов неактивно
Собственно появилсь поля для хранения истории, что замедлило поиск с учётом того, что 20% лишнее.
В очередной раз к нам пришли пользователи и им понадобился набор функционала, на котором текущее решение стало весьма медленным.
Такие вещи как поиск по тегам с OR, возможность исключения объектов при наличии определённых тегов, добавление к условиям поиска по тегам «последнего изменившего» пользователя, фацетная навигация и пейджинг роняли производительность где-то процентов на 40. Если вы помните запросы к моделям данных, то запрос на поиск по тегам занимал на данном этапе около листа А4.
Наступало время релиза и было принято решение денормализовать таблицу тегов к варианту 2, который мы рассматривали в самом начале нашей презентации, чтобы исключить влияние парсинга джейсона и боли с полнотекстовыми индексами в версии 12.1. Так как джаву времени не оставалось менять в части вставки, то сделали триггер, поменяли view , снесли полнотекстовые индексы и исключили специфичный код для учёта их нюансов.
Ну и как видно появилась таблица со списком фацетов
Что важно – в таблице тегов для поиска лежат только текущие активные теги без истории
Собственно после релиза мы сделали следующее – убрали триггер и сделали денормализацию на application server-е,
Убрали забосы к вьюхе и написали выделенные запросы прямо на таблицу, а главное, проанализировали по каким комбинациям тегов чаще всего идёт поиск и также их денормализовали. По 2 всё же пришлось построить полнотекстовый индекс, но в нём отсутствовали проблемы характерные для JSON индексов, поэтому он проблем нам не доставляет.
Хотелось бы ещё остановится на индексе, игры с которыми нам тоже дали прирост в скорости.
Основной индекс выглядит и занимает примерно 4 гигабайта. Вроде бы немного и этим можно принебречь, но попробуем не только уменьшить размер индекса, но и получить некий прирост производительности. Для этого используем компрессию индексов.
Суть компрессированого индекса состоит в том, что для повторяющихся значений составных полей создаётся префиксная таблица, а в блоке данных хранятся указатели на данную таблицу. Из-за этого индекс занимает меньше места, меньше уходит операций ввода-вывода, но растёт нагрузка на CPU и незначительно уменьшается скорость вставки.
Compress 2 говорит о том, что для полей object_type_id и tag_type_id будет создана префиксная таблица, а так как эти поля совпадают у множества строк, то мы получим серьёзную экономию
В цифрах мы получили серьёзную экономию места, но главное поиск ускорился на 6% фактически забесплатно.
Если суммировать, то проблемы были решены через асинхронный пользовательский интерфейс, денормализованную модель в реляционной бд, обычные индексы с префиксами, чтобы занимать меньше места и полнотекстовые индексы только для поиска по 2-м тегам. Когда идёт совместный поиск Oracle делает порой весьма эффективную операцию index combine и запросы весьма эффективны.
Как бы я оценил наше решение? Ну наверное так – ездит на самом деле быстро, но требует усилий, чтобы не упасть.
Собственно у нас много планов на будущее.
Например, в последней версии Оракла 18с появился встроенный в их полнотекстовый сервер функционал фацетной навигации. Не знаю пока, насколько он быстр и безопасен при реальных объёмах, но мы обязательно его изучим.
Он позволяет указать, в каких тегах лежат данные по каким фацетам и осуществлять поиск по ним с возвратом агрегатов. На малых объемах это работает весьма резво, на больших руки не дошли проверить.
Вначале создаем объект для хранения фацетов,
Пото задаем правила извлечения фацетов из текста и их типы данных,
а потом по каким будет идти поиск в соответствии со сценарием
Практически уверен, что Postgres, который очень любит копировать, воплотит это скоро.
Далее есть 2 варианта
Использовать гибкую структуру с тегами и предыдущих настроек будет достаточно чтобы создать индекс на основе фацетов, указанных на предыдущей странице
Видите, поля в xml совпадают с настройками фацетов
Либо испозовать плоскую таблицу с небольшими ухищрениями, где мы указываем, что поля для фацетов лежат в реляционной структуре, ну а после этого создаём индекс как обычнос использованием маппинга
Собственно выполняем поиск в нашем индексе по ключевому слову, указав, какие нам нужны фацеты на выходе
Ну и собственно обратите внимание, что для каждой группы можно задавать свои параметры сортировки и прочего
И получаем наши фацеты с агрегатами с xml в соответствии с заданными ранее параметрами.
Выглядит весьма многообещающе, но на реальных объемах данных, с учётом постоянной вставки и изменения данных в таблицы, а также что пользователей много я ещё не проверял.
Ситуация может радикально поменяться.
Встроенный в Oracle фацетный поиск выглядит круто, но возможно и не взлетит, а у нас пока что нет времени, на переход на красивые и правильные архитектуры, тем не менее хочется быть всё быстрее. Итак, на самом деле мы уже почти на максимальном уровне того, что можно вытащить из текущей архитектуры.
Сейчас мы находимся на этапе проверки как же текущую модель данных с минимальными переделками может ускорить Oracle In-Memory и в целом у нас это получается
В целом у нас получилось ценой финансовых вливаний в лицензии и где-то недели времени работы получить ускорение где-то в 4 раза и новые цифры теперь выглядят так
В целом я буду рассказывать про то, как же нам это удалось на In-Memory Summit в этом году и если приложу усилия, то и на Highload++, так как уже сейчас предложенный доклад вызвал живой интерес.
Исходя из нашего опыта мы делаем вывод, что полнотекстовые сервера это хорошо и правильно, но они не должны быть расположены в базе данных, так как возникали проблемы с их производительностью, более медленной реакцией саппорта на баги и линейной деградацией при увеличении тегов в запросах.
Рассмотрим поиск по тегам с использованием Apache Solr. Почему Солр, а не эластик? Ну я просто его люблю и в нём есть встроенный язык для фацентной навигации
Мы будем испольовать режим работы солра со схемой и будем работать с данными, хранимыми следующим образом в файле magaged-schema.xml
Сообзазно указанной ранее схеме tags это массив из строк тегов. Я не стал его делать просто текстом, чтобы не усложнять. Фактически на solr мы делаем модель со сложными типами данных
Я взял те же самые тестовые данные, что и в примере с реляционными базами данных, но занизил память и процессоры в 2 раза
И проверим в Солре наши ключевые запросы
Поиск по идентификатору
Поиск по одному тегу с сортировкой, фильтрацией и пейджингом. Обратите внимание, что время подскачило весьма ощутимо, но в целом очень приемлемо, особенно с учётом того, что есть сортировка
Убедимся, что добавление нескольких тегов не приводит к деградации производительности, что очень существенно
Ну и в итоге попробуем создать облако тегов с использованием упрощённого варианта языка фацетов
Для фацетов уже нет UI, поэтому запрос я собрал в адресной строке. В целом, в данном синтаксисе можно уже добавить группы по категориям аналогично как мы делали в Oracle и facets будут считаться по ним.
Что важно, что время всё равно очень быстрое
Кстати, вам не кажется, что что-то странное произошло с облаком?
Да, си шарп превратился в си, потому что надо так настроить токенайзер, чтобы он его не обрезал шарп
Так как синтаксис фацетов очень обширен, то сейчас запросы для фацетной нафигации принято делать в JSON, который SOLR прекрасно обрабатывает.
Например, интервальные фацеты по 2 категориям в старом синтаксисе были конечно понятливы, но в новом гораздо более структурированы.
В общем то это показывает, что решения на базе полнотекстовых серверов являются оптимальными для поиска по тегам и фацетной навигации как по численным показателям, но и по выразительности запросов
Кстати, хочу заметить, что для примера со StackOverflow я интереса ради разворачивал кластер из 2-х нод с равномерным распределением коллекции по кластеру. В целом радикально быстрее не стало, разница была около 10%, скорее всего он себя проявит в многопользовательском режиме.
В целом, можно найти много примеров очень продвинутых архитектур для фацетного поиска, но сложные архитектуры чаще всего диктуются предметной областью и требуют отдельного доклада.
Не верьте в универсальные решения – померьте скорость на ваших тегах и на ваших сценариях, всё может быть по-иному
Верьте в вашу систему базы данных, неважно реляционная она или полнотекстовая – использование уникальных возможностей позволит съэкономить кучу времени на разработку
Придётся разобраться, как фишки работают изнутри. Составные типы данных в оракле и как в Солре Си шарп превратился в Си превосходные примеры для этого.
Выберите лучшую модель от того, что у вас есть
Если у вас не будет нагрузки StackOverflow и Instagram, а сценарий использования как у них – не тратьте время, решения на базе реляционной базы данных более чем эффективно работают
Если у вас есть сложные запросы, особенно от пользователей или фацетная навигация используйте полнотекстовые решения. На самом деле, я видел для фацетной навигации и самостоятельные решения на том же in memory grid oracle coherence, но мне они кажутся переусложнёнными
7. Поиск не равен аналитике