BITMAP
Битовая индексация — это эффективный метод индексирования, предназначенный для повышения производительности запросов по скалярным полям с низкой кардинальностью. Кардинальность — это количество различных значений в поле. Поля с небольшим количеством различных элементов считаются полями с низкой кардинальностью.
Этот тип индекса помогает сократить время выполнения скалярных запросов за счёт представления значений полей в компактном двоичном формате и выполнения над ними эффективных побитовых операций. По сравнению с другими типами индексов битовые индексы, как правило, обеспечивают более высокую эффективность использования пространства и более высокую скорость выполнения запросов при работе с полями с низкой кардинальностью.
Обзор
Термин «битовая карта» (Bitmap) объединяет два слова: «бит» (Bit) и «карта» (Map). Бит представляет собой наименьшую единицу данных в компьютере, которая может принимать только значение 0 или 1. Карта в данном контексте означает процесс преобразования и организации данных в соответствии с тем, какое значение следует присвоить 0 и 1.
Битовый индекс состоит из двух основных компонентов: битовых карт и ключей. Ключи представляют собой уникальные значения в индексируемом поле. Для каждого уникального значения существует соответствующая битовая карта. Длина этих битовых карт равна количеству записей в коллекции. Каждый бит в растровой карте соответствует одной записи в коллекции. Если значение индексируемого поля в записи совпадает с ключом, соответствующий бит устанавливается в 1; в противном случае — в 0.
Рассмотрим коллекцию документов с полями «Категория» и «Доступно для общественности». Нам нужно извлечь документы, относящиеся к категории «Технологии » и доступные для общественности. В этом случае ключами для наших растровых индексов являются «Технологии » и «Доступно для общественности».
Битовая карта
Как показано на рисунке, битовые индексы для полей «Category» и «Public» имеют следующий вид:
«Tech»: [1, 0, 1, 0, 0], что показывает, что только 1-й и 3-й документы относятся к категории «Tech ».
Public: [1, 0, 0, 1, 0], что показывает, что только первый и четвёртый документы доступны для общественности.
Чтобы найти документы, соответствующие обоим критериям, мы выполняем побитовую операцию «И» над этими двумя битовыми картами:
- «Tech» И «Public»: [1, 0, 0, 0, 0]
Полученная битовая карта [1, 0, 0, 0, 0] указывает, что только первый документ (ID 1) удовлетворяет обоим критериям. Используя битовые индексы и эффективные битовые операции, мы можем быстро сузить область поиска, избавившись от необходимости сканировать весь набор данных.
Создание битового индекса
Чтобы создать растровый индекс в Milvus, воспользуйтесь методом ` create_index() ` и установите для параметра ` index_type ` значение ` "BITMAP"`.
from pymilvus import MilvusClient
client = MilvusClient(
uri="http://localhost:19530",
)
index_params = client.prepare_index_params() # Prepare an empty IndexParams object, without having to specify any index parameters
index_params.add_index(
field_name="category", # Name of the scalar field to be indexed
index_type="BITMAP", # Type of index to be created
index_name="category_bitmap_index" # Name of the index to be created
)
client.create_index(
collection_name="my_collection", # Specify the collection name
index_params=index_params
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.index.request.CreateIndexReq;
import java.util.Collections;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
client.createIndex(CreateIndexReq.builder()
.collectionName("my_collection")
.indexParams(Collections.singletonList(IndexParam.builder()
.fieldName("category")
.indexType(IndexParam.IndexType.BITMAP)
.indexName("category_bitmap_index")
.build()))
.build());
import (
"context"
"log"
"github.com/milvus-io/milvus/client/v3/index"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "http://localhost:19530",
})
if err != nil {
log.Fatal("failed to connect to milvus server: ", err.Error())
}
_, err = cli.CreateIndex(ctx, milvusclient.NewCreateIndexOption("my_collection", "category", index.NewBitmapIndex()).WithIndexName("category_bitmap_index"))
if err != nil {
log.Fatal("failed to create index: ", err.Error())
}
use milvus::v2::prelude::*;
let config = ConnectConfig::new().uri("http://localhost:19530");
let client = ClientV2::new(&config).await?;
client
.create_index(
CreateIndexRequest::builder()
.collection_name("my_collection")
.index_param(
IndexParam::new()
.field_name("category")
.index_type(IndexType::Bitmap)
.index_name("category_bitmap_index"),
)
.build()?,
)
.await?;
#include <iostream>
#include "milvus/MilvusClientV2.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("http://localhost:19530"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
status = client->CreateIndex(milvus::CreateIndexRequest().WithCollectionName("my_collection")
.AddIndex(milvus::IndexDesc("category", "category_bitmap_index", milvus::IndexType::BITMAP)));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({ address: "http://localhost:19530" });
await client.createIndex({
collection_name: "my_collection",
field_name: "category",
index_type: "BITMAP",
index_name: "category_bitmap_index",
});
using Milvus.Client.V2;
using Milvus.Client.V2.Requests.Index;
using Milvus.Client.V2.Types;
var config = new ConnectConfig
{
Uri = "http://localhost:19530"
};
using var client = new MilvusClientV2(config);
await client.ConnectAsync();
// Prepare index building params
var indexParam = new IndexParam(
fieldName: "category",
indexType: IndexType.Bitmap,
indexName: "category_bitmap_index");
await client.CreateIndexAsync(new CreateIndexReq
{
CollectionName = "my_collection",
Indexes = [indexParam]
});
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"indexParams": [
{
"fieldName": "category",
"indexName": "category_bitmap_index",
"indexType": "BITMAP"
}
]
}'
# {
# "code": 0,
# "data": {}
# }
В этом примере мы создаем битовый индекс для поля ` category ` коллекции ` my_collection `. Метод ` add_index() ` используется для указания имени поля, типа индекса и имени индекса.
После создания растрового индекса можно использовать параметр ` filter ` в операциях запроса для выполнения скалярной фильтрации на основе индексированного поля. Это позволяет эффективно сужать результаты поиска с помощью растрового индекса. Дополнительные сведения см. в разделе «Объяснение фильтрации».
Удаление индекса
Используйте метод ` drop_index() ` для удаления существующего индекса из коллекции.
В версии 2.6.3 и более ранних версиях перед удалением скалярного индекса необходимо освободить коллекцию.
Начиная с версии 2.6.4 и более поздних, скалярный индекс можно удалить напрямую, как только он больше не нужен — нет необходимости сначала освобождать коллекцию.
client.drop_index(
collection_name="my_collection", # Name of the collection
index_name="category_bitmap_index" # Name of the index to drop
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.index.request.DropIndexReq;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
client.dropIndex(DropIndexReq.builder()
.collectionName("my_collection")
.indexName("category_bitmap_index")
.build());
import (
"context"
"log"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "http://localhost:19530",
})
if err != nil {
log.Fatal("failed to connect to milvus server: ", err.Error())
}
err = cli.DropIndex(ctx, milvusclient.NewDropIndexOption("my_collection", "category_bitmap_index"))
if err != nil {
log.Fatal("failed to drop index: ", err.Error())
}
use milvus::v2::prelude::*;
let config = ConnectConfig::new().uri("http://localhost:19530");
let client = ClientV2::new(&config).await?;
client
.drop_index(
DropIndexRequest::builder()
.collection_name("my_collection")
.index_name("category_bitmap_index")
.build()?,
)
.await?;
#include <iostream>
#include "milvus/MilvusClientV2.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("http://localhost:19530"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
status = client->DropIndex(milvus::DropIndexRequest()
.WithCollectionName("my_collection")
.WithIndexName("category_bitmap_index"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({ address: "http://localhost:19530" });
await client.dropIndex({
collection_name: "my_collection",
index_name: "category_bitmap_index",
});
using Milvus.Client.V2.Requests.Index;
await client.DropIndexAsync(new DropIndexReq
{
CollectionName = "my_collection", // Name of the collection
IndexName = "category_bitmap_index" // Name of the index to drop
});
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/drop" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"indexName": "category_bitmap_index"
}'
# {
# "code": 0,
# "data": {}
# }
Ограничения
Битовые индексы поддерживаются только для скалярных полей, не являющихся первичными ключами.
Тип данных поля должен быть одним из следующих:
BOOL,INT8,INT16,INT32,INT64,VARCHARARRAY(элементы должны быть одним из следующих:BOOL,INT8,INT16,INT32,INT64,VARCHAR)
Растровые индексы не поддерживают следующие типы данных:
FLOAT,DOUBLE: Типы с плавающей запятой несовместимы с двоичной природой растровых индексов.JSON: Типы данных JSON имеют сложную структуру, которую невозможно эффективно представить с помощью растровых индексов.
Битовые индексы не подходят для полей с высокой кардинальностью (т. е. полей с большим количеством различных значений).
Как правило, растровые индексы наиболее эффективны, когда кардинальность поля меньше 500.
Когда кардинальность превышает этот порог, преимущества растровых индексов в плане производительности снижаются, а накладные расходы на хранение становятся значительными.
Для полей с высокой кардинальностью рекомендуется рассмотреть возможность использования альтернативных методов индексирования, таких как инвертированные индексы, в зависимости от конкретного сценария использования и требований к запросам.