Este tópico demonstra como avaliar o desempenho de busca vetorial no ApsaraDB RDS for PostgreSQL usando índices Hierarchical Navigable Small Worlds (HNSW). O teste utiliza a ferramenta ANN-Benchmarks para medir a taxa de recall, consultas por segundo (QPS) e tempo de criação do índice em diferentes combinações de parâmetros. Por padrão, o ANN-Benchmarks testa o desempenho single-threaded. Para testes de desempenho com concorrência, consulte Use the pgvector extension to test performance based on IVF indexes.
Ambiente de teste
Coloque a instância RDS e a instância Elastic Compute Service (ECS) na mesma Virtual Private Cloud (VPC) e vSwitch para evitar variações nos resultados causadas pela rede.
|
Componente |
Especificação |
|
Instância RDS |
PostgreSQL 16, RDS High-availability Edition, pg.x8.2xlarge.2c (16 núcleos, 128 GB de memória), pgvector 0.8.0 |
|
Instância ECS |
ecs.c6.xlarge (4 núcleos, 8 GiB de memória), Alibaba Cloud Linux 3 |
|
Ferramenta de teste |
ANN-Benchmarks (single-threaded por padrão) |
Pré-requisitos
Antes de começar, verifique se você possui:
Uma instância RDS for PostgreSQL com uma conta privilegiada chamada
ann_testusere um banco de dados chamadoann_testdb. Consulte Create a database and an accountA extensão pgvector (chamada
vectorno sistema) instalada emann_testdb. Consulte Manage extensionsDocker instalado na instância ECS. Consulte Install Docker
Configure a instância ECS
Execute os comandos abaixo na instância ECS para baixar o ANN-Benchmarks e criar um ambiente Python:
# Download ANN-Benchmarks
cd ~
git clone https://github.com/erikbern/ann-benchmarks.git
# Install Conda and create the test environment
yum install git
yum install conda
conda create -n ann_test python=3.10.6
conda init bash
source /usr/etc/profile.d/conda.sh
conda activate ann_test
# Install dependencies
cd ~/ann-benchmarks/
pip install -r requirements.txt
Todas as etapas subsequentes são executadas dentro do ambiente virtual ann_test. Caso sua sessão expire devido a timeout, execute conda activate ann_test para reativar o ambiente.
Execute o benchmark
Etapa 1: Configure a conexão com o RDS
Adicione as configurações abaixo ao final do arquivo ~/ann-benchmarks/ann_benchmarks/algorithms/pgvector/module.py, substituindo os valores de exemplo pelos detalhes reais da sua conexão RDS:
# RDS instance connection settings
os.environ['ANN_BENCHMARKS_PG_USER'] = 'ann_testuser'
os.environ['ANN_BENCHMARKS_PG_PASSWORD'] = 'testPawword' # Replace with your password
os.environ['ANN_BENCHMARKS_PG_DBNAME'] = 'ann_testdb'
os.environ['ANN_BENCHMARKS_PG_HOST'] = 'pgm-****.pg.rds.aliyuncs.com' # Replace with your internal endpoint
os.environ['ANN_BENCHMARKS_PG_PORT'] = '5432'
os.environ['ANN_BENCHMARKS_PG_START_SERVICE'] = 'false' # Disable automatic startup
Etapa 2: Configure os parâmetros de teste HNSW
Edite o arquivo ~/ann-benchmarks/ann_benchmarks/algorithms/pgvector/config.yml para definir as combinações de parâmetros a serem testadas. Este teste abrange três grupos: M-16(100), M-16(200) e M-24(200).
float:
any:
- base_args: ['@metric']
constructor: PGVector
disabled: false
docker_tag: ann-benchmarks-pgvector
module: ann_benchmarks.algorithms.pgvector
name: pgvector
run_groups:
M-16(100):
arg_groups: [{M: 16, efConstruction: 100}]
args: {}
query_args: [[10, 20, 40, 80, 120, 200, 400, 800]]
M-16(200):
arg_groups: [{M: 16, efConstruction: 200}]
args: {}
query_args: [[10, 20, 40, 80, 120, 200, 400, 800]]
M-24(200):
arg_groups: [{M: 24, efConstruction: 200}]
args: {}
query_args: [[10, 20, 40, 80, 120, 200, 400, 800]]
Descrição dos parâmetros:
|
Parâmetro |
Descrição |
|
|
Número máximo de nós vizinhos por nó em cada camada do grafo. Valores maiores geram um grafo mais denso, aumentando a taxa de recall e o tempo de criação do índice. |
|
|
Tamanho do conjunto de candidatos durante a construção do índice. Valores maiores melhoram a taxa de recall, mas aumentam o tempo de criação. |
|
|
Tamanho do conjunto de candidatos durante as consultas. Valores maiores melhoram a taxa de recall e aumentam a latência da consulta. |
Diretrizes de ajuste:
Para aumentar a taxa de recall: eleve
M,efConstructionouef_search.Para reduzir a latência da consulta: diminua
ef_search.Para diminuir o tempo de criação do índice: reduza
efConstructionou aumente o número de workers paralelos (consulte Apêndice: Impacto dos parâmetros no tempo de criação do índice).Os valores padrão (
M=16,ef_construction=64,ef_search=40) frequentemente resultam em recall subótimo. Ajuste esses valores conforme suas necessidades específicas.
Etapa 3: Crie a imagem Docker de teste
-
(Opcional) Modifique o arquivo
~/ann-benchmarks/ann_benchmarks/algorithms/pgvector/Dockerfilepara ignorar a configuração interna do PostgreSQL e usar apenas os pacotes psycopg e pgvector:FROM ann-benchmarks USER root RUN pip install psycopg[binary] pgvector -
Crie a imagem Docker:
cd ~/ann-benchmarks/ python install.py --algorithm pgvectorExecute
python install.py --helppara visualizar todas as opções disponíveis.
Etapa 4: Baixe o conjunto de dados
Ao executar o script de teste, o ANN-Benchmarks baixa automaticamente o conjunto de dados especificado. Este teste utiliza o dataset nytimes-256-angular. Para detalhes sobre os datasets disponíveis, consulte Conjuntos de dados do ANN-Benchmarks.
|
Dataset |
Dimensões |
Linhas |
Vetores de teste |
Vizinhos mais próximos |
Distância |
|
NYTimes |
256 |
290.000 |
10.000 |
100 |
Angular |
Se os datasets públicos não corresponderem à sua carga de trabalho, converta seus dados vetoriais para o formato Hierarchical Data Format version 5 (HDF5) e utilize-os como um dataset personalizado. Consulte Custom datasets.
Etapa 5: Execute o teste e colete os resultados
-
Execute o benchmark:
Parâmetro
Descrição
--datasetDataset a ser testado
-kValor LIMIT na consulta SQL — número de resultados a retornar
--algorithmAlgoritmo para benchmark (
pgvectorneste teste)--runsNúmero de execuções; o melhor conjunto de resultados é selecionado
--parallelismNúmero de workers paralelos (padrão: 1)
cd ~/ann-benchmarks nohup python run.py --dataset nytimes-256-angular -k 10 --algorithm pgvector --runs 1 > ann_benchmark_test.log 2>&1 & tail -f ann_benchmark_test.log -
Gere os gráficos de resultados:
cd ~/ann-benchmarks python plot.py --dataset nytimes-256-angular --recompute -
(Opcional) Exporte resultados detalhados incluindo taxa de recall, QPS, tempo de resposta (RT), tempo de criação do índice e tamanho do índice:
cd ~/ann-benchmarks python data_export.py --out res.csv
Resultados dos testes
Taxa de recall e QPS
A tabela a seguir apresenta os resultados obtidos com o dataset nytimes-256-angular. Todos os três grupos utilizam os mesmos valores de query_args (ef_search = 10, 20, 40, 80, 120, 200, 400, 800).
| m | ef_construction | ef_search | Taxa de recall | QPS |
|---|---|---|---|---|
| 16 | 100 | 10 | 0,630 | 1.423,985 |
| 20 | 0,741 | 1.131,941 | ||
| 40 | 0,820 | 836,017 | ||
| 80 | 0,871 | 574,733 | ||
| 120 | 0,894 | 440,076 | ||
| 200 | 0,918 | 297,267 | ||
| 400 | 0,945 | 162,759 | ||
| 800 | 0,969 | 84,268 | ||
| 16 | 200 | 10 | 0,683 | 1.299,667 |
| 20 | 0,781 | 1.094,968 | ||
| 40 | 0,849 | 790,838 | ||
| 80 | 0,895 | 533,826 | ||
| 120 | 0,914 | 405,975 | ||
| 200 | 0,933 | 272,591 | ||
| 400 | 0,956 | 148,688 | ||
| 800 | 0,977 | 76,555 | ||
| 24 | 200 | 10 | 0,767 | 1.182,747 |
| 20 | 0,840 | 922,770 | ||
| 40 | 0,887 | 639,899 | ||
| 80 | 0,920 | 411,140 | ||
| 120 | 0,936 | 303,323 | ||
| 200 | 0,953 | 199,752 | ||
| 400 | 0,973 | 105,506 | ||
| 800 | 0,988 | 53,904 |
Tempo de criação do índice
|
m |
ef_construction |
Tempo de criação (segundos) |
|
16 |
100 |
33,35 |
|
16 |
200 |
57,66 |
|
24 |
200 |
87,23 |
Conclusões
O aumento de m, efConstruction e ef_search melhora consistentemente a taxa de recall, porém reduz o QPS e aumenta o tempo de criação. Especificamente:
Elevar
ef_searchaumenta a taxa de recall, mas reduz o QPS.Elevar
meefConstructionaumenta a taxa de recall, reduz o QPS e prolonga o tempo de criação.Se sua aplicação exige alto recall, evite os valores padrão dos parâmetros (
m=16,ef_construction=64,ef_search=40), pois são otimizados para velocidade em vez de precisão.
Apêndice: Impacto dos parâmetros no tempo de criação do índice
Efeito de maintenance_work_mem
O parâmetro maintenance_work_mem define a memória máxima para operações de manutenção, como VACUUM e CREATE INDEX (unidade: KB). Aumentar esse valor reduz o tempo de criação, mas apenas até atingir o tamanho do dataset. Quando maintenance_work_mem excede o tamanho do dataset, o tempo de criação para de diminuir.
Os resultados abaixo foram obtidos com o tipo de instância pg.x8.2xlarge.2c (16 núcleos, 128 GB de memória), max_parallel_maintenance_workers=8 e o dataset nytimes-256-angular (~324 MB).
|
maintenance_work_mem |
Tempo de criação (segundos) |
|
64 MB (65.536 KB) |
52,82 |
|
128 MB (131.072 KB) |
46,79 |
|
256 MB (262.144 KB) |
36,40 |
|
512 MB (524.288 KB) |
18,90 |
|
1 GB (1.048.576 KB) |
19,06 |
O tempo de criação estabiliza entre 512 MB e 1 GB porque ambos os valores excedem o tamanho do dataset de ~324 MB.
Efeito de max_parallel_maintenance_workers
O parâmetro max_parallel_maintenance_workers controla o número de workers paralelos para uma única operação CREATE INDEX. O tempo de criação diminui à medida que esse valor aumenta.
Os resultados abaixo utilizam o mesmo tipo de instância, maintenance_work_mem=2048 (2 GB) e o dataset nytimes-256-angular.
|
max_parallel_maintenance_workers |
Tempo de criação (segundos) |
|
1 |
76,00 |
|
2 |
51,34 |
|
4 |
32,49 |
|
8 |
19,66 |
|
12 |
14,44 |
|
16 |
13,07 |
|
24 |
13,15 |
Efeito das dimensões do vetor
Os resultados abaixo utilizam o dataset GloVe (1.183.514 linhas), m=16, efConstruction=64 e ef_search=40, com maintenance_work_mem=8 GB e max_parallel_maintenance_workers=16.
À medida que a dimensão do vetor aumenta, o tempo de criação do índice aumenta, a taxa de recall diminui, o QPS cai e a latência da consulta sobe.
|
Dimensão |
Tempo de criação (segundos) |
Taxa de recall |
QPS |
P99 (ms) |
|
25 |
195,10 |
0,99985 |
192,94 |
7,84 |
|
50 |
236,92 |
0,99647 |
152,36 |
9,69 |
|
100 |
319,36 |
0,97231 |
126,89 |
11,14 |
|
200 |
529,33 |
0,93186 |
95,05 |
15,11 |
P99 representa a latência do percentil 99 — 99% de todas as requisições de consulta são concluídas dentro desse tempo.
Efeito do tamanho do dataset
Os resultados abaixo utilizam o dataset dbpedia-openai-{n}k-angular com m=48, efConstruction=256 e ef_search=200.
Conforme o número de linhas aumenta, o tempo de criação do índice cresce de forma não linear, a taxa de recall diminui, o QPS cai e a latência da consulta sobe.
|
Tamanho do dataset |
Linhas (dezenas de milhar) |
Tempo de criação (segundos) |
Taxa de recall |
QPS |
P99 (ms) |
|
100k |
10 |
54,05 |
0,9993 |
171,74 |
8,93 |
|
200k |
20 |
137,23 |
0,99901 |
146,78 |
10,81 |
|
500k |
50 |
436,68 |
0,999 |
118,55 |
13,94 |
|
1.000k |
100 |
957,26 |
0,99879 |
101,60 |
16,35 |
Datasets personalizados
Caso os datasets públicos não representem sua carga de trabalho, gere um dataset HDF5 personalizado a partir dos seus próprios dados vetoriais.
Este exemplo requer a extensão rds_ai. Para instalação, consulte Use the AI capabilities provided by the rds_ai extension .
-
Execute o script abaixo para exportar seus dados vetoriais e os vizinhos ground-truth para um arquivo HDF5:
import h5py import numpy as np import psycopg2 import pgvector.psycopg2 conn_info = { 'host': 'pgm-****.rds.aliyuncs.com', 'user': 'ann_testuser', 'password': '****', 'port': '5432', 'dbname': 'ann_testdb' } embedding_len = 1024 distance_top_n = 100 query_batch_size = 100 try: with psycopg2.connect(**conn_info) as connection: pgvector.psycopg2.register_vector(connection) with connection.cursor() as cur: # Fetch training vectors cur.execute("select count(1) from test_rag") count = cur.fetchone()[0] train_embeddings = [] for start in range(0, count, query_batch_size): query = f"SELECT embedding FROM test_rag ORDER BY id OFFSET {start} LIMIT {query_batch_size}" cur.execute(query) res = [embedding[0] for embedding in cur.fetchall()] train_embeddings.extend(res) train = np.array(train_embeddings) # Generate query embeddings using rds_ai with open('query.txt', 'r', encoding='utf-8') as file: queries = [query.strip() for query in file] test = [] for query in queries: cur.execute(f"SELECT rds_ai.embed('{query.strip()}')::vector(1024)") test.extend([cur.fetchone()[0]]) test = np.array(test) # Compute ground-truth nearest neighbors using angular distance dot_product = np.dot(test, train.T) norm_test = np.linalg.norm(test, axis=1, keepdims=True) norm_train = np.linalg.norm(train, axis=1, keepdims=True) similarity = dot_product / (norm_test * norm_train.T) distance_matrix = 1 - similarity neighbors = np.argsort(distance_matrix, axis=1)[:, :distance_top_n] distances = np.take_along_axis(distance_matrix, neighbors, axis=1) # Save to HDF5 with h5py.File('custom_dataset.hdf5', 'w') as f: f.create_dataset('distances', data=distances) f.create_dataset('neighbors', data=neighbors) f.create_dataset('test', data=test) f.create_dataset('train', data=train) f.attrs.update({ "type": "dense", "distance": "angular", "dimension": embedding_len, "point_type": "float" }) print("The HDF5 file is created and the dataset is added.") except (Exception, psycopg2.DatabaseError) as error: print(f"Error: {error}") -
Registre o dataset personalizado na seção
DATASETSdo arquivo~/ann-benchmarks/ann_benchmarks/datasets.py:DATASETS: Dict[str, Callable[[str], None]] = { ......, "<custom_dataset>": None, } Faça upload do arquivo
custom_dataset.hdf5para o diretório~/ann-benchmarkse passe seu nome para o scriptrun.pyusando o parâmetro--dataset <custom_dataset>.
Próximos passos
Use the pgvector extension to test performance based on IVF indexes — teste o desempenho de concorrência usando índices IVF (Inverted File)
Use the AI capabilities provided by the rds_ai extension — gere embeddings diretamente no banco de dados