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