第 37 章

向量搜索

MySQL 向量搜索与 AI 集成

随着大语言模型(LLM)的爆发式增长,向量数据库成为 AI 应用的核心基础设施。MySQL 9.0 正式引入 VECTOR 数据类型,使开发者可以在熟悉的 MySQL 环境中实现语义搜索、RAG(检索增强生成)等 AI 功能,无需引入额外的向量数据库服务。

什么是向量搜索


文本/图片/音频
       ↓  Embedding 模型 (OpenAI, text-embedding-3, BGE...)
  高维向量 [0.12, -0.34, 0.78, ..., 0.05]  (通常 768~3072 维)
       ↓  存入数据库
  近似最近邻搜索 (ANN)
       ↓  余弦相似度 / 点积 / L2 距离
  语义相似的结果集

向量搜索的核心应用场景:

MySQL 9.0 VECTOR 数据类型

MySQL 9.0(2024 年 7 月发布)正式引入 VECTOR(dimensions) 数据类型,用于存储固定维度的浮点向量。

-- 创建包含向量列的表(存储商品的语义向量)
CREATE TABLE products (
    id          INT UNSIGNED    NOT NULL AUTO_INCREMENT,
    name        VARCHAR(200)    NOT NULL,
    description TEXT,
    category    VARCHAR(50),
    price       DECIMAL(10,2),
    embedding   VECTOR(1536)    NOT NULL COMMENT 'OpenAI text-embedding-3-small 输出 1536 维',
    created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    VECTOR INDEX idx_embedding (embedding)  -- 向量索引(MySQL 9.0+)
) ENGINE=InnoDB;

-- 插入向量数据
-- embedding 使用 JSON 数组格式输入
INSERT INTO products (name, description, embedding)
VALUES (
    '防晒霜 SPF50',
    '高效防紫外线,轻薄不油腻',
    STRING_TO_VECTOR('[0.12, -0.34, 0.78, ..., 0.05]')  -- 1536 个浮点数
);

VECTOR 类型操作函数

-- 向量相似度计算(余弦相似度)
SELECT id, name,
       VECTOR_DISTANCE(embedding, STRING_TO_VECTOR('[...]'), 'COSINE') AS distance
FROM products
ORDER BY distance ASC
LIMIT 10;

-- 向量相似度(点积,向量已归一化时等价于余弦相似度)
SELECT id, name,
       DOT_PRODUCT(embedding, STRING_TO_VECTOR('[...]')) AS similarity
FROM products
ORDER BY similarity DESC
LIMIT 10;

-- 向量转换
SELECT VECTOR_TO_STRING(embedding) FROM products WHERE id = 1;  -- 转回 JSON
SELECT VECTOR_DIM(embedding) FROM products WHERE id = 1;        -- 查看维度 → 1536

-- 向量索引 ANN 搜索(近似最近邻)
SELECT id, name, VECTOR_DISTANCE(embedding, ?, 'COSINE') AS dist
FROM products
ORDER BY dist ASC
LIMIT 10
-- MySQL 优化器自动使用 VECTOR INDEX 进行 ANN 搜索

生成 Embedding 向量

使用 OpenAI API(Go 示例)

package main

import (
    "context"
    "github.com/sashabaranov/go-openai"
    "database/sql"
)

func embedText(text string) ([]float32, error) {
    client := openai.NewClient(os.Getenv("OPENAI_API_KEY"))
    resp, err := client.CreateEmbeddings(context.Background(),
        openai.EmbeddingRequest{
            Input: []string{text},
            Model: openai.SmallEmbedding3, // text-embedding-3-small, 1536 维
        })
    if err != nil {
        return nil, err
    }
    return resp.Data[0].Embedding, nil
}

func storeProduct(db *sql.DB, name, desc string) error {
    // 1. 生成 embedding
    vec, err := embedText(name + " " + desc)
    if err != nil { return err }

    // 2. 转为 JSON 数组字符串
    vecJSON, _ := json.Marshal(vec)

    // 3. 插入 MySQL
    _, err = db.Exec(
        "INSERT INTO products (name, description, embedding) VALUES (?, ?, STRING_TO_VECTOR(?))",
        name, desc, string(vecJSON))
    return err
}

使用本地模型(BGE-M3,中文效果好)

# 使用 Ollama 运行本地 Embedding 模型
ollama pull bge-m3

# Python 示例
import ollama
import json

def embed_text(text: str) -> list[float]:
    response = ollama.embeddings(model='bge-m3', prompt=text)
    return response['embedding']  # 1024 维

# 批量处理(节省 API 调用费用)
texts = ["防晒霜 SPF50", "保湿乳液", "护肤精华"]
embeddings = [embed_text(t) for t in texts]

相似度计算详解

方法 公式 适用场景 MySQL 函数
余弦相似度 cos(θ) = A·B / (|A||B|) 文本语义相似度(最常用) VECTOR_DISTANCE(a, b, 'COSINE')
欧氏距离 (L2) √∑(aᵢ-bᵢ)² 图像特征、地理位置 VECTOR_DISTANCE(a, b, 'EUCLIDEAN')
点积 ∑(aᵢ×bᵢ) 向量已归一化时等价余弦 DOT_PRODUCT(a, b)
-- 综合示例:找到与给定文本最相似的 5 个商品
-- 先在应用层获取查询文本的 embedding,再传入 SQL
SELECT
    id,
    name,
    description,
    ROUND(1 - VECTOR_DISTANCE(embedding, ?, 'COSINE'), 4) AS similarity
FROM products
WHERE category = '护肤'  -- 可以先用传统过滤缩小范围
ORDER BY VECTOR_DISTANCE(embedding, ?, 'COSINE') ASC
LIMIT 5;

RAG 架构实战

RAG(Retrieval-Augmented Generation)让 LLM 能回答私有知识库中的问题,是 AI 应用最流行的架构模式。


用户提问: "如何退货?"
         ↓
  1. 对问题生成 Embedding
         ↓
  2. MySQL 向量搜索 TOP-K 相关文档
         ↓
  3. 拼接 Prompt: 上下文 + 原始问题
         ↓
  4. 发送给 LLM (GPT-4, Claude, Qwen...)
         ↓
  5. 返回基于知识库的答案

-- 知识库表
CREATE TABLE knowledge_base (
    id          INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title       VARCHAR(200) NOT NULL,
    content     TEXT         NOT NULL,
    source      VARCHAR(200) COMMENT '来源文档',
    chunk_index SMALLINT     COMMENT '文档分块索引',
    embedding   VECTOR(1536) NOT NULL,
    created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    VECTOR INDEX idx_emb (embedding)
) ENGINE=InnoDB;
// Go RAG 实现
func AnswerQuestion(db *sql.DB, userQuestion string) (string, error) {
    // 1. 对用户问题生成 embedding
    qEmbedding, _ := embedText(userQuestion)
    qVecJSON, _ := json.Marshal(qEmbedding)

    // 2. 向量搜索 Top 5 相关文档
    rows, _ := db.Query(`
        SELECT title, content,
               ROUND(1 - VECTOR_DISTANCE(embedding, STRING_TO_VECTOR(?), 'COSINE'), 4) AS sim
        FROM knowledge_base
        ORDER BY VECTOR_DISTANCE(embedding, STRING_TO_VECTOR(?), 'COSINE') ASC
        LIMIT 5`,
        string(qVecJSON), string(qVecJSON))

    var context strings.Builder
    for rows.Next() {
        var title, content string
        var sim float64
        rows.Scan(&title, &content, &sim)
        if sim > 0.7 { // 相似度阈值过滤
            context.WriteString(fmt.Sprintf("# %s\n%s\n\n", title, content))
        }
    }

    // 3. 构建 Prompt 调用 LLM
    prompt := fmt.Sprintf(`基于以下知识库内容回答问题:

%s

问题:%s

请基于以上内容给出准确答案,如果知识库中没有相关信息,请说明。`,
        context.String(), userQuestion)

    return callLLM(prompt)
}

混合检索(向量 + 关键词)

纯向量搜索有时会漏掉精确匹配的结果(如产品型号、人名),混合检索结合了语义搜索和关键词搜索的优势。

-- 方案:MySQL 全文索引 + 向量索引联合检索
SELECT id, name, description,
       -- 归一化向量相似度分
       (1 - VECTOR_DISTANCE(embedding, STRING_TO_VECTOR(?), 'COSINE')) AS vec_score,
       -- 全文搜索相关度分
       MATCH(name, description) AGAINST(? IN NATURAL LANGUAGE MODE) AS ft_score,
       -- 加权混合分
       (1 - VECTOR_DISTANCE(embedding, STRING_TO_VECTOR(?), 'COSINE')) * 0.7 +
       MATCH(name, description) AGAINST(? IN NATURAL LANGUAGE MODE) * 0.3 AS hybrid_score
FROM products
WHERE MATCH(name, description) AGAINST(? IN BOOLEAN MODE)  -- 先用 FTS 过滤
   OR VECTOR_DISTANCE(embedding, STRING_TO_VECTOR(?), 'COSINE') < 0.5
ORDER BY hybrid_score DESC
LIMIT 20;

替代方案对比

方案 优势 劣势 适用场景
MySQL 9.0 VECTOR 无需额外服务,SQL 集成 ANN 算法较基础,大规模性能有限 <100 万向量,已有 MySQL 基础设施
pgvector (PostgreSQL) HNSW/IVFFlat 索引,性能强 需要切换到 PostgreSQL AI 原生应用,已有 PG 基础设施
Milvus / Qdrant 专用向量数据库,亿级规模 额外运维成本,数据同步 超大规模向量搜索
Elasticsearch 混合搜索(向量+关键词) 复杂、资源消耗大 搜索为核心场景
Redis (RedisVL) 低延迟,适合实时推荐 内存成本高 实时推荐,热数据向量

MySQL 9.0 向量搜索的局限:目前(9.0)向量索引使用的是较简单的 ANN 实现,在千万级向量规模时性能不及专用向量数据库(Milvus/pgvector)。适合:中小规模(<500 万)向量,希望减少基础设施复杂度的场景。大规模场景仍建议使用 pgvector 或专用向量数据库。

实践建议:对于已在使用 MySQL 的 SaaS 产品,MySQL 9.0 的 VECTOR 类型是引入 AI 功能的最快路径:不用学新数据库,不用管理额外服务,直接在现有 MySQL 表上加 VECTOR 列即可实现语义搜索。待规模增长后,再评估是否迁移到专用向量数据库。

本章评分
4.7  / 5  (3 评分)

💬 留言讨论