AlloyDB 中的混合搜索使用入门

1. 简介

在此 Codelab 中,您将学习如何使用(排名更新方法)RUM 扩展程序和可扩缩最近邻 (ScaNN) 索引在 AlloyDB 中执行混合搜索。本实验是专门介绍 AlloyDB AI 功能的实验合集中的一个。如需了解详情,请参阅文档中的 AlloyDB AI 页面

前提条件

  • 对 Google Cloud 控制台有基本的了解
  • 具备命令行界面和 Google Shell 方面的基本技能

学习内容

所需条件

  • Google Cloud 账号和 Google Cloud 项目
  • 网络浏览器,例如 Chrome

2. 设置和要求

项目设置

登录 Google Cloud 控制台。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

请改用个人账号,而非工作账号或学校账号。

创建 Google Cloud 项目

  1. Google Cloud 控制台的项目选择器页面上,选择或创建一个 Google Cloud 项目
  2. 确保您的 Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能

启用结算功能

您可以通过以下两种方式启用结算功能。您可以使用个人结算账号,也可以按照以下步骤兑换积分。

设置个人结算账号

如果您使用 Google Cloud 抵用金设置了结算,则可以跳过此步骤。

如需设置个人结算账号,请点击此处在 Cloud 控制台中启用结算功能

注意事项:

  • 完成本实验的 Cloud 资源费用应低于 3 美元。
  • 您可以按照本实验末尾的步骤删除资源,以避免产生更多费用。
  • 新用户符合参与 $300 USD 免费试用计划的条件。

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

Cloud Shell 是在 Google Cloud 中运行的命令行环境,预加载了必要的工具。

  1. 点击 Google Cloud 控制台顶部的激活 Cloud Shell
  2. 连接到 Cloud Shell 后,验证您的身份验证:
    gcloud auth list
    
  3. 确认您的项目已配置:
    gcloud config get project
    
  4. 如果项目未按预期设置,请进行设置:
    export PROJECT_ID=<YOUR_PROJECT_ID>
    gcloud config set project $PROJECT_ID
    

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的所有工作都可以在浏览器中完成。您无需安装任何程序。

3. 准备工作

启用 API

输出:

如需使用 AlloyDBCompute Engine网络服务Vertex AI,您需要在 Google Cloud 云项目中启用它们各自的 API。

启用 API

在 Cloud Shell 的终端中,确保项目 ID 已设置:

gcloud config set project [YOUR-PROJECT-ID]

设置环境变量 PROJECT_ID:

PROJECT_ID=$(gcloud config get-value project)

启用所有必需的 API:

gcloud services enable alloydb.googleapis.com \
                       compute.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       aiplatform.googleapis.com

预期输出

student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417
Updated property [core/project].
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project)
Your active configuration is: [cloudshell-14650]
student@cloudshell:~ (test-project-001-402417)$ 
student@cloudshell:~ (test-project-001-402417)$ gcloud services enable alloydb.googleapis.com \
                       compute.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       aiplatform.googleapis.com
Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.

API 简介

  • 借助 AlloyDB API (alloydb.googleapis.com),您可以创建、管理和扩缩 AlloyDB for PostgreSQL 集群。它提供与 PostgreSQL 兼容的全托管式数据库服务,专为要求苛刻的企业事务性和分析性工作负载而设计。
  • 借助 Compute Engine API (compute.googleapis.com),您可以创建和管理虚拟机 (VM)、永久性磁盘和网络设置。它提供运行工作负载所需的核心基础设施即服务 (IaaS) 基础,并为许多托管服务托管底层基础架构。
  • 借助 Cloud Resource Manager API (cloudresourcemanager.googleapis.com),您可以以编程方式管理 Google Cloud 云项目的元数据和配置。借助它,您可以整理资源、处理 Identity and Access Management (IAM) 政策,以及验证项目层次结构中的权限。
  • 借助 Service Networking API (servicenetworking.googleapis.com),您可以自动设置虚拟私有云 (VPC) 网络与 Google 的受管服务之间的专用连接。您必须为 AlloyDB 等服务建立专用 IP 访问通道,以便它们能够与其他资源安全地通信。
  • Vertex AI API (aiplatform.googleapis.com) 可让您的应用构建、部署和扩缩机器学习模型。它为所有 Google Cloud AI 服务提供统一的界面,包括访问生成式 AI 模型(如 Gemini)和自定义模型训练。

您可以选择配置默认区域,以使用 Vertex AI 嵌入模型。详细了解 Vertex AI 的可用位置。在本示例中,我们使用的是 us-central1 区域。

gcloud config set compute/region us-central1

4. 部署 AlloyDB

在创建 AlloyDB 集群之前,我们需要在 VPC 中分配一个可用的专用 IP 范围,以供未来的 AlloyDB 实例使用。如果我们没有该服务账号,则需要创建该服务账号,并将其分配给内部 Google 服务使用,之后我们才能创建集群和实例。

创建专用 IP 范围

我们需要在 VPC 中为 AlloyDB 配置专用服务访问配置。这里假设我们的项目中有“默认”VPC 网络,它将用于所有操作。

创建专用 IP 范围:

gcloud compute addresses create psa-range \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=24 \
    --description="VPC private service access" \
    --network=default

使用分配的 IP 范围创建专用连接:

gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=psa-range \
    --network=default

预期的控制台输出:

student@cloudshell:~ (test-project-402417)$ gcloud compute addresses create psa-range \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=24 \
    --description="VPC private service access" \
    --network=default
Created [https://www.googleapis.com/compute/v1/projects/test-project-402417/global/addresses/psa-range].

student@cloudshell:~ (test-project-402417)$ gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=psa-range \
    --network=default
Operation "operations/pssn.p24-4470404856-595e209f-19b7-4669-8a71-cbd45de8ba66" finished successfully.

student@cloudshell:~ (test-project-402417)$

创建 AlloyDB 集群

在此部分中,我们将在 us-central1 区域中创建一个 AlloyDB 集群。

为 postgres 用户定义密码。您可以自行定义密码,也可以使用随机函数生成密码

export PGPASSWORD=`openssl rand -hex 12`

预期的控制台输出:

student@cloudshell:~ (test-project-402417)$ export PGPASSWORD=`openssl rand -hex 12`

请记下该 PostgreSQL 密码,以备将来使用。

echo $PGPASSWORD

您日后需要使用该密码以 postgres 用户身份连接到实例。我建议将其写下来或复制到某个地方,以便日后使用。

预期的控制台输出:

student@cloudshell:~ (test-project-402417)$ echo $PGPASSWORD
bbefbfde7601985b0dee5723

创建 AlloyDB 集群

定义区域和 AlloyDB 集群名称。我们将使用 us-central1 区域和 alloydb-hybrid-search 作为集群名称:

export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search

运行命令以创建集群:

gcloud alloydb clusters create $ADBCLUSTER \
    --password=$PGPASSWORD \
    --network=default \
    --region=$REGION

预期的控制台输出:

export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud alloydb clusters create $ADBCLUSTER \
    --password=$PGPASSWORD \
    --network=default \
    --region=$REGION 
Operation ID: operation-1697655441138-6080235852277-9e7f04f5-2012fce4
Creating cluster...done.                                                                                                                                                                                                                                                           

在同一 Cloud Shell 会话中为集群创建 AlloyDB 主实例。如果您断开连接,则需要再次定义区域和集群名称环境变量。

gcloud alloydb instances create $ADBCLUSTER-pr \
    --instance-type=PRIMARY \
    --cpu-count=2 \
    --region=$REGION \
    --cluster=$ADBCLUSTER

预期的控制台输出:

student@cloudshell:~ (alloydb-hybrid-search)$ gcloud alloydb instances create $ADBCLUSTER-pr \
    --instance-type=PRIMARY \
    --cpu-count=2 \
    --region=$REGION \
    --availability-type ZONAL \
    --cluster=$ADBCLUSTER
Operation ID: operation-1697659203545-6080315c6e8ee-391805db-25852721
Creating instance...done.                                                                                                                                                                                                                                                     

5. 连接到 AlloyDB

AlloyDB 是使用仅限专用连接进行部署的,因此我们需要安装了 PostgreSQL 客户端的虚拟机才能使用该数据库。

部署 GCE 虚拟机

在 AlloyDB 集群所在的区域和 VPC 中创建 GCE 虚拟机。

在 Cloud Shell 中,执行以下命令:

export ZONE=us-central1-a
gcloud compute instances create instance-1 \
    --zone=$ZONE \
    --create-disk=auto-delete=yes,boot=yes,image=projects/debian-cloud/global/images/$(gcloud compute images list --filter="family=debian-12 AND family!=debian-12-arm64" --format="value(name)") \
    --scopes=https://www.googleapis.com/auth/cloud-platform

预期的控制台输出:

student@cloudshell:~ (alloydb-hybrid-search)$ export ZONE=us-central1-a
student@cloudshell:~ (talloydb-hybrid-search)$ export ZONE=us-central1-a
gcloud compute instances create instance-1 \
    --zone=$ZONE \
    --create-disk=auto-delete=yes,boot=yes,image=projects/debian-cloud/global/images/$(gcloud compute images list --filter="family=debian-12 AND family!=debian-12-arm64" --format="value(name)") \
    --scopes=https://www.googleapis.com/auth/cloud-platform

Created [https://www.googleapis.com/compute/v1/projects/test-project-402417/zones/us-central1-a/instances/instance-1].
NAME: instance-1
ZONE: us-central1-a
MACHINE_TYPE: n1-standard-1
PREEMPTIBLE: 
INTERNAL_IP: 10.128.0.2
EXTERNAL_IP: 34.71.192.233
STATUS: RUNNING

安装 Postgres 客户端

在已部署的虚拟机上安装 PostgreSQL 客户端软件

连接到虚拟机:

gcloud compute ssh instance-1 --zone=us-central1-a

预期的控制台输出:

student@cloudshell:~ (alloydb-hybrid-search)$ gcloud compute ssh instance-1 --zone=us-central1-a
Updating project ssh metadata...working..Updated [https://www.googleapis.com/compute/v1/projects/alloydb-hybrid-search].                                                                                                                                                         
Updating project ssh metadata...done.                                                                                                                                                                                                                                              
Waiting for SSH key to propagate.
Warning: Permanently added 'compute.5110295539541121102' (ECDSA) to the list of known hosts.
Linux instance-1.us-central1-a.c.gleb-test-short-001-418811.internal 6.1.0-18-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
student@instance-1:~$ 

在虚拟机内运行以下命令来安装软件:

sudo apt-get update
sudo apt-get install --yes postgresql-client

预期的控制台输出:

student@instance-1:~$ sudo apt-get update
sudo apt-get install --yes postgresql-client
Get:1 https://packages.cloud.google.com/apt google-compute-engine-bullseye-stable InRelease [5146 B]
Get:2 https://packages.cloud.google.com/apt cloud-sdk-bullseye InRelease [6406 B]   
Hit:3 https://deb.debian.org/debian bullseye InRelease  
Get:4 https://deb.debian.org/debian-security bullseye-security InRelease [48.4 kB]
Get:5 https://packages.cloud.google.com/apt google-compute-engine-bullseye-stable/main amd64 Packages [1930 B]
Get:6 https://deb.debian.org/debian bullseye-updates InRelease [44.1 kB]
Get:7 https://deb.debian.org/debian bullseye-backports InRelease [49.0 kB]
...redacted...
update-alternatives: using /usr/share/postgresql/13/man/man1/psql.1.gz to provide /usr/share/man/man1/psql.1.gz (psql.1.gz) in auto mode
Setting up postgresql-client (13+225) ...
Processing triggers for man-db (2.9.4-2) ...
Processing triggers for libc-bin (2.31-13+deb11u7) ...

连接到实例

使用 psql 从虚拟机连接到主实例。

在同一 Cloud Shell 标签页中,打开与实例 1 虚拟机的 SSH 会话。

使用记下的 AlloyDB 密码 (PGPASSWORD) 值和 AlloyDB 集群 ID 从 GCE 虚拟机连接到 AlloyDB:

export PGPASSWORD=<Noted password>
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
export INSTANCE_IP=$(gcloud alloydb instances describe $ADBCLUSTER-pr --cluster=$ADBCLUSTER --region=$REGION --format="value(ipAddress)")
psql "host=$INSTANCE_IP user=postgres sslmode=require"

预期的控制台输出:

student@instance-1:~$ export PGPASSWORD=CQhOi5OygD4ps6ty
student@instance-1:~$ ADBCLUSTER=alloydb-aip-01
student@instance-1:~$ REGION=us-central1
student@instance-1:~$ INSTANCE_IP=$(gcloud alloydb instances describe $ADBCLUSTER-pr --cluster=$ADBCLUSTER --region=$REGION --format="value(ipAddress)")
gleb@instance-1:~$ psql "host=$INSTANCE_IP user=postgres sslmode=require"
psql (15.6 (Debian 15.6-0+deb12u1), server 15.5)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

postgres=>

关闭 psql 会话:

exit

6. 准备数据库

我们需要创建数据库、启用 Vertex AI 集成、创建数据库对象并导入数据。

向 AlloyDB 授予必要权限

向 AlloyDB 服务代理添加 Vertex AI 权限。

使用顶部的“+”号打开另一个 Cloud Shell 标签页。

abc505ac4d41f24e.png

在新的 Cloud Shell 标签页中,执行以下命令:

PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
  --role="roles/aiplatform.user"

预期的控制台输出:

student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project)
Your active configuration is: [cloudshell-11039]
student@cloudshell:~ (test-project-001-402417)$ gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
  --role="roles/aiplatform.user"
Updated IAM policy for project [test-project-001-402417].
bindings:
- members:
  - serviceAccount:service-4470404856@gcp-sa-alloydb.iam.gserviceaccount.com
  role: roles/aiplatform.user
- members:
...
etag: BwYIEbe_Z3U=
version: 1
 

在标签页中执行命令“exit”,关闭该标签页:

exit

创建数据库

创建一个名为 quickstart 的数据库。

在 GCE 虚拟机会话中,执行以下命令:

创建数据库:

psql "host=$INSTANCE_IP user=postgres" -c "CREATE DATABASE quickstart_db"

预期的控制台输出:

student@instance-1:~$ psql "host=$INSTANCE_IP user=postgres" -c "CREATE DATABASE quickstart_db"
CREATE DATABASE
student@instance-1:~$  

启用 Vertex AI 集成

在数据库中启用 Vertex AI 集成和 pgvector 扩展程序。

在 GCE 虚拟机中,执行以下命令:

psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE"
psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS vector"

预期的控制台输出:

student@instance-1:~$ psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE"
psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS vector"
CREATE EXTENSION
CREATE EXTENSION
student@instance-1:~$ 

导入数据

下载准备好的数据,然后将其导入到新数据库中。

在 GCE 虚拟机中,执行以下命令:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_products from stdin csv header"
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_inventory from stdin csv header"
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_stores from stdin csv header"

预期的控制台输出:

student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"
SET
SET
SET
SET
SET
 set_config 
------------
 
(1 row)
SET
SET
SET
SET
SET
SET
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE SEQUENCE
ALTER TABLE
ALTER SEQUENCE
ALTER TABLE
ALTER TABLE
ALTER TABLE
student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_products from stdin csv header"
COPY 941
student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_inventory from stdin csv header"
COPY 263861
student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_stores from stdin csv header"
COPY 4654
student@instance-1:~$

7. 生成向量嵌入

导入数据后,我们得到以下表格:cymbal_products 用于存储商品相关信息,cymbal_inventory 用于跟踪每家商店的商品库存,以及 cymbal_stores 用于存储商店列表。为了对我们的产品执行语义搜索,我们需要使用 initialize_embeddings 函数生成产品说明的向量嵌入。我们将使用 Vertex AI 集成功能,根据产品说明计算向量数据并将其添加到表中。如需详细了解所用技术,请参阅文档

如需使用集成,请使用 AlloyDB 实例 IP 和 postgres 密码从虚拟机使用 psql 连接到数据库:

psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"

验证 google_ml_integration 扩展程序的版本。

SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';

版本应为 1.5.2 或更高版本。以下是输出示例:

quickstart_db=> SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';
 extversion 
------------
 1.5.2
(1 row)

默认版本应为 1.5.2 或更高版本,但如果您的实例显示的是旧版本,则可能需要更新。检查实例是否已停用维护。

我们将使用批量嵌入生成功能来提高效率。如需详细了解不同的嵌入生成选项和技术,请参阅此指南。如需使用批量嵌入,我们必须启用 goole_ml_integration.enable_faster_embedding_generation

show google_ml_integration.enable_faster_embedding_generation;

如果标志位于正确的位置,则预期输出如下所示:

quickstart_db=> show google_ml_integration.enable_faster_embedding_generation;                          
 google_ml_integration.enable_faster_embedding_generation 
----------------------------------------------------------
 on
(1 row)

但如果显示“关闭”,则需要更新实例。您可以使用 Web 控制台或 gcloud 命令来执行此操作,具体如文档中所述。下面展示了如何使用 gcloud 命令执行此操作:

export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud beta alloydb instances update $ADBCLUSTER-pr \
   --database-flags google_ml_integration.enable_faster_embedding_generation=on \
   --region=$REGION \
   --cluster=$ADBCLUSTER \
   --project=$PROJECT_ID \
   --update-mode=FORCE_APPLY

这可能需要几分钟时间,但最终标志值应切换为“开启”。之后,您可以继续执行后续步骤。

psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"

在连接到数据库的 psql 会话中,创建一个新列来存储嵌入 cymbal_products

ALTER TABLE cymbal_products ADD COLUMN product_embedding vector(768);

预期的控制台输出:

quickstart_db=> ALTER TABLE cymbal_products ADD COLUMN product_embedding vector(768);
ALTER TABLE
quickstart_db=> 

最后,我们还希望在列值发生更改时刷新嵌入,因此在函数调用中添加了 incremental_refresh_mode 实参。这会给我们的数据库带来开销,但我们做出了这种权衡,以便自动使嵌入与内容保持同步。如果您想手动更新嵌入内容,可以在文档中找到相关说明。

现在,我们将所有内容整合在一起并生成嵌入,使用 initialize_embeddings 函数并传递 50 的 batch_size 作为批次提示,并将 incremental_refresh_mode 设置为 transactional

CALL ai.initialize_embeddings(
    model_id => 'text-embedding-005',
    table_name => 'cymbal_products',
    content_column => 'product_description',
    embedding_column => 'product_embedding',
    batch_size => 50,
    incremental_refresh_mode => 'transactional'
);

现在,如果我们向表中插入一个新行,并为 product_embedding 列指定 NULL

INSERT INTO "cymbal_products" ("uniq_id", "crawl_timestamp", "product_url", "product_name", "product_description", "list_price", "sale_price", "brand", "item_number", "gtin", "package_size", "category", "postal_code", "available", "product_embedding") VALUES ('fd604542e04b470f9e6348e640cff794', NOW(), 'https://example.com/new_product', 'New Cymbal Product', 'This is a new cymbal product description.', 199.99, 149.99, 'Example Brand', 'EB123', '1234567890', 'Single', 'Cymbals', '12345', TRUE, NULL);

现在,当我们查询刚刚插入的行时,会看到 product_embedding 列已自动更新。

SELECT uniq_id, (product_embedding::real[])[1:5] as product_embedding  FROM cymbal_products WHERE uniq_id='fd604542e04b470f9e6348e640cff794';

输出应如下所示:

quickstart_db=> SELECT uniq_id,(product_embedding::real[])[1:5] as product_embedding  FROM cymbal_products WHERE uniq_id='fd604542e04b470f9e6348e640cff794';
             uniq_id              |                      product_embedding                       
----------------------------------+---------------------------------------------------------------
 fd604542e04b470f9e6348e640cff794 | {0.015003494,-0.005349732,-0.059790313,-0.0087091,-0.0271452}
(1 row)

Time: 3.295 ms

8. 创建向量索引

为了提升向量搜索性能,我们将添加 ScaNN 索引

创建 ScaNN 索引

如需构建 SCANN 索引,我们需要再启用一个扩展程序。扩展程序 alloydb_scann 提供了一个接口,用于使用 Google 的 ScaNN 算法处理 ANN 类型的向量索引。

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

预期输出:

quickstart_db=> CREATE EXTENSION IF NOT EXISTS alloydb_scann;
CREATE EXTENSION
Time: 27.468 ms
quickstart_db=> 

索引可以在 MANUAL 或 AUTO 模式下创建。默认情况下,系统会启用 MANUAL 模式,您可以像创建和维护任何其他索引一样创建和维护索引。不过,如果您启用 AUTO 模式,则可以创建不需要您进行任何维护的索引。您可以在文档中详细了解所有选项。在我们的示例中,没有足够的行来以 AUTO 模式创建索引,因此我们将以 MANUAL 模式创建索引并添加调优参数。您可以在该文档中了解有关调整索引参数的信息。

我们必须启用 scann.enable_preview_features 标志,以便修改调谐参数。在 Cloud Shell 中

export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud beta alloydb instances update $ADBCLUSTER-pr \
   --database-flags scann.enable_preview_features=on \
   --region=$REGION \
   --cluster=$ADBCLUSTER \
   --project=$PROJECT_ID \
   --update-mode=FORCE_APPLY

这可能需要几分钟时间,但最终标志值应切换为“开启”。设置标志后,我们可以切换回虚拟机上的 psql 会话,并使用调整参数创建索引。

CREATE INDEX cymbal_products_embeddings_scann ON cymbal_products
  USING scann (product_embedding cosine)
  WITH (mode='MANUAL', num_leaves=31, max_num_levels = 2);

预期输出:

quickstart_db=> CREATE INDEX cymbal_products_embeddings_scann ON cymbal_products
  USING scann (product_embedding cosine)
  WITH (num_leaves=31, max_num_levels = 2);
CREATE INDEX
quickstart_db=>

检查索引使用情况

现在,我们可以在 EXPLAIN 模式下运行向量搜索查询,并验证是否正在使用索引。

EXPLAIN (analyze) 
WITH trees as (
SELECT
        cp.product_name,
        left(cp.product_description,80) as description,
        cp.sale_price,
        cs.zip_code,
        cp.uniq_id as product_id
FROM
        cymbal_products cp
JOIN cymbal_inventory ci on
        ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
        cs.store_id=ci.store_id
        AND ci.inventory>0
        AND cs.store_id = 1583
ORDER BY
        (cp.product_embedding <=> embedding('text-embedding-005','What kind of fruit trees grow well here?')::vector) ASC
LIMIT 1)
SELECT json_agg(trees) FROM trees;

预期输出(为清晰起见,这里省略了部分信息):

...
Aggregate (cost=16.59..16.60 rows=1 width=32) (actual time=2.875..2.877 rows=1 loops=1)
-> Subquery Scan on trees (cost=8.42..16.59 rows=1 width=142) (actual time=2.860..2.862 rows=1 loops=1)
-> Limit (cost=8.42..16.58 rows=1 width=158) (actual time=2.855..2.856 rows=1 loops=1)
-> Nested Loop (cost=8.42..6489.19 rows=794 width=158) (actual time=2.854..2.855 rows=1 loops=1)
-> Nested Loop (cost=8.13..6466.99 rows=794 width=938) (actual time=2.742..2.743 rows=1 loops=1)
-> Index Scan using cymbal_products_embeddings_scann on cymbal_products cp (cost=7.71..111.99 rows=876 width=934) (actual time=2.724..2.724 rows=1 loops=1)
Order By: (embedding <=> '[0.008864171,0.03693164,-0.024245683,-0.00355923,0.0055611245,0.015985578,...<redacted>...5685,-0.03914233,-0.018452475,0.00826032,-0.07372604]'::vector)
...

从输出中,我们可以清楚地看到查询使用了“Index Scan using cymbal_products_embeddings_scann on cymbal_products”。

9. 全文搜索索引

AlloyDB 支持原生 PostgreSQL 支持的所有全文搜索索引类型。索引的选择取决于搜索速度、索引构建时间、更新速度以及所需的特定搜索功能(例如短语搜索或相关性排名)之间的平衡。

在我们的示例中,我们将使用 RUM 扩展程序来提高全文搜索操作的性能。RUM 通过直接在索引中存储位置信息来改进标准 GIN 索引,让您无需访问表数据即可执行更快的短语搜索和相关性排名。

您可以使用 AlloyDB Studio 或继续使用 psql 客户端来启用 rum 扩展程序

创建 RUM 索引

CREATE EXTENSION IF NOT EXISTS rum;

为了在 cymbal_products 表中搜索商品说明,我们需要创建一个将商品说明存储为 tsvector 的列。此列会自动存储处理后的文本,并提高查询性能。

ALTER TABLE cymbal_products
ADD COLUMN product_search_vector tsvector
GENERATED ALWAYS AS (to_tsvector('english', product_description)) STORED;

现在,我们可以为 product_search_vector 列创建新的 RUM 索引。

CREATE INDEX cymbal_products_rum
ON cymbal_products
USING rum (product_search_vector rum_tsvector_ops);

如需使用索引查询表,请运行以下查询来搜索“樱桃树”的匹配项。<=> 运算符直接从索引中计算文档与查询之间的相关性得分或距离。

SELECT product_name, product_description
FROM cymbal_products
WHERE product_search_vector @@ to_tsquery('english', 'cherry <-> tree')
ORDER BY product_search_vector <=> to_tsquery('english', 'cherry <-> tree');

10. 执行混合搜索

借助 google_vector_utils.hybrid_search() 函数,您可以合并多种搜索类型(例如向量搜索和全文搜索)的结果。该函数使用倒数排序融合 (RRF) 算法将每个搜索组件的排名结果融合为一个统一的列表。与单独使用一种搜索类型相比,这种方法可提供更相关的搜索结果。

hybrid_search() 函数可动态构建并执行单个 SQL 查询。它会为您定义的每个搜索组件创建一个通用表表达式 (CTE)。然后,该函数会联接所有 CTE 的结果,并计算每个文档的最终 RRF 得分,以生成统一的排名列表。

如需使用该函数,我们必须在主实例中开启 enable_preview_ai_functions。在 Cloud Shell 中运行以下命令

export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud beta alloydb instances update $ADBCLUSTER-pr \
   --database-flags google_ml_integration.enable_preview_ai_functions=on \
   --region=$REGION \
   --cluster=$ADBCLUSTER \
   --project=$PROJECT_ID \
   --update-mode=FORCE_APPLY

以下查询将之前的向量搜索问题与全文搜索问题结合在一起。这是一个非常简单的混合搜索查询;您可以尝试更复杂的查询,例如在向量搜索组件中使用“比房屋高的树”,在 FTS 组件中使用“加利福尼亚”。

SELECT score, id, p.product_name
FROM ai.hybrid_search(
  search_inputs => ARRAY[
      '{
        "data_type": "vector",
        "table_name": "cymbal_products",
        "key_column": "uniq_id",
        "vec_column": "product_embedding",
        "distance_operator": "public.<=>",
        "limit": 5,
        "query_vector": "ai.embedding(''text-embedding-005'', ''cherry'')::vector"
      }'::JSONB,
      '{
        "data_type": "text",
        "table_name": "cymbal_products",
        "key_column": "uniq_id",
        "text_column": "product_search_vector",
        "limit": 5,
        "ranking_function": "<=>",
        "query_text_input": "tree"
      }'::JSONB
  ]
) JOIN cymbal_products p ON id = p.uniq_id;

预期输出

"score","id","product_name"
"0.00819672631147241","d536e9e823296a2eba198e52dd23e712","Cherry Tree"
"0.015873015873015872","23e41a71d63d8bbc9bdfa1d118cfddc5","Apple Tree"
"0.00819672631147241","dc789a2f87b142e94e6e325689482af9","Oak Tree"
"0.008064521129029258","f5c70d62ccf3118d73863bf3b17edcbe","Cypress Tree"
"0.008064521129029258","b70c44b1a38c0a2329fa583c9109a80f","Peach Tree"

在结果中,您会看到 id,这是指定的 key_columnscore 是 RRF 计算出的最终值。倒数排序融合 (RRF) 是一种基于排名的算法,它通过为每个文档分配一个得分,将多个已排名的搜索结果列表整合为一个已排名的列表。此得分基于通过 RRF 获得的相应文档在所有贡献列表中的倒数排序,排名越高的文档贡献越大。在参数中使用 include_json_output => true,系统将返回一个 detail_json 列,其中包含每个组件的分数计算明细。

虽然全文搜索最擅长查找特定字词或完全匹配项,但向量搜索即使在字词不匹配的情况下,也能出色地查找同义词和意图。通过合并这两种方法,混合搜索可确保用户获得一组可靠的结果,这些结果在字面意义上准确无误,并且在语义上相关

11. 清理环境

完成实验后销毁 AlloyDB 实例和集群。

删除 AlloyDB 集群和所有实例

如果您曾使用 AlloyDB 试用版。如果您计划使用试用集群测试其他实验和资源,请勿删除试用集群。您将无法在同一项目中创建其他试用集群。

系统会通过强制选项销毁集群,该选项还会删除属于该集群的所有实例。

如果您已断开连接且之前的所有设置都已丢失,请在 Cloud Shell 中定义项目和环境变量:

gcloud config set project <your project id>
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
export PROJECT_ID=$(gcloud config get-value project)

删除集群:

gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force

预期的控制台输出:

student@cloudshell:~ (test-project-001-402417)$ gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force
All of the cluster data will be lost when the cluster is deleted.

Do you want to continue (Y/n)?  Y

Operation ID: operation-1697820178429-6082890a0b570-4a72f7e4-4c5df36f
Deleting cluster...done.   

删除 AlloyDB 备份

删除集群的所有 AlloyDB 备份:

for i in $(gcloud alloydb backups list --filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" --format="value(name)" --sort-by=~createTime) ; do gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done

预期的控制台输出:

student@cloudshell:~ (test-project-001-402417)$ for i in $(gcloud alloydb backups list --filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" --format="value(name)" --sort-by=~createTime) ; do gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done
Operation ID: operation-1697826266108-60829fb7b5258-7f99dc0b-99f3c35f
Deleting backup...done.                                                                                                                                                                                                                                                            

现在我们可以销毁虚拟机了

删除 GCE 虚拟机

在 Cloud Shell 中,执行以下命令:

export GCEVM=instance-1
export ZONE=us-central1-a
gcloud compute instances delete $GCEVM \
    --zone=$ZONE \
    --quiet

预期的控制台输出:

student@cloudshell:~ (test-project-001-402417)$ export GCEVM=instance-1
export ZONE=us-central1-a
gcloud compute instances delete $GCEVM \
    --zone=$ZONE \
    --quiet
Deleted

12. 恭喜

恭喜您完成此 Codelab。

所学内容