Vector 基于多索引表架构应对海量多租户的大规模向量检索

本文涉及的产品
对象存储 OSS,OSS 加速器 50 GB 1个月
简介: OSS 向量 Bucket 支持多索引架构,通过按租户/业务创建独立索引,实现数据隔离、并发检索与结果合并,兼顾高安全性与毫秒级响应,轻松应对千万级向量的多租户 RAG 与语义搜索场景。

随着 RAG 和语义搜索需求的增长,向量检索系统常面临两类挑战:

  • 多租户隔离:SaaS 服务商为大量企业客户提供知识库,或企业内部各部门拥有独立知识库,要求数据严格隔离。
  • 超大规模数据:单索引数据量达到千万甚至亿级时,检索延迟显著增加,难以满足实时性要求。

OSS 向量 Bucket 支持同一账号在同一地域创建大量向量索引(Index)。通过多索引架构,可以按租户或业务维度拆分数据,兼顾隔离性和检索性能。

多索引架构的优势

  • 数据隔离:不同租户或业务的数据存放在独立索引中,从底层避免跨租户数据泄露。
  • 检索提速:将大表拆分为多张小表,缩小单次检索范围;配合并发检索多个索引再合并结果,可有效降低总响应耗时。
  • 运维灵活:不同索引可独立配置维度、模型、相似度算法。删除某个租户的数据只需删除对应索引,无需逐条过滤删除。

通过 CLI 按租户导入数据

oss-vectors-embed CLI 工具支持将指定文件写入指定索引,实现按租户或业务维度的定向导入。

安装方式请参见使用OSS Vectors Embed CLI工具写入和检索向量数据

开始前,请确保满足以下条件:

  • 已配置环境变量 OSS_ACCESS_KEY_IDOSS_ACCESS_KEY_SECRETDASHSCOPE_API_KEY
  • 已创建向量 Bucket 和各租户对应的向量索引。

将以下示例中的占位符替换为实际值:

占位符

说明

<your-account-id>

阿里云账号 ID

<your-vector-bucket>

向量 Bucket 名称

按租户写入不同索引

将不同租户的数据写入各自独立的索引,实现数据隔离。

# 将租户 A 的文档写入租户 A 的索引
oss-vectors-embed \
  --account-id "<your-account-id>" \
  --vectors-region cn-hangzhou \
  put \
  --vector-bucket-name "<your-vector-bucket>" \
  --index-name "tenantcompanya" \
  --model-id text-embedding-v4 \
  --text-value "租户A的知识库文档内容" \
  --key "doc_001" \
  --metadata '{"tenant": "company_a", "category": "faq"}'
# 将租户 B 的文档写入租户 B 的索引
oss-vectors-embed \
  --account-id "<your-account-id>" \
  --vectors-region cn-hangzhou \
  put \
  --vector-bucket-name "<your-vector-bucket>" \
  --index-name "tenantcompanyb" \
  --model-id text-embedding-v4 \
  --text-value "租户B的知识库文档内容" \
  --key "doc_001" \
  --metadata '{"tenant": "company_b", "category": "manual"}'

按租户定向检索

检索时只查询目标租户的索引,天然实现数据隔离。

# 仅在租户 A 的索引中检索
oss-vectors-embed \
  --account-id "<your-account-id>" \
  --vectors-region cn-hangzhou \
  query \
  --vector-bucket-name "<your-vector-bucket>" \
  --index-name "tenantcompanya" \
  --model-id text-embedding-v4 \
  --text-value "常见问题" \
  --top-k 5 \
  --return-metadata

通过 SDK 构建多索引架构

Python SDK

开始前请安装 alibabacloud-oss-v2 SDK:

pip install alibabacloud-oss-v2

确保已配置环境变量 OSS_ACCESS_KEY_IDOSS_ACCESS_KEY_SECRET

创建多租户索引

以租户 ID 为后缀命名索引,批量创建独立的向量索引。

import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.vectors as oss_vectors
ACCOUNT_ID = "<your-account-id>"
REGION = "cn-hangzhou"
BUCKET = "<your-vector-bucket>"
def create_vector_client():
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = REGION
    cfg.account_id = ACCOUNT_ID
    return oss_vectors.Client(cfg)
client = create_vector_client()
# 批量为租户创建索引
tenant_ids = ["companya", "companyb", "companyc"]
for tenant_id in tenant_ids:
    index_name = f"tenant{tenant_id}"
    result = client.put_vector_index(oss_vectors.models.PutVectorIndexRequest(
        bucket=BUCKET,
        index_name=index_name,
        dimension=1024,
        data_type="float32",
        distance_metric="cosine",
    ))
    print(f"索引 {index_name} 创建完成,status_code={result.status_code}")

运行后输出:

索引 tenantcompanya 创建完成,status_code=200
索引 tenantcompanyb 创建完成,status_code=200
索引 tenantcompanyc 创建完成,status_code=200

按租户写入数据

将不同租户的数据写入各自的索引。

import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.vectors as oss_vectors
ACCOUNT_ID = "<your-account-id>"
REGION = "cn-hangzhou"
BUCKET = "<your-vector-bucket>"
def create_vector_client():
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = REGION
    cfg.account_id = ACCOUNT_ID
    return oss_vectors.Client(cfg)
client = create_vector_client()
# 向租户 A 的索引写入数据
result = client.put_vectors(oss_vectors.models.PutVectorsRequest(
    bucket=BUCKET,
    index_name="tenantcompanya",
    vectors=[
        {
            "key": "faq_001",
            "data": {"float32": [0.1] * 1024},  # 向量维度需与索引一致
            "metadata": {"tenant": "company_a", "category": "faq"}
        }
    ]
))
print(f"租户 A 写入完成,status_code={result.status_code}")
# 向租户 B 的索引写入数据
result = client.put_vectors(oss_vectors.models.PutVectorsRequest(
    bucket=BUCKET,
    index_name="tenantcompanyb",
    vectors=[
        {
            "key": "manual_001",
            "data": {"float32": [0.2] * 1024},  # 向量维度需与索引一致
            "metadata": {"tenant": "company_b", "category": "manual"}
        }
    ]
))
print(f"租户 B 写入完成,status_code={result.status_code}")

运行后输出:

租户 A 写入完成,status_code=200
租户 B 写入完成,status_code=200

并发检索多个索引并合并结果

将大表拆分为多张小表后,通过并发检索多个索引再合并排序,降低总响应耗时。

from concurrent.futures import ThreadPoolExecutor, as_completed
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.vectors as oss_vectors
ACCOUNT_ID = "<your-account-id>"
REGION = "cn-hangzhou"
BUCKET = "<your-vector-bucket>"
def create_vector_client():
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = REGION
    cfg.account_id = ACCOUNT_ID
    return oss_vectors.Client(cfg)
def search_index(client, index_name, query_vector, top_k=10):
    """检索单个索引"""
    result = client.query_vectors(oss_vectors.models.QueryVectorsRequest(
        bucket=BUCKET,
        index_name=index_name,
        query_vector=query_vector,
        return_metadata=True,
        return_distance=True,
        top_k=top_k,
    ))
    return {
        "index": index_name,
        "status_code": result.status_code,
        "vectors": result.vectors or [ ],
    }
def parallel_search(index_names, query_vector, top_k=10):
    """并发检索多个索引并合并结果"""
    client = create_vector_client()
    all_vectors = [ ]
    with ThreadPoolExecutor(max_workers=len(index_names)) as executor:
        futures = {
            executor.submit(search_index, client, idx, query_vector, top_k): idx
            for idx in index_names
        }
        for future in as_completed(futures):
            result = future.result()
            print(f"索引 {result['index']} 返回 {len(result['vectors'])} 条结果")
            all_vectors.extend(result["vectors"])
    # 按 distance 升序排序(距离越小越相似),取全局 TopK
    all_vectors.sort(key=lambda v: v.get("distance", float("inf")))
    return all_vectors[:top_k]
# 并发检索 3 个分表索引
indices = ["tenantcompanya", "tenantcompanyb", "tenantcompanyc"]
query_vec = {"float32": [0.1] * 1024}  # 向量维度需与索引一致
results = parallel_search(indices, query_vec, top_k=5)
print(f"\n合并后全局 Top5:")
for v in results:
    print(f"  key={v.get('key')}, distance={v.get('distance')}, metadata={v.get('metadata')}")

运行后输出:

索引 tenantcompanya 返回 1 条结果
索引 tenantcompanyb 返回 1 条结果
索引 tenantcompanyc 返回 0 条结果
合并后全局 Top5:
  key=faq_001, distance=0.0, metadata={'tenant': 'company_a', 'category': 'faq'}
  key=manual_001, distance=0.19999998807907104, metadata={'tenant': 'company_b', 'category': 'manual'}

说明:并发检索多个索引后,在客户端按 distance 排序合并结果。如需更高精度,可引入 Rerank 模型进行二次精排。

Go SDK

开始前请安装 alibabacloud-oss-go-sdk-v2 SDK:

go get github.com/aliyun/alibabacloud-oss-go-sdk-v2

确保已配置环境变量 OSS_ACCESS_KEY_IDOSS_ACCESS_KEY_SECRET

创建多租户索引

package main
import (
  "context"
  "fmt"
  "log"
  "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
  "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
  "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/vectors"
)
const (
  region     = "cn-hangzhou"
  bucketName = "<your-vector-bucket>"
  accountId  = "<your-account-id>"
)
func main() {
  cfg := oss.LoadDefaultConfig().
    WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    WithRegion(region).
    WithAccountId(accountId)
  client := vectors.NewVectorsClient(cfg)
  // 批量为租户创建索引
  tenantIDs := [ ]string{"companya", "companyb", "companyc"}
  for _, tenantID := range tenantIDs {
    indexName := fmt.Sprintf("tenant%s", tenantID)
    result, err := client.PutVectorIndex(context.TODO(), &vectors.PutVectorIndexRequest{
      Bucket:         oss.Ptr(bucketName),
      IndexName:      oss.Ptr(indexName),
      Dimension:      oss.Ptr(1024),
      DataType:       oss.Ptr("float32"),
      DistanceMetric: oss.Ptr("cosine"),
    })
    if err != nil {
      log.Printf("索引 %s 创建失败: %v", indexName, err)
      continue
    }
    fmt.Printf("索引 %s 创建完成,status_code=%d\n", indexName, result.StatusCode)
  }
}

运行后输出:

索引 tenantcompanya 创建完成,status_code=200
索引 tenantcompanyb 创建完成,status_code=200
索引 tenantcompanyc 创建完成,status_code=200

并发检索多个索引并合并结果

package main
import (
  "context"
  "fmt"
  "log"
  "sort"
  "sync"
  "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
  "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
  "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/vectors"
)
const (
  region     = "cn-hangzhou"
  bucketName = "<your-vector-bucket>"
  accountId  = "<your-account-id>"
  dimension  = 1024
)
func makeVector(val float32, dim int) [ ]float32 {
  v := make([ ]float32, dim)
  for i := range v {
    v[i] = val
  }
  return v
}
func main() {
  cfg := oss.LoadDefaultConfig().
    WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    WithRegion(region).
    WithAccountId(accountId)
  client := vectors.NewVectorsClient(cfg)
  indices := [ ]string{"tenantcompanya", "tenantcompanyb", "tenantcompanyc"}
  queryVector := map[string]any{"float32": makeVector(0.1, dimension)}
  var mu sync.Mutex
  var allVectors [ ]map[string]any
  var wg sync.WaitGroup
  for _, indexName := range indices {
    wg.Add(1)
    go func(idx string) {
      defer wg.Done()
      result, err := client.QueryVectors(context.TODO(), &vectors.QueryVectorsRequest{
        Bucket:         oss.Ptr(bucketName),
        IndexName:      oss.Ptr(idx),
        QueryVector:    queryVector,
        ReturnMetadata: oss.Ptr(true),
        ReturnDistance:  oss.Ptr(true),
        TopK:           oss.Ptr(10),
      })
      if err != nil {
        log.Printf("索引 %s 检索失败: %v", idx, err)
        return
      }
      fmt.Printf("索引 %s 返回 %d 条结果\n", idx, len(result.Vectors))
      mu.Lock()
      allVectors = append(allVectors, result.Vectors...)
      mu.Unlock()
    }(indexName)
  }
  wg.Wait()
  // 按 distance 升序排序,取全局 Top5
  sort.Slice(allVectors, func(i, j int) bool {
    di, _ := allVectors[i]["distance"].(float64)
    dj, _ := allVectors[j]["distance"].(float64)
    return di < dj
  })
  topK := 5
  if len(allVectors) < topK {
    topK = len(allVectors)
  }
  fmt.Printf("\n合并后全局 Top%d:\n", topK)
  for _, v := range allVectors[:topK] {
    fmt.Printf("  key=%v, distance=%v, metadata=%v\n", v["key"], v["distance"], v["metadata"])
  }
}

运行后输出:

索引 tenantcompanya 返回 1 条结果
索引 tenantcompanyc 返回 0 条结果
索引 tenantcompanyb 返回 1 条结果
合并后全局 Top2:
  key=faq_001, distance=0, metadata=map[category:faq tenant:company_a]
  key=manual_001, distance=0.19999998807907104, metadata=map[category:manual tenant:company_b]

最佳实践

  • 索引命名规范:以租户 ID 或业务维度作为索引名称的后缀(如 tenant{tenantid})。索引名仅支持小写字母和数字,不支持下划线和连字符。
  • 租户数较多时:直接利用索引名称进行逻辑隔离。OSS 向量索引的创建是秒级的,管理开销极低。
  • 追求极低延迟时:当单索引超过千万级数据量,按业务逻辑(如时间、类别)进行水平拆分,通过并发检索多个索引再合并结果。
  • 结果重排(Rerank):多索引表的检索结果合并后,可以根据距离相似度进行简单重排,也可引入 Rerank 模型进行二次排序。
  • 索引清理:删除某个租户或业务的数据只需调用 DeleteVectorIndex 删除对应索引,无需逐条过滤删除。
相关实践学习
对象存储OSS快速上手——如何使用ossbrowser
本实验是对象存储OSS入门级实验。通过本实验,用户可学会如何用对象OSS的插件,进行简单的数据存、查、删等操作。
相关文章
|
1月前
|
人工智能 自然语言处理 安全
别再乱装了!OpenClaw 中文版一键部署,Windows 免配置直用(包含新安装包)
告别报错和复杂教程,这款 OpenClaw 汉化一键包自带完整运行环境,安装即汉化,无需手动修改任何文件,真正开箱即用。
|
24天前
|
存储 人工智能 弹性计算
揭秘千问 APP 千万级 AI 订单背后的记忆存储实践
2026年春节,千问 APP “春节请客计划” 9 小时破 1000 万单,依赖 Tablestore 构建的一站式记忆系统:支持短期/长期记忆统一管理、毫秒级读写、Serverless 弹性伸缩、多模态数据融合及原生向量检索,实现数十亿条记忆的高效存储与实时流转。
290 6
|
存储 人工智能 安全
揭秘 MiniMax MaxClaw:如何用阿里云让“龙虾”企业级大规模落地
MiniMax 依托于阿里云容器服务 Kubernetes 版(ACK)和容器计算服务(ACS)提供的 ACS Agent Sandbox,为其最新发布的企业级平台 MaxClaw 构建了一套端到端的云原生 Agent 基础设施。
426 0
|
1月前
|
人工智能 数据可视化 API
一文看懂 OpenClaw:基础概念详解 + 部署实操教程
2026年初爆火的开源AI智能体OpenClaw(昵称“龙虾”),突破传统聊天AI局限,具备读写文件、运行代码、操控浏览器等“动手能力”。支持微信/飞书交互,兼容多模型,本地或云端一键部署,赋能办公、开发、生活与创作场景。
558 5
|
1月前
|
人工智能 安全 BI
阿里云权益中心最新优惠权益:AI产品与云产品优惠权益解析
阿里云权益中心为开发者和企业提供丰富的AI产品与云产品优惠权益,涵盖Qwen3.6大模型折扣、千问旗舰模型、大模型创新场景应用(如电商营销、广告创作、短剧漫剧、AI Coding)、精选AI产品组合购及云产品权益。同时提供新人限时抢购、核心业务场景组合、长效“99”计划、云上“应用盒子”、开发者与中小企业优选方案、免费试用及高校学生专属权益等,通过多场景覆盖与成本优化,助力用户快速构建云上应用,推动业务创新与发展。
460 7
|
1月前
|
数据可视化 应用服务中间件 API
告别古法编程!我用Trae+阿里百炼Coding Plan,5分钟搭建一个Nginx日志可视化看板
利用闲置的阿里百炼Coding Plan,快速开发出一款单文件PHP版Nginx日志分析器。支持日志解析、IP/UA/Referer等多维统计、原始日志查看、关键词搜索及昼夜双模式,功能实用、部署简便。(239字)
|
1月前
|
人工智能 自然语言处理 JavaScript
OpenClaw 汉化中文版(包含新安装包)|Windows 一键安装,全程不用输任何命令
超省心的 OpenClaw 汉化一键安装包,Windows 系统直接双击安装,无需配置环境、不用敲代码命令,自动完成汉化与部署,小白也能轻松搞定。
|
2月前
|
弹性计算 安全 网络安全
最佳实践:OSS AP 和云网络 Gateway Endpoint
本文介绍阿里云 OSS AP 与 VPC 网关终端节点的组合方案,解决企业数据湖中私网访问、多部门权限隔离及 Bucket Policy 维护复杂等难题,实现安全、低成本的多租户架构。
473 3

热门文章

最新文章