Riak Search

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 16:20, 30 ноября 2018.
Riak Search
fraimed
Разработчики: Basho Technologies, Inc.
Выпущена: 30 September 2011 года; 8 years ago (2011-09-30) [Источник 1]
Постоянный выпуск: 2.2.3 [Источник 2]
Состояние разработки: Active
Написана на: Erlang
Операционная система: Linux, BSD, Mac OS X, Solaris
Платформа: IA-32, x86-64
Тип ПО: NoSQL-база данных, облачное хранилище данных, система поиска
Лицензия: Apache 2.0
Веб-сайт docs.basho.com

Riak Search является интеграцией Solr (для индексирования и запросов) и Riak (для хранения и распространения). Версия Reak Search 1.0 носила кодовое название Yokozuna. Текущая версия, Reak Search 2.0, какого-либо наименования не имеет.

По своей сути является посредником между хранилищем Riak и поисковиком Apache Solr. Она сама запускает и мониторит на каждом узле кластера отдельный JVM-процесс с Solr, передаёт ему поисковые запросы и изменения в данных.

Сам Riak – это распределённая децентрализованная система хранения данных, созданная компанией Basho Technologies. Riak является масштабируемым и отказоустойчивым NoSQL-решением для хранения информации. Создан Basho под влиянием идей подсистемы хранения СУБД DynamoDB компании Amazon. Написана преимущественно на Erlang и частично на Си и JavaScript. Имеет встроенную поддержку парадигмы MapReduce. Для причинно-следственного упорядочивания изменений в данных используется механизм векторных часов.

К особенностям использования Riak Search можно отнести:

  • Схемы объясняют Solr, как индексировать поля
  • Индексы называются индексами Solr, по которым будут происходить запросы
  • Bucket-index связывает сигналы с Riak, когда требуется индексировать значения (это также включает ассоциацию типа индекса).

Данные в Riak Search

Eventually Consistent хранилища, к которым относится Riak, допускают возникновение т.н. Data Incostistent ситуаций, когда содержимое одного и того же ключа на разных репликах отличается. В зависимости от настроек, Riak может попытаться решить конфликты сам с помощью vector clocks или timestamps, или же переложить обязанность определить правильную версию значения на приложение, предоставив ему все имеющиеся версии (siblings). В реальной ситуации, если ваши документы будут редактироваться в многопользовательском режиме и реплицироваться на несколько узлов, мердж конфликтных данных может стать весьма непростой задачей. В этом случае, возможно, лучшим решением будет использование Riak Data Types (также известных как CRDT). Эта технология позволяет описать данные с помощью специальных типов, которые возьмут на себя решение задачи конвергентности данных кластера и освободят приложение от обязанностей по решению конфликтов.


Как и все объекты, хранящиеся в Riak, типам данных Riak назначаются типы контента. В отличие от других объектов Riak, это происходит автоматически. Когда вы храните, скажем, счетчик в Riak, ему автоматически присваивается тип application / riak_counter. При использовании поиска не нужно беспокоиться об этом, так как типы данных Riak автоматически индексируются на основе этих типов контента. [Источник 3]

Riak Data Types, на текущий момент, реализуют следующие пять типов CRDT:

  • flag — Битовый флаг. Доступные операции — снять, установить. Может использоваться только внутри map;
  • counter — Счётчик. Доступные операции — увеличить, уменьшить. Тоже может использоваться только внутри map;
  • register — Какое-либо значение (хранится как строка);
  • set — Множество значений. Доступные операции — добавить элемент, удалить элемент;
  • map — Контейнер для других типов. Позволяет хранить внутри себя флаги, счётчики, регистры, множества и вложенные map'ы. Доступные операции — добавить поле, удалить поле, а также операции для внутренних полей, соответствующие их типам.

Существует два типа схем, связанных с типами данных Riak:

  • Схемы верхнего уровня относятся к типам данных, которые хранятся на ключевом уровне (счетчики и наборы)
  • Встроенные схемы относятся к типам данных, вложенным внутри карт (флаги, счетчики, регистры и наборы)

Как видно из схемы поиска по умолчанию, каждый из типов данных имеет свою собственную схему по умолчанию, за исключением карт, что означает, что дефолтная схема будет автоматически индексировать типы данных на основе их назначенного типа контента. Это означает, что нет никакой дополнительной работы по индексированию типов данных Riak. Можно просто сохранить их и начать запрос, при условии, что они правильно проиндексированы. [Источник 4].

Как упоминалось выше, для карт нет схем по умолчанию. Это связано с тем, что карты являются носителями для других типов данных. Даже когда карты встроены в другие карты, все данные, которые требуется индексировать и искать, содержатся в счетчиках, наборах, регистрах и флагах.

Особенности архитектуры Riak Search

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

Riak Search использует активную антиэнтропию (AAE) для обеспечения согласованности данных между бэкэндами Riak и индексами Solr. Для реализации поиска документов по тэгам, по заголовку, по дате создания и по содержимому используется технология Riak Search под кодовым названием Yokozuna, которая по своей сути является посредником между хранилищем Riak и поисковиком Solr. Yokuzuna сама запускает и мониторит на каждом узле кластера отдельный JVM-процесс с Solr, передаёт ему поисковые запросы и изменения в данных. [Источник 5]

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

Затем необходимо создать названный индекс Solr с помощью поиска Riak. Этот индекс представляет собой набор похожих данных, с которыми вы соединяетесь для выполнения запросов. При создании индекса можно дополнительно предоставить схему. В случае, если это не будет реализовано, будет использоваться схема по умолчанию.

Установка и начальная настройка

Рассмотрен пример установки для Mac OS. Предполагается, что у пользователя установлена Java, а установка ведется через Homebrew.

Прежде всего, требуется установить Riak:

brew install riak

Исходная конфигурация будет находиться здесь: /usr/local/Cellar/riak/2.2.3/libexec/etc/riak.conf

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

echo kern.maxfiles=65536 >> /etc/sysctl.conf
echo kern.maxfilesperproc=65536 >> /etc/sysctl.conf
sudo sysctl -w kern.maxfiles=65536
sudo sysctl -w kern.maxfilesperproc=65536
echo ulimit -n 65536 65536 >> ~/.bash_profile
ulimit -n 65536 65536

Для запуска и проверки работоспособности:

Riak start
Riak-admin test

В случае успешной установки и настройки ответ должен быть примерно таким: Successfully completed 1 read/write cycle to 'riak@127.0.0.1'

Настройка безопасности Riak Search

Настройки безопасности являются новым функционалом Riak 2.0, который позволяет администратору ограничить доступ к определенным ресурсам. В случае поиска ваши варианты ограничивают администрирование схем или индексов (разрешение search.admin) определенным пользователям и ограничивают запрос (разрешение search.query) любому индексу или определенному индексу. В приведенном ниже примере показаны различные варианты. [Источник 6]

riak-admin security grant search.admin on schema to username
riak-admin security grant search.admin on index to username
riak-admin security grant search.query on index to username
riak-admin security grant search.query on index famous to username

Эти разрешения могут быть отозваны:

riak-admin security revoke search.admin on schema from username
riak-admin security revoke search.admin on index from username
riak-admin security revoke search.query on index from username
riak-admin security revoke search.query on index famous from username

Пример использования с созданием новой рабочей схемы

Рассмотрим пример использования для поиска документов по тэгам, по заголовку, по дате создания и по содержимому.

Прежде всего, необходимо создать и активировать bucket-type под названием documents-type для хранения наших документов:

riak-admin bucket-type create documents-type '{"props":{"datatype":"map"}}'
riak-admin bucket-type activate documents-type

Для того, чтобы Solr знал как индексировать документы, нам необходимо создать поисковую схему. Вообще, Riak Search имеет и дефолтную схему, которую удобно использовать во время разработки, но в данном случае рассмотрим создание новой.

Так как структура данных у нас уже определена, создадим схему сразу. В схеме нужно перечислить поля документа, для каждого из них указать тип, необходимо ли строить по нему индекс и хранить его значения, чтобы потом вернуть их в поисковой выдаче. Также в схему нужно обязательно включить служебные поля Riak Search. Следует заметить, что, при использовании Riak Data Types, к названиям полей добавляется суффикс, соответствующий их типу. Получившийся файл заменит собой дефолтную схему в нашем примере использования. Таким образом, у нас получится следующее описание: [Источник 7]

<schema name="schedule" version="1.5">
    <fields>
        <field name="title_register" type="string" indexed="true" stored="false" multiValued="fase" />
        <field name="body_register" type="text_ru" indexed="true" stored="false" multiValued="true" />
        <field name="tags_set" type="string" indexed="true" stored="false" multiValued="true" />
        <field name="created_at_register" type="tdate" indexed="true" stored="false" multiValued="false" omitNorms="true" />

        <!--Все остальные поля игнорируем-->
        <dynamicField name="*" type="ignored" />

        <!--Это обязательные поля для Riak Search-->
        <field name="_yz_id"   type="_yz_str" indexed="true" stored="true"  multiValued="false" required="true"/>
        <field name="_yz_ed"   type="_yz_str" indexed="true" stored="false" multiValued="false"/>
        <field name="_yz_pn"   type="_yz_str" indexed="true" stored="false" multiValued="false"/>
        <field name="_yz_fpn"  type="_yz_str" indexed="true" stored="false" multiValued="false"/>
        <field name="_yz_vtag" type="_yz_str" indexed="true" stored="false" multiValued="false"/>
        <field name="_yz_rk"   type="_yz_str" indexed="true" stored="true"  multiValued="false"/>
        <field name="_yz_rt"   type="_yz_str" indexed="true" stored="true"  multiValued="false"/>
        <field name="_yz_rb"   type="_yz_str" indexed="true" stored="true"  multiValued="false"/>
        <field name="_yz_err"  type="_yz_str" indexed="true" stored="false" multiValued="false"/>
    </fields>

    <uniqueKey>_yz_id</uniqueKey>

    <types>
        <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
        <fieldType name="tdate" class="solr.TrieDateField" precisionStep="6" positionIncrementGap="0" sortMissingLast="true" />

        <fieldType name="text_ru" class="solr.TextField" positionIncrementGap="100">
            <analyzer>
               <tokenizer class="solr.StandardTokenizerFactory"/>
               <filter class="solr.LowerCaseFilterFactory"/>
               <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ru.txt" format="snowball" />
               <filter class="solr.SnowballPorterFilterFactory" language="Russian"/>
               <!-- less aggressive: <filter class="solr.RussianLightStemFilterFactory"/> -->
            </analyzer>
        </fieldType>

        <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />

        <!-- YZ String: Used for non-analyzed fields -->
        <fieldType name="_yz_str" class="solr.StrField" sortMissingLast="true" />
    </types>
</schema>

Затем подключаемся к ноде с помощью клиента для Erlang. Для этого необходимо скачать клиент и запустить REPL:

git clone https://github.com/basho/riak-erlang-client.git
cd riak-erlang-client/
make
cd ..
erl -pa riak-erlang-client/ebin riak-erlang-client/deps/*/ebin

Подключение:

{ok, RiakPid} = riakc_pb_socket:start_link("127.0.0.1", 8087).
% Проверим соединение
pong = riakc_pb_socket:ping(RiakPid).

Создаём поисковую схему и индекс:

{ok, Schema} = file:read_file("docs_search_schema.xml").
ok = riakc_pb_socket:create_search_schema(RiakPid, <<"documents-schema">>, Schema).
ok = riakc_pb_socket:create_search_index(RiakPid, <<"documents-index">>, <<"documents-schema">>, []).

Создаём bucket и назначаем поисковый индекс:

ok = riakc_pb_socket:set_search_index(RiakPid, {<<"documents-type">>, <<"documents-bucket">>}, <<"documents-index">>).

Создаём новый документ:

Map = riakc_map:new().

% Заголовок документа - регистр (register)
Map1 = riakc_map:update({<<"title">>, register},
                        fun(Reg) ->
                            riakc_register:set(<<"DocumentTitle">>, Reg)
                        end,
                        Map).

% Тело документа - тоже регистр (register)
Map2 = riakc_map:update({<<"body">>, register},
                        fun(Reg) ->
                            riakc_register:set(<<"Some Document Body">>, Reg)
                        end,
                        Map1).

% Тэги - множество (set)
Map3 = riakc_map:update({<<"tags">>, set},
                        fun(Set) ->
                            Set1 = riakc_set:add_element(<<"Tag One">>, Set),
                            Set2 = riakc_set:add_element(<<"Tag Two">>, Set1),
                            Set2
                        end,
                        Map2).

% Дата создания - регистр (register)
Map4 = riakc_map:update({<<"created_at">>, register},
                        fun(Reg) ->
                            % Cледует заметить что Solr понимает только даты в формате ISO8601.
                            % https://cwiki.apache.org/confluence/display/solr/Working+with+Dates.
                            ISODateFmtStr = "~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0BZ",
                            {{Year, Month, Day}, {Hour, Min, Sec}} = calendar:universal_time(),
                            ISODate = list_to_binary(io_lib:format(ISODateFmtStr, [Year, Month, Day, Hour, Min, Sec])),

                            riakc_register:set(ISODate, Reg)
                        end,
                        Map3).

% Выполним операции по созданию нашего документа в хранилище
MapOperations = riakc_map:to_op(Map4).
ok = riakc_pb_socket:update_type(RiakPid, {<<"documents-type">>, <<"documents-bucket">>}, <<"DocumentKey">>, MapOperations).

Находим и получаем документы:

% Загрузим определения записей Riak Client.
rr("riak-erlang-client/include/riakc.hrl").

% Пример запроса по заголовку тэгу, содержимому и дате создания
{ok, Results} = riakc_pb_socket:search(RiakPid, <<"documents-index">>, <<"title_register:DocumentTitle AND tags_set:\"Tag One\" AND body_register:Some AND created_at_register:[1972-05-20T17:33:18Z TO NOW]">>).

% Получение документов
Docs = Results#search_results.docs.
lists:foldr(fun({_Index, Doc}, Acc) ->
                {_, DocumentId} = lists:keyfind(<<"_yz_rk">>, 1, Doc),
                {ok, {map, Image, _, _, _}} = riakc_pb_socket:fetch_type(RiakPid, {<<"documents-type">>, <<"documents-bucket">>}, DocumentId),
                Image
            end,
            [],
            Docs).

Удаляем документ:

riakc_pb_socket:delete(RiakPid, {<<"documents-type">>, <<"documents-bucket">>}, <<"DocumentKey">>).

% Проверим
riakc_pb_socket:search(RiakPid, <<"documents-index">>, <<"body_register:\"Содержимое\"">>).

Cсылки

Источники

  1. Basho // Блог Дата обновления: 30.01.2018. URL: http://basho.com/posts/business/riak-1-0-is-officially-released/ (дата обращения: 30.11.2018)
  2. Последняя версия Riak KV на момент написания данной статьи в соответствии с документацией Riak Дата обновления: 30.01.2018. URL: http://docs.basho.com/riak/kv (дата обращения: 30.11.2018)
  3. Basho // Официальный сайт Дата обновления: 30.01.2018. URL: http://docs.basho.com/riak/kv/2.2.3/developing/usage/searching-data-types/ (дата обращения: 30.11.2018)
  4. Basho // Дата обновления: 30.01.2018. URL: http://docs.basho.com/riak/kv/2.2.3/developing/usage/search/ (дата обращения: 30.11.2018)
  5. Basho // Официальный сайт Дата обновления: 30.01.2018. URL: http://docs.basho.com/riak/kv/2.2.3/developing/usage/search/ (дата обращения: 30.11.2018)
  6. Basho // Официальный сайт Дата обновления: 30.01.2018. URL: http://docs.basho.com/riak/kv/2.2.3/developing/usage/search/ (дата обращения: 30.11.2018)
  7. Habr // Дата обновления: 30.01.2018. URL: https://habr.com/post/265399/ (дата обращения: 30.11.2018)