一、引言
在大模型与检索增强生成(RAG)技术普及的当下,向量检索已从逐渐从小众能力跃升为通用需求。关系型数据库作为企业数据架构的核心,长期以来以结构化数据管理、ACID 事务、成熟的 SQL 生态为核心优势,但在非结构化数据(文本、图片、音频)的语义检索场景中存在天然短板。
MySQL 作为全球使用最广泛的关系型数据库,也顺应这一趋势完成了向量检索能力的迭代,MySQL 8.4.0 首次引入原生VECTOR类型和 HNSW 向量索引,将向量检索纳入标准功能体系;而对于存量的 8.0.x 低版本用户,也可通过 "字符串存储 + 自定义函数" 的兼容方案实现向量检索。今天我们将以 MySQL 为核心,由浅入深解析关系型数据库融合向量检索的技术逻辑、版本特性差异、兼容架构设计,以及可落地的实战案例,完整呈现这一数据库领域的重要演进方向。

二、MySQL的向量检索能力
1. 传统 MySQL 的能力缺口
MySQL 作为经典的关系型数据库,擅长处理具有明确 schema 的结构化数据(数值、字符串、日期等),支持精准的 SQL 条件查询、关联查询和事务管理。但在 AI 时代,企业 80% 以上的数据为非结构化数据(用户评论、产品文档、客服对话等),这类数据的核心价值在于语义内涵而非显性特征,传统 MySQL 的处理痛点极为突出:
- 非结构化数据只能以TEXT/BLOB类型存储,无法理解其语义;
- 检索依赖关键词匹配(LIKE/ 全文索引),无法实现语义相似性检索;
- 无法与大模型联动,支撑 RAG、智能问答、内容推荐等新兴业务;
- 需与独立向量数据库(如 Milvus、Chroma)联动,增加架构复杂度和运维成本。
2. 向量检索的补位价值
向量检索的核心是将非结构化数据通过 Embedding 模型转化为高维向量,利用 "向量空间距离越近、语义越相似" 的特性实现语义匹配。将向量检索能力融入 MySQL,可带来三大核心价值:
- 架构简化:无需额外部署独立向量数据库,在现有 MySQL 架构中统一管理结构化数据 + 向量数据,降低运维成本;
- 能力互补:支持 "SQL 条件过滤 + 向量相似性检索" 的复合查询,例如 "查询 2024 年发布的、与'MySQL 向量检索'语义相似的技术文档";
- 生态兼容:依托 MySQL 成熟的事务机制、备份策略和生态工具(如 binlog、主从复制),保障向量数据的安全性和稳定性。
3. MySQL 向量检索的技术演进
MySQL 的向量检索能力演进分为两个阶段,适配不同版本用户的需求:
3.1 MySQL 8.0.x(全系列),不包含原生的向量能力,需要兼容实现:
- 实现方案:字符串存储向量 + 自定义函数计算相似度
- 优势:无需升级、适配存量系统
- 局限性:无原生索引,仅适合中小规模数据
3.2 MySQL 8.4.0+,支持内置的向量能力
- 实现方式:内置VECTOR类型、HNSW 向量索引、VECTOR_DISTANCE函数
- 优势:性能优、检索效率高、原生集成
- 局限性:需升级数据库,存量系统改造成本高
三、MySQL 向量检索原生特性
MySQL 8.4.0 是向量检索能力的里程碑版本,首次将向量功能纳入核心标准,以下是其核心特性解析:
1. 原生向量数据类型:VECTOR
- 支持自定义向量维度(最大支持 32768 维),适配主流 Embedding 模型(如all-MiniLM-L6-v2的 384 维、BERT 的 768 维);
- 存储优化:针对高维向量做了内存对齐和压缩,存储效率优于普通数组类型;
语法示例:重点关注VECTOR类型字段声明
python
-- 创建含768维向量字段的表
CREATE TABLE tech_docs (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
publish_date DATE NOT NULL,
tag VARCHAR(50) NOT NULL,
embedding VECTOR(768) NOT NULL -- 768维向量字段
);
2. 原生向量索引:HNSW
MySQL 8.4.0 引入分层导航小世界(HNSW)索引,这是当前业界性能最优的近似最近邻(ANN)算法之一:
- 支持余弦距离(COSINE)、欧氏距离(L2)、曼哈顿距离(MANHATTAN)三种核心距离计算方式;
- 可配置核心参数(m邻接节点数、ef_construction构建时搜索范围),平衡检索精度与速度;
语法示例:
python
-- 为向量字段创建HNSW索引(余弦距离)
CREATE INDEX idx_tech_docs_embedding ON tech_docs
USING HNSW (embedding) WITH (
metric_type = 'COSINE', -- 距离计算方式
m = 16, -- 每个节点的邻接数
ef_construction = 64 -- 构建索引时的探索范围
);
3. 原生相似度计算:VECTOR_DISTANCE函数
- 用于计算两个向量的距离值,值越小表示语义越相似;
- 余弦相似度 = 1 - VECTOR_DISTANCE(向量1, 向量2, 'COSINE');
语法示例:
python
-- 计算查询向量与文档向量的余弦相似度
SELECT
title,
1 - VECTOR_DISTANCE(embedding, '[0.123,0.456,...]', 'COSINE') AS similarity
FROM tech_docs
ORDER BY similarity DESC;
四、MySQL 向量检索兼容实现
对于无法升级到 8.4.0 的存量 MySQL 8.0.x 用户,需通过 "兼容架构" 实现向量检索,以下案例适配所有 MySQL 8.0.x 版本,包含数据库初始化、数据入库、复合查询、可视化全流程,可直接落地。
1. 兼容性说明
在不支持原生特性的情况下,我们要做针对性的兼容性处理:
1.1 兼容架构核心逻辑
第一层:向量存储层
- 放弃原生VECTOR类型,改用TEXT类型存储向量,格式为 "逗号分隔的浮点数字符串"(如0.123,0.456,...,0.789);
- 向量维度选择 384 维(all-MiniLM-L6-v2模型),平衡语义精度与计算成本;
- 新增向量归一化处理:入库前将向量归一化(模长 = 1),简化后续相似度计算。
第二层:相似度计算层
- 创建自定义 MySQL 函数calc_cosine_similarity,手动实现余弦相似度计算:
- 余弦相似度 = 向量点积 / (向量1模长 × 向量2模长)
- 归一化后向量的模长 = 1,相似度可简化为 "向量点积",大幅提升计算效率。
第三层:检索层
- 放弃原生向量索引,采用 "暴力检索":先通过 SQL 过滤结构化条件(如发布时间、标签),再对过滤后的数据集计算向量相似度;
- 适合中小规模数据(<1 万条),大规模数据建议升级 MySQL 或引入分布式检索引擎。
1.2 兼容架构关键组件
- 向量转换函数:处理向量数组 ↔ 字符串相互转换,通过 Python 函数embedding_to_str/str_to_embedding实现
- 自定义相似度函数:计算余弦相似度,新增MySQL 函数calc_cosine_similarity
- 结构化索引:加速条件过滤,对publish_date/tag创建普通索引
- 复合查询逻辑:过滤优先/检索优先,通过SQL 条件 + 相似度排序
1.3 兼容架构核心流程

2. 核心代码说明
2.1 数据库配置与初始化
python
import pymysql
import numpy as np
import matplotlib.pyplot as plt
from sentence_transformers import SentenceTransformer
from datetime import date
from typing import List, Tuple
# ===================== 1. 数据库配置与连接 =====================
DB_CONFIG = {
"host": "localhost",
"user": "root",
"password": "**********",
"database": "数据库名称",
"charset": "utf8mb4"
}
# 初始化Embedding模型(生成384维向量,降低计算成本)
model = SentenceTransformer('D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2')
VECTOR_DIM = 384 # 向量维度
# ===================== 2. 工具函数 =====================
def get_mysql_connection():
"""创建并返回MySQL连接"""
conn = pymysql.connect(**DB_CONFIG)
conn.autocommit(False)
return conn
def init_database():
"""初始化数据库表结构(兼容低版本MySQL)"""
conn = get_mysql_connection()
cursor = conn.cursor()
# 1. 创建表:向量存储为字符串(逗号分隔的float值)
cursor.execute("""
DROP TABLE IF EXISTS tech_docs;
""")
cursor.execute("""
CREATE TABLE tech_docs (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
publish_date DATE NOT NULL,
tag VARCHAR(50) NOT NULL,
embedding TEXT NOT NULL -- 向量存储为字符串:"x1,x2,...x384"
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""")
# 2. 创建结构化字段索引(向量无原生索引,用暴力检索)
cursor.execute("""
CREATE INDEX idx_tech_docs_publish_date ON tech_docs(publish_date);
""")
cursor.execute("""
CREATE INDEX idx_tech_docs_tag ON tech_docs(tag);
""")
# 3. 创建自定义余弦相似度计算函数(核心兼容逻辑)
cursor.execute("""
DROP FUNCTION IF EXISTS calc_cosine_similarity;
""")
cursor.execute("""
CREATE FUNCTION calc_cosine_similarity(vec1 TEXT, vec2 TEXT)
RETURNS FLOAT
DETERMINISTIC
BEGIN
DECLARE dot_product FLOAT DEFAULT 0.0;
DECLARE norm1 FLOAT DEFAULT 0.0;
DECLARE norm2 FLOAT DEFAULT 0.0;
DECLARE i INT DEFAULT 1;
DECLARE v1 FLOAT;
DECLARE v2 FLOAT;
-- 拆分向量为数组并计算点积、模长
WHILE i <= %d DO
SET v1 = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(vec1, ',', i), ',', -1) AS FLOAT);
SET v2 = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(vec2, ',', i), ',', -1) AS FLOAT);
SET dot_product = dot_product + (v1 * v2);
SET norm1 = norm1 + (v1 * v1);
SET norm2 = norm2 + (v2 * v2);
SET i = i + 1;
END WHILE;
-- 计算余弦相似度(避免除以0)
IF norm1 = 0 OR norm2 = 0 THEN
RETURN 0.0;
ELSE
RETURN dot_product / (SQRT(norm1) * SQRT(norm2));
END IF;
END;
""" % VECTOR_DIM)
conn.commit()
cursor.close()
conn.close()
print("数据库表、索引、自定义函数初始化完成!")
2.2 数据入库与向量处理
python
def embedding_to_str(embedding: List[float]) -> str:
"""将向量数组转为逗号分隔的字符串"""
return ','.join(map(str, embedding))
def str_to_embedding(embedding_str: str) -> np.ndarray:
"""将字符串转回向量数组"""
return np.array([float(x) for x in embedding_str.split(',')])
def insert_docs(docs: List[dict]):
"""插入文档数据(生成向量并入库)"""
conn = get_mysql_connection()
cursor = conn.cursor()
for doc in docs:
# 生成384维向量并转为字符串
embedding = model.encode(doc["content"], normalize_embeddings=True).tolist() # 归一化提升相似度计算精度
embedding_str = embedding_to_str(embedding)
# 插入数据
cursor.execute("""
INSERT INTO tech_docs (title, content, publish_date, tag, embedding)
VALUES (%s, %s, %s, %s, %s)
""", (doc["title"], doc["content"], doc["publish_date"], doc["tag"], embedding_str))
conn.commit()
cursor.close()
conn.close()
print(f"成功插入{len(docs)}条文档数据!")
2.3 复合查询实现
python
def hybrid_query_filter_first(query_text: str, publish_date_after: date, top_k: int = 2) -> List[Tuple]:
"""过滤优先:先按发布时间过滤,再向量检索"""
# 生成查询向量并转为字符串
query_embedding = model.encode(query_text, normalize_embeddings=True).tolist()
query_embedding_str = embedding_to_str(query_embedding)
conn = get_mysql_connection()
cursor = conn.cursor()
# 使用自定义函数计算余弦相似度
cursor.execute("""
SELECT
title, content, publish_date, tag,
calc_cosine_similarity(embedding, %s) AS similarity,
1 - calc_cosine_similarity(embedding, %s) AS distance
FROM tech_docs
WHERE publish_date >= %s
ORDER BY similarity DESC -- 相似度越高越靠前
LIMIT %s
""", (query_embedding_str, query_embedding_str, publish_date_after, top_k))
results = cursor.fetchall()
cursor.close()
conn.close()
return results
def hybrid_query_search_first(query_text: str, target_tag: str, top_k: int = 3) -> List[Tuple]:
"""检索优先:先向量检索Top-K,再过滤标签"""
# 生成查询向量并转为字符串
query_embedding = model.encode(query_text, normalize_embeddings=True).tolist()
query_embedding_str = embedding_to_str(query_embedding)
conn = get_mysql_connection()
cursor = conn.cursor()
# 先暴力检索Top-K(按相似度排序)
cursor.execute("""
SELECT
title, content, publish_date, tag,
calc_cosine_similarity(embedding, %s) AS similarity,
1 - calc_cosine_similarity(embedding, %s) AS distance
FROM tech_docs
ORDER BY similarity DESC
LIMIT %s
""", (query_embedding_str, query_embedding_str, top_k))
raw_results = cursor.fetchall()
cursor.close()
conn.close()
# 过滤标签
filtered_results = [r for r in raw_results if r[3] == target_tag]
return filtered_results
2.4 结果可视化与输出
python
def plot_similarity(results: List[Tuple], title: str):
"""绘制相似度分布柱状图(图片输出)"""
if not results:
print("无数据可绘制图表!")
return
# 提取数据
doc_titles = [r[0][:10] + "..." for r in results] # 标题截断
similarities = [round(r[4], 4) for r in results] # 相似度
distances = [round(r[5], 4) for r in results] # 距离
# 设置中文字体
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 创建子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 子图1:余弦相似度
ax1.bar(doc_titles, similarities, color="#34A853", alpha=0.8)
ax1.set_title(f"{title} - 余弦相似度")
ax1.set_xlabel("文档标题")
ax1.set_ylabel("相似度(越大越相似)")
ax1.grid(axis="y", linestyle="--", alpha=0.7)
for i, v in enumerate(similarities):
ax1.text(i, v + 0.01, f"{v}", ha="center")
# 子图2:余弦距离
ax2.bar(doc_titles, distances, color="#4285F4", alpha=0.8)
ax2.set_title(f"{title} - 余弦距离")
ax2.set_xlabel("文档标题")
ax2.set_ylabel("距离值(越小越相似)")
ax2.grid(axis="y", linestyle="--", alpha=0.7)
for i, v in enumerate(distances):
ax2.text(i, v + 0.01, f"{v}", ha="center")
plt.tight_layout()
plt.savefig(f"{title.replace(' ', '_')}.png", dpi=300, bbox_inches="tight")
plt.show()
print(f"相似度图表已保存为:{title.replace(' ', '_')}.png")
def print_formatted_results(results: List[Tuple], scene_name: str):
"""格式化输出检索结果"""
print(f"\n===== {scene_name} 查询结果 =====")
if not results:
print("无符合条件的结果!")
return
for i, res in enumerate(results):
title, content, publish_date, tag, similarity, distance = res
print(f"\n【结果{i+1}】")
print(f"标题:{title}")
print(f"标签:{tag}")
print(f"发布时间:{publish_date}")
print(f"余弦相似度:{similarity:.4f}(越大越相似)")
print(f"余弦距离:{distance:.4f}(越小越相似)")
print(f"内容:{content[:100]}...")
2.5 主流程执行
python
# ===================== 3. 主流程执行 =====================
if __name__ == "__main__":
# 步骤1:初始化数据库
init_database()
# 步骤2:准备测试数据
test_docs = [
{
"title": "RAG技术核心原理与实践",
"content": "RAG技术通过检索外部知识增强大模型生成能力,解决幻觉问题,是当前大模型落地的核心技术之一",
"publish_date": date(2024, 5, 10),
"tag": "RAG"
},
{
"title": "MySQL向量检索兼容方案",
"content": "低版本MySQL可通过字符串存储向量,自定义函数计算余弦相似度,实现基础的向量检索能力",
"publish_date": date(2024, 6, 15),
"tag": "向量数据库"
},
{
"title": "PostgreSQL pgvector插件实战",
"content": "pgvector为PostgreSQL提供向量存储和检索能力,支持余弦、欧氏等多种距离计算方式,适配RAG场景",
"publish_date": date(2024, 7, 20),
"tag": "向量数据库"
},
{
"title": "大模型微调技术LoRA实战",
"content": "LoRA通过低秩适配实现高效微调,无需训练整个模型参数,大幅降低大模型微调的硬件成本",
"publish_date": date(2023, 12, 5),
"tag": "大模型"
}
]
# 步骤3:插入测试数据
insert_docs(test_docs)
# 步骤4:场景A - 过滤优先查询
query_a = "MySQL向量检索"
publish_date_a = date(2024, 1, 1)
results_a = hybrid_query_filter_first(query_a, publish_date_a, top_k=2)
print_formatted_results(results_a, "场景A(过滤优先)")
plot_similarity(results_a, "场景A - MySQL向量检索文档相似度分布")
# 步骤5:场景B - 检索优先查询
query_b = "大模型应用"
target_tag_b = "RAG"
results_b = hybrid_query_search_first(query_b, target_tag_b, top_k=3)
print_formatted_results(results_b, "场景B(检索优先)")
plot_similarity(results_b, "场景B - 大模型应用-RAG文档相似度分布")
输出结果:
数据库表、索引、自定义函数初始化完成!
成功插入4条文档数据!
===== 场景A(过滤优先) 查询结果 =====
【结果1】
标题:MySQL向量检索兼容方案
标签:向量数据库
发布时间:2024-06-15
余弦相似度:0.6495(越大越相似)
余弦距离:0.3505(越小越相似)
内容:低版本MySQL可通过字符串存储向量,自定义函数计算余弦相似度,实现基础的向量检索能力...
【结果2】
标题:RAG技术核心原理与实践
标签:RAG
发布时间:2024-05-10
余弦相似度:0.3424(越大越相似)
余弦距离:0.6576(越小越相似)
内容:RAG技术通过检索外部知识增强大模型生成能力,解决幻觉问题,是当前大模型落地的核心技术之一...
相似度图表已保存为:场景A_-_MySQL向量检索文档相似度分布.png

===== 场景B(检索优先) 查询结果 =====
【结果1】
标题:RAG技术核心原理与实践
标签:RAG
发布时间:2024-05-10
余弦相似度:0.5568(越大越相似)
余弦距离:0.4432(越小越相似)
内容:RAG技术通过检索外部知识增强大模型生成能力,解决幻觉问题,是当前大模型落地的核心技术之一...
相似度图表已保存为:场景B_-_大模型应用-RAG文档相似度分布.png

数据入库参考:

五、总结
MySQL 向量检索能力的融入,标志着关系型数据库从结构化数据管理向结构化 + 非结构化数据统一管理的转型。对于我们开发版本兼容选择来说:
- 若使用 MySQL 8.4.0+,优先采用原生VECTOR类型和 HNSW 索引,享受最优性能;
- 若使用 8.0.x 低版本,可通过 "字符串存储 + 自定义函数" 的兼容架构,快速落地向量检索能力;
无论哪种方案,都能在现有 MySQL 架构中实现 "SQL 条件过滤 + 向量语义检索" 的复合能力,无需重构数据架构,大幅降低 AI 应用的落地成本。向量检索终将成为 MySQL 的标准功能,使得我们在不改变现有数据架构的前提下,快速拥抱大模型和语义检索技术,释放数据的深层价值。