Использование вторичных индексов в RethinkDB
Вторичные индексы — это структуры данных, которые повышают скорость многих запросов чтения за счёт небольшого увеличения занимаемого места и снижения производительности записи.
RethinkDB поддерживает различные типы вторичных индексов:
- Простые индексы, основанные на значении одного поля.
- Составные индексы, основанные на нескольких полях.
- Многомерные индексы, основанные на массивах значений.
- Индексы, основанные на произвольных выражениях.
Простые индексы
Используйте простые индексы для эффективного извлечения и упорядочивания документов по значению одного поля.
Создание
// Create a secondary index on the last_name attribute
r.table("users").indexCreate("last_name").run(conn);
// Wait for the index to be ready to use
r.table("users").indexWait("last_name").run(conn);
Запросы
// Get all users whose last name is "Smith"
r.table("users").getAll("Smith").optArg("index", "last_name").run(conn);
// Get all users whose last names are "Smith" or "Lewis"
r.table("users").getAll("Smith", "Lewis").optArg("index", "last_name").run(conn);
// Get all users whose last names are between "Smith" and "Wade"
r.table("users").between("Smith", "Wade").optArg("index", "last_name").run(conn);
// Efficiently order users by last name using an index
r.table("users").orderBy().optArg("index", "last_name").run(conn);
// For each blog post, return the post and its author using the last_name index
r.table("posts").eqJoin("author_last_name", r.table("users")).optArg("index", "last_name").zip().run(conn);
Хотите узнать больше об объединениях в RethinkDB? См. как использовать объединения для запроса один-ко-многим и многие-ко-многим отношений.
Составные индексы
Составные индексы используют массивы для эффективного извлечения документов по нескольким полям.
Создание
// Create a compound secondary index based on the first_name and last_name attributes
r.table("users").indexCreate("full_name",
row -> r.array(row.g("last_name"), row.g("first_name"))
).run(conn);
// Wait for the index to be ready to use
r.table("users").indexWait("full_name").run(conn);
Запросы
// Get all users whose full name is John Smith.
r.table("users").getAll(r.array("Smith", "John"))
.optArg("index", "full_name").run(conn);
// Get all users whose full name is between "John Smith" and "Wade Welles"
r.table("users").between(
r.array("Smith", "John"), r.array("Welles", "Wade")
).optArg("index", "full_name").run(conn);
// Get all users whose last name is Smith.
r.table("users").between(
r.array("Smith", r.minval()), r.array("Smith", r.maxval())
).optArg("index", "full_name").run(conn);
// Efficiently order users by first name and last name using an index
r.table("users").orderBy().optArg("index", "full_name").run(conn);
// For each blog post, return the post and its author using the full_name index
// (assume "author_full_name" is the name of a field in "posts")
r.table("posts").eqJoin(
"author_full_name", r.table("users")
).optArg("index", "full_name").run(conn);
Внутренне составные и простые индексы в RethinkDB являются одним типом индекса; составные индексы — это просто частный случай обычного индекса, возвращающего массив вместо одного значения.
Многомерные индексы
При использовании простых и составных индексов документ будет индексироваться максимум по одному ключу индекса: одно значение для простого индекса и набор значений для составного индекса. Несколько документов могут иметь один и тот же ключ индекса. С помощью многомерного индекса документ может быть индексирован по нескольким ключам в одном индексе. Например, у блога может быть несколько тегов, и каждый тег может относиться к нескольким статьям.
Ключи в многомерном индексе могут быть одиночными значениями, составными значениями или даже произвольными выражениями. (См. раздел ниже для получения дополнительных сведений об индексах, использующих функции.)
Создание
Предположим, что каждая запись имеет поле tags , отображающее массив тегов. Схема таблицы posts будет примерно такой:
{
"title": "...",
"content": "...",
"tags": [ <tag1>, <tag2>, ... ]
}
// Create the multi index based on the field tags
r.table("posts").indexCreate("tags").optArg("multi", true).run(conn);
// Wait for the index to be ready to use
r.table("posts").indexWait("tags").run(conn);
Запросы
// Get all posts with the tag "travel" (where the field tags contains "travel")
r.table("posts").getAll("travel").optArg("index", "tags").run(conn);
// For each tag, return the tag and the posts that have such tag
r.table("tags").eqJoin("tag", r.table("posts"))
.optArg("index", "tags").run(conn);
Обратите внимание, что запросы с getAll или between могут возвращать один и тот же документ несколько раз, если не использовать команду distinct.
Индексы на произвольных выражениях ReQL
Вы можете создать индекс на произвольном выражении, передав анонимную функцию в indexCreate.
// A different way to do a compound index
r.table("users").indexCreate("full_name2",
user -> r.add(user.g("last_name"), "_", user.g("first_name"))
).run(conn);
Функция, которую вы передаёте в indexCreate , должна быть детерминированной. На практике это означает, что вы не можете использовать функцию, содержащую подзапрос или команду r.js.
Если функция, переданная в
indexCreate, возвращает ошибку для данного документа, этот документ не будет индексирован. Ошибки для таких документов не возвращаются.
Использование многомерных индексов и произвольных выражений вместе
Вы можете создать многомерный индекс на произвольном выражении аналогичным образом, передав параметр multi в качестве последнего параметра в indexCreate.
// Create a multi index on a ReQL expression
r.table("users").indexCreate("activities",
row -> row.g("hobbies").add(row.g("sports"))
).optArg("multi", true).run(conn);
Использование многомерного индекса и функции отображения для ускорения getAll/contains
Если ваша программа часто выполняет getAll, за которым следует contains, эту операцию можно сделать более эффективной, создав составной многомерный индекс, используя функцию отображения для поля, содержащего список.
// Create the index
r.table("users").indexCreate("user_equipment",
user -> user.g("equipment").map(
equipment -> r.array(user.g("id"), equipment)
)
).optArg("multi", true).run(conn);
// Query equivalent to:
// r.table("users").getAll(1).filter(
// user -> user.g("equipment").contains("tent")
// ).run(conn);
r.table("users").getAll(r.array(1, "tent"))
.optArg("index", "user_equipment").distinct().run(conn);
// Администрирование #
// list indexes on table "users"
r.table("users").indexList().run(conn);
// drop index "last_name" on table "users"
r.table("users").indexDrop("last_name").run(conn);
// return the status of all indexes
r.table("users").indexStatus().run(conn);
// return the status of the index "last_name"
r.table("users").indexStatus("last_name").run(conn);
// return only when the index "last_name" is ready
r.table("users").indexWait("last_name").run(conn);
Изменение индексов с помощью веб-интерфейса
Веб-интерфейс поддерживает создание и удаление простых вторичных индексов. В списке таблиц щелкните по таблице users. Вы можете управлять индексами через панель вторичных индексов в представлении таблицы.
Примечания
Первичный индекс таблицы может быть использован в любом команд ReQL, использующем вторичный индекс.
Индексы (как вторичные, так и первичные) гарантированно обновляются успешными операциями записи. Если операция insert, update или delete успешна, изменение будет корректно отражено в индексе. (Подробнее об гарантиях согласованности операций записи в RethinkDB.)
Часть ключа вторичного индекса, используемая для быстрых поисков, зависит от длины первичного ключа (который должен быть 127 байтов или меньше). Длина этой части составляет 238−PK, где PK — длина первичного ключа; если длина первичного ключа, например, составляет 36-символьный GUID, это означает, что 202 символа в вторичном индексе будут значимыми. Если в таблице есть несколько записей, у которых первые 238−PK символов идентичны, производительность поиска будет резко снижена, так как RethinkDB будет вынуждена выполнить линейный поиск для нахождения правильных записей.
Вторичные индексы не будут хранить null значения или объекты. Таким образом, результаты команды, такой как:
r.table("users").indexCreate("group").run(conn);
r.table("users").orderBy().optArg("index", "group").run(conn);
могут отличаться от эквивалентной команды без индекса:
r.table("users").orderBy("group").run(conn);
если поле, которое индексируется, имеет неиндексируемые значения. Это ограничение будет убрано в будущих версиях RethinkDB. Следите за прогрессом в вопросе GitHub #1032.
В RethinkDB в настоящее время нет оптимизатора. Например, следующий запрос не будет автоматически использовать индекс:
// This query does not use a secondary index! Use getAll instead.
r.table("users").filter(r.hashMap("last_name", "Smith")).run(conn);
Вы должны явно использовать команду getAll , чтобы воспользоваться вторичными индексами.
// This query uses a secondary index.
r.table("users").getAll("Smith").optArg("index", "last_name").run(conn);
Нельзя объединять несколько команд getAll . Используйте составной индекс для эффективного извлечения документов по нескольким полям.
Команда orderBy , использующая вторичный индекс, не может быть объединена после getAll. Ее можно объединить только после команды table . Однако, можно объединить orderBy после команды between , если она использует тот же индекс.
RethinkDB не поддерживает уникальные вторичные индексы, даже для несгруппированных таблиц.
Подробнее
Просмотрите справочник API, чтобы узнать больше о командах вторичных индексов:
© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/secondary-indexes/java/