编程 MySQL 9.x 深度实战:原生向量检索 + JSON Duality + 容器化,一个关系型数据库如何长出 AI 与文档数据库的翅膀(2026)

2026-07-13 05:43:33 +0800 CST views 10

MySQL 9.x 深度实战:原生向量检索 + JSON Duality + 容器化,一个关系型数据库如何长出 AI 与文档数据库的翅膀(2026)

当所有人都在讨论 pgvector、Milvus、MongoDB 时,MySQL 这个"老古董"在 9.x 系列里悄悄完成了一次基因突变:它既能存向量做语义检索,又能把关系表直接投影成可读写的 JSON 文档,还能在 Kubernetes 里自动感知 CPU/内存限制。本文不堆参数,只讲一个务实工程师真正用得上什么、怎么用、以及坑在哪。

一、背景介绍:为什么 2026 年我们还要关心 MySQL 的新版本

如果你是一名做了五六年业务的后端,听到"数据库又发新版了"大概率会无感——MySQL 8.0 一用就是七八年,稳定、熟悉、生态全。但 2026 年这个时间点有点不一样:AI 应用落地的主力矛盾,已经从"模型够不够聪明"变成了"数据怎么喂给模型"

RAG(检索增强生成)是当下最主流的落地形态,而 RAG 的检索层,本质就是一个向量相似度搜索。过去两年行业的标准答案是:业务数据放 MySQL/PostgreSQL,向量单独搬去 Pinecone、Milvus 或 pgvector。这套架构跑得起来,但代价是双写一致性、两套运维、两份账单、两套备份策略——对于一个日活几万的普通应用来说,这个复杂度是严重过剩的。

MySQL 9.x 想解决的,就是把这个复杂度收回到一张库里:

  • 9.0(2024-07):引入原生 VECTOR 类型,意味着你终于可以在一张 InnoDB 表里同时存订单记录和它的向量,不用再维护一个外置向量库。
  • 9.6(2026-01):JSON Duality Views 正式可用,让同一份关系数据既能用 SQL 查、又能以 MongoDB 式 JSON 文档读写;同时 container_aware 启动参数让它在容器里"懂事"了;GTID 结构升级、InnoDB 多项改进。
  • 9.7DISTANCE() 距离函数补齐 EUCLIDEAN(欧氏距离),和 COSINE、DOT 凑齐三件套。

这篇文章的核心观点很明确:MySQL 9.x 不是要取代专用向量库或文档库,而是给"还没到那个规模、但已经被多套存储折磨"的团队一个"先别拆"的理由。 下面我们用代码说话。

注:MySQL 采用"Innovation"创新版与 LTS 长期支持版双轨。9.x 属于创新版,迭代快、特性新,生产环境请评估稳定性后再上。本文所有示例基于 9.6 / 9.7 语法。

二、核心概念:三块新拼图到底长什么样

2.1 VECTOR 类型:不是"加了个字段类型"那么简单

VECTOR(N) 表示一个由 N 个 4 字节单精度浮点(FLOAT)组成的定长数组。关键点:

  • 每个元素固定 4 字节,默认维度 2048,最大 16383。维度在定义时固定,例如 VECTOR(1536) 对应 OpenAI text-embedding-3-small 的维度。
  • 底层以二进制字符串存储,不是 JSON、不是 TEXT。你看不到 [0.1, 0.2] 这种明文。
  • 不能做主键、外键、唯一键、分区键——向量本身不是个好的"身份标识"。
  • 只能和另一个 VECTOR 做相等比较,不能和数值、字符串比较,也不能直接 ORDER BY embedding(必须借助距离函数)。

四个核心函数:

函数作用
STRING_TO_VECTOR()(别名 TO_VECTOR()'[1.0, 2.0]' 这种字符串转成二进制向量
VECTOR_TO_STRING()(别名 FROM_VECTOR()反向,把二进制向量转回字符串便于查看
VECTOR_DIM()返回向量维度
DISTANCE() / VECTOR_DISTANCE()计算两向量距离,支持 COSINEDOT,9.7 起加 EUCLIDEAN

一个最朴素的例子:

-- 建一张带向量列的 InnoDB 表
CREATE TABLE product_embeddings (
    id        BIGINT PRIMARY KEY,
    name      VARCHAR(200),
    embedding VECTOR(3)          -- 为了演示用 3 维,真实场景是 1536/768 等
);

INSERT INTO product_embeddings VALUES
  (1, '无线耳机',  TO_VECTOR('[0.9, 0.1, 0.2]')),
  (2, '机械键盘',  TO_VECTOR('[0.2, 0.8, 0.1]')),
  (3, '蓝牙音箱',  TO_VECTOR('[0.85, 0.15, 0.25]'));

-- 以余弦距离为度量,找出和查询向量最像的 2 个
SELECT
    name,
    VECTOR_DIM(embedding)                          AS dim,
    DISTANCE(embedding,
             TO_VECTOR('[0.88, 0.12, 0.22]'),
             'COSINE')                             AS cosine_dist
FROM product_embeddings
ORDER BY cosine_dist
LIMIT 2;

DISTANCE() 返回的是距离,越小越相似;如果你想要"相似度分数",用 1 - DISTANCE(..., 'COSINE')(余弦距离 = 1 - 余弦相似度)即可。

2.2 JSON Duality Views:关系表和 JSON 文档的"双向翻译官"

这是 9.x 里最容易被低估、但对业务开发最友好的特性。一句话解释:它让你用一条 SQL 视图定义,把多张关系表"拼"成一个 JSON 文档,而且这个文档不仅能读,还能被 UPDATE/INSERT/DELETE 回写进原来的关系表里。

传统 ETL 或 ORM 里,"关系→文档"通常是单向的(读的时候拼),写文档再拆回关系表得自己写一堆逻辑。JSON Duality 在数据库内核层把这个双向映射做掉了。

语法核心是 CREATE JSON DUALITY VIEW,用 JSON { ... } 描述文档结构,_id 作为文档主键:

-- 先准备关系表
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name        VARCHAR(100),
    email       VARCHAR(200)
);
CREATE TABLE orders (
    order_id    INT PRIMARY KEY,
    customer_id INT,
    product     VARCHAR(100),
    amount      DECIMAL(10,2),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
INSERT INTO customers VALUES (1, 'Alice', 'alice@demo.com'), (2, 'Bob', 'bob@demo.com');
INSERT INTO orders VALUES
  (1, 1, 'Milk', 10), (2, 1, 'Curd', 5), (3, 2, 'Flour', 20);

-- 定义一个"客户 + 其订单数组"的 JSON 文档视图
CREATE OR REPLACE JSON DUALITY VIEW customer_doc AS
SELECT JSON {
    '_id': c.customer_id,
    'name': c.name,
    'email': c.email,
    'orders': [
        SELECT JSON {
            'orderId': o.order_id,
            'product': o.product,
            'amount': o.amount
        }
        FROM orders o
        WHERE o.customer_id = c.customer_id
    ]
}
FROM customers c;

读出来就是标准 JSON 文档:

SELECT c.* FROM customer_doc c;
-- 输出大致是:
-- {"_id": 1, "name": "Alice", "email": "alice@demo.com",
--  "orders": [{"orderId":1,"product":"Milk","amount":10}, ...]}

而关键的能力——反向写回——意味着你的应用可以像操作 MongoDB 文档一样 UPDATE 这个视图,数据库会自动把改动映射回 customersorders 两张表。对前端/Node 同学来说,这相当于免费获得了一个"关系型底、文档型面"的存储层。

视图还有 WITH { INSERT | UPDATE | DELETE } 以及各种投影修饰符(EAGER/LAZY/INCLUDE)来控制嵌套数据的加载与同步策略,后面性能章节会展开。

2.3 9.6 的"运维向"硬货:容器、GTID、InnoDB、安全

这才是很多团队真正会拍大腿的部分,因为它们直接关乎"能不能安稳跑在生产里":

  • container_aware 启动参数:MySQL 以前在容器里常常读不准宿主的 CPU/内存(尤其被 cgroup 限制后),导致缓冲池算错、线程数失控。--container-aware=ON 让它主动读取 cgroup 的 limit,自动按容器实际配额调整资源使用,在 K8s 里部署稳定性直接上一个台阶。
  • GTID 集合结构升级:全新的 GTID 集合数据结构,处理全局事务 ID 更高效,复制场景下性能与可维护性提升,事务集的分组识别更直观。
  • InnoDB 改进:唯一 rowid 生成效率提升;事务恢复机制优化(避免无效事务残留);Redo 日志错误信息增强,现在会带上 LSN 与容量信息,排障时能直接定位到"是哪个文件、写到哪了"。
  • 审计日志组件化:拆成可插拔组件,用 AUDIT_ADMIN 权限精细管理输出位置、格式、缓冲区;传统 MD5()SHA1() 被挪进 classic_hashing 组件,默认不装,符合现代安全合规(SHA-1 已被攻破)。
# my.cnf 中启用容器感知(K8s 部署强烈建议)
[mysqld]
container-aware=ON
# 9.6 起还可按需加载经典哈希组件(不推荐用于新系统)
# INSTALL COMPONENT 'file://component_classic_hashing';

三、架构分析:这些特性在引擎层是怎么落地的

3.1 向量在 InnoDB 里到底是什么

VECTOR 列在存储层就是一个定长二进制大对象,每个元素 4 字节紧凑排列。它不走传统的 B+Tree 索引路径——事实上 9.0 ~ 9.6 没有为向量建立 ANN(近似最近邻)索引的能力。这意味着:

一次 ORDER BY DISTANCE LIMIT k 的语义检索,本质是全表扫描 + 逐行算距离 + 排序

这听起来吓人,但在实践中完全可控,原因有两点:

  1. 向量维度虽然高(1536),但每一行的向量就那 1536×4≈6KB,顺序扫描的内存带宽压力远小于"随机读一堆小行"。
  2. 向量检索几乎总是带业务过滤条件的("找和这个最像、且 category=耳机、且 status=on_sale 的商品")。你可以先用传统的 B+Tree 索引把候选集从百万级砍到千级,再在这千行里算距离。

架构上,推荐的形态是:向量列 + 若干普通索引列共存于同一张 InnoDB 表,检索时 WHERE 业务条件 AND ... ORDER BY DISTANCE LIMIT k。这正是 MySQL 9.x 相比"外置向量库"的最大架构优势——过滤和向量检索在同一个查询计划里完成,没有跨系统 join。

3.2 JSON Duality 的双向映射原理

Duality View 不是一个物化视图(默认不冗余存储文档),它是一层查询重写 + 写操作分解的逻辑。读时,它把 JSON 结构展开成对底层关系表的 JOIN/子查询;写时,它把对 JSON 文档的 UPDATE/INSERT 翻译成对 customersorders 等基础表的事务化改写,并通过 _id 维护文档与主表的对应关系。

这就意味着:你完全没有数据冗余,文档和关系表是同一份数据的两种投影。这跟 MongoDB 的文档模型、以及 PostgreSQL 的 jsonb 有本质区别——那些是"数据本身就是文档";而 MySQL Duality 是"数据还是关系表,只是多了一副文档的面具"。

3.3 复制与高可用

GTID 结构升级后,基于 GTID 的主从复制、以及组复制(MGR)在事务集合的表示与比对上更高效。对于向量 + 关系混合的库,复制链路和普通 InnoDB 表没有区别——向量列就是普通的列,跟着 binlog 走,不存在"向量库要单独同步"的麻烦。

四、代码实战:从零搭一个 MySQL 9.x 的语义检索 + 文档读写服务

下面是一套能真正跑起来的最小实践。我们用 Python 演示两个场景:① 用 MySQL 9.x 向量列做商品语义搜索;② 用 JSON Duality 做文档式读写。

4.1 环境与建表

# 拉取 9.x 创新版(演示用 9.6+)
docker run -d --name mysql96 \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=demo \
  -p 3306:3306 \
  mysql:9.6 --container-aware=ON
-- 真实语义搜索:用 1536 维(OpenAI text-embedding-3-small)
CREATE TABLE products (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    title       VARCHAR(255) NOT NULL,
    category    VARCHAR(64)  NOT NULL,
    on_sale     TINYINT      NOT NULL DEFAULT 1,
    embedding   VECTOR(1536) NULL,
    KEY idx_cat  (category),        -- 业务过滤靠它
    KEY idx_sale (on_sale)
);

4.2 Python:把文本变成向量,存进 MySQL,再搜回来

# pip install pymysql sentence-transformers numpy
import pymysql
import numpy as np
from sentence_transformers import SentenceTransformer

# 1) 加载本地嵌入模型(离线、免费、1536 维可选模型)
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")  # 384 维
DIM = model.get_sentence_embedding_dimension()

# 2) 连接 MySQL 9.x
conn = pymysql.connect(
    host="127.0.0.1", port=3306,
    user="root", password="secret", database="demo",
    charset="utf8mb4",
)

# 3) 动态建一张匹配维度的表
with conn.cursor() as cur:
    cur.execute(f"CREATE TABLE IF NOT EXISTS docs ("
                f"id BIGINT PRIMARY KEY AUTO_INCREMENT, "
                f"text VARCHAR(512), emb VECTOR({DIM}), "
                f"KEY idx_len (CHAR_LENGTH(text)))")
    conn.commit()

def embed_and_insert(texts):
    vecs = model.encode(texts, normalize_embeddings=True)  # 归一化,余弦=点积
    with conn.cursor() as cur:
        for t, v in zip(texts, vecs):
            # 把 numpy 数组拼成 '[a,b,c]' 字符串交给 TO_VECTOR
            arr = "[" + ",".join(f"{x:.6f}" for x in v.tolist()) + "]"
            cur.execute("INSERT INTO docs(text, emb) VALUES (%s, TO_VECTOR(%s))",
                        (t, arr))
    conn.commit()

def search(query, top_k=3, category_filter=None):
    q = model.encode([query], normalize_embeddings=True)[0]
    q_arr = "[" + ",".join(f"{x:.6f}" for x in q.tolist()) + "]"
    sql = """
        SELECT text,
               1 - DISTANCE(emb, TO_VECTOR(%s), 'COSINE') AS score
        FROM docs
        {where}
        ORDER BY score DESC
        LIMIT %s
    """
    where = ""
    params = [q_arr]
    if category_filter:
        where = "WHERE CHAR_LENGTH(text) > 0"  # 占位,可替换为真实过滤列
        # 真实业务里这里会是:WHERE category = %s
    params.append(top_k)
    with conn.cursor() as cur:
        cur.execute(sql.format(where=where), params)
        return cur.fetchall()

# 4) 灌入一点数据
embed_and_insert([
    "如何给 Kubernetes 集群做资源限制和弹性伸缩",
    "MySQL 9.x 原生向量类型做语义搜索的完整教程",
    "用 Rust 写一个高性能的 gRPC 微服务框架",
    "前端构建工具从 webpack 迁移到 Vite 的踩坑记录",
])
# 5) 搜一下
print(search("数据库怎么做 AI 检索"))
# => 会命中 'MySQL 9.x 原生向量类型...' 且 score 最高

这段代码里有几个工程上很重要的细节,很多教程会漏掉:

  • 向量先做归一化normalize_embeddings=True):归一化后余弦相似度 = 点积,而 DOT 距离计算比 COSINE 更省事(省了除以模长的步骤)。如果你归一化了,用 DISTANCE(emb, q, 'DOT') 再取负更直接。
  • 维度必须和模型对齐all-MiniLM-L6-v2 是 384 维,OpenAI 是 1536,本地 bge-small 是 512。建表维度写死后不能改,所以建表前先 model.get_sentence_embedding_dimension() 确认。
  • 字符串 → 向量用 TO_VECTOR('[...]'):注意浮点数精度,保留 6 位小数足够,别用 str(float) 默认格式(可能出 1e-05 这种科学计数法,部分版本解析要测一下)。
  • 过滤先行:真实业务里务必把 category/on_sale 这类带索引的条件写进 WHERE,让 MySQL 先用 B+Tree 把行数砍下来,再算距离。百万级数据全表扫向量会慢,但"先过滤到几千行"后速度完全可接受。

4.3 JSON Duality:像操作文档一样读写关系数据

-- 创建一个"用户档案"文档视图:基础信息 + 嵌套的标签数组
CREATE OR REPLACE JSON DUALITY VIEW user_profile AS
SELECT JSON {
    '_id':       u.id,
    'nickname':  u.nickname,
    'bio':       u.bio,
    'tags': [
        SELECT JSON {'tag': t.name}
        FROM user_tags t
        WHERE t.user_id = u.id
    ]
} WITH INSERT UPDATE DELETE
FROM users u;

应用层(Node/Python 任意)就可以直接拿 JSON 文档来用,读是标准 JSON,写回库时数据库自动拆回 usersuser_tags

-- 像改文档一样改关系数据:把 Alice 的 bio 更新,同时追加一个标签
UPDATE user_profile
SET doc = JSON_SET(doc, '$.bio', '热爱云原生与数据库内核')
WHERE doc->>'$.nickname' = 'Alice';

对前端同学来说,这等于省掉了一整个"把关系结果拼成前端想要的 JSON"的胶水层,而且对写操作有事务保证——不会出现"文档更新了但某个子表没跟上"的部分失败。

4.4 把检索封装成一个生产可用的 HTTP 接口(FastAPI)

上面的代码还停留在脚本层面。真正落地时,语义搜索是要被业务服务调用的。下面用 FastAPI 包一层,注意几个生产细节:连接池、超时、对空结果的处理、以及把向量计算放到后台模型、查询走 MySQL 连接池

# pip install fastapi uvicorn pymysql sentence-transformers dbutils
import pymysql, pymysql.cursors
from fastapi import FastAPI
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
from dbutils.pooled_db import PooledDB  # pip install dbutils

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
DIM = model.get_sentence_embedding_dimension()

pool = PooledDB(
    creator=pymysql, maxconnections=10, mincached=2, maxcached=5,
    host="127.0.0.1", port=3306,
    user="app", password="apppwd", database="demo",
    charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor,
)

app = FastAPI()

class SearchReq(BaseModel):
    query: str
    top_k: int = 5
    category: str | None = None

@app.post("/search")
def search(req: SearchReq):
    # 1) 编码查询(已归一化,余弦=点积)
    q = model.encode([req.query], normalize_embeddings=True)[0]
    q_arr = "[" + ",".join(f"{x:.6f}" for x in q.tolist()) + "]"

    # 2) 拼 SQL:过滤条件 + 向量距离,务必参数化防注入
    where = "WHERE 1=1"
    params: list = [q_arr]
    if req.category:
        where += " AND category = %s"
        params.append(req.category)
    where += " ORDER BY 1 - DISTANCE(embedding, TO_VECTOR(%s), 'COSINE') DESC LIMIT %s"
    params.append(req.top_k)

    with pool.connection() as conn, conn.cursor() as cur:
        cur.execute(
            f"SELECT id, title, 1 - DISTANCE(embedding, TO_VECTOR(%s), 'COSINE') AS score FROM products {where}",
            params,
        )
        rows = cur.fetchall()
    if not rows:
        return {"hits": [], "note": "无匹配,可能是该分类下无数据或模型维度不匹配"}
    return {"hits": rows}

几个上线前必须想清楚的要点:

  • 模型维度漂移是头号事故源:你 products.embedding 是 384 维,结果哪天换了 1536 维的模型,灌进来的向量维度对不上,查询直接报错或返回垃圾。务必在应用启动和写入两条路径上统一维度来源,最好把维度写进一张 meta 表做校验。
  • TO_VECTOR 的参数是字符串,必须走参数化:别用 f-string 把向量拼进 SQL,既不安全也可能因格式问题注入失败。上面把 TO_VECTOR(%s) 当占位符传参是正确的。
  • 连接池 + 只读副本:语义检索是读多写少,建议查向量走只读副本连接池,写入走主库,避免大查询拖慢主库写入。

五、性能优化:把 MySQL 9.x 用到"刚好够快"

5.1 向量检索的三条提速路径

由于 9.x 暂无原生 ANN 索引,检索性能完全靠"减少参与距离计算的行数":

  1. 强过滤 + 索引列:把 categorystatuscreated_at 等高频过滤条件建 B+Tree 索引,查询时先 WHERE 砍行。这是性价比最高的一招。
  2. 分区表:如果数据天然可分桶(按业务线、按地区),用 PARTITION BY 把向量表按某个枚举列分区,查询时只扫相关分区。注意 VECTOR 列本身不能做分区键,但可以用其它列分区。
  3. 维度权衡:嵌入模型维度越高越准,但每行体积和算距成本线性上升。业务搜索用 384(all-MiniLM-L6-v2)往往够用,别无脑上 1536。实测中 384 维在百万行"先过滤后检索"场景下,P99 延迟通常在百毫秒级。
-- 典型的"过滤 + 向量"混合查询模板
SELECT id, title,
       1 - DISTANCE(embedding, TO_VECTOR(%s), 'COSINE') AS score
FROM products
WHERE category = '耳机' AND on_sale = 1      -- 先用索引砍行
ORDER BY score DESC
LIMIT 10;                                    -- 再在候选集里排序取 top10

5.2 JSON Duality 的投影策略

Duality View 支持在嵌套结构上加修饰符控制加载行为:

  • EAGER:查询文档时立即把嵌套数据 JOIN 出来(适合小数据集、强一致读)。
  • LAZY:访问时才懒加载(适合大数组、避免每次都拖一大坨)。
  • INCLUDE:只读投影,不回写。

经验法则:顶层字段用 EAGER,超长的子数组(如订单历史、评论流)用 LAZY,否则一次读文档就把整张子表拉爆。

5.3 InnoDB 与容器化调优

  • 开了 --container-aware=ON 后,不要再硬编码 innodb_buffer_pool_size 为宿主物理内存的 80%——让 MySQL 自己读 cgroup limit 来定,否则在 2G 限制的容器里硬配 12G 缓冲池会 OOM 被杀。
  • 9.6 的 Redo 日志错误带 LSN/容量信息后,建议把 innodb_print_all_deadlocks=ON 配上,排障时能直接拿到"卡在哪段日志"的关键线索。
  • 向量列不参与事务回滚的额外代价很低(就是普通列),但批量灌向量时请关掉 autocommit、每 1000~5000 行 commit 一次,比单行提交快一个数量级。

5.4 排障与踩坑清单(血泪向)

把这套东西跑进生产,有几个坑是文档不会主动告诉你的:

  1. VECTOR 列不能直接放进 GROUP BY / DISTINCT / 聚合函数。想对向量去重?先 VECTOR_TO_STRING() 转成字符串再操作,但那样会丢失精度且很慢——正确做法是向量列只做距离比较,其它聚合走普通列。
  2. DISTANCE() 的第三个参数是字符串且必须加引号'COSINE''DOT''EUCLIDEAN'(9.7+)。写成 DISTANCE(a, b, COSINE)(没引号)会被当成列名/标识符而报错。
  3. JSON Duality 写回失败,多半是 _id 映射歧义:视图里必须有一个能唯一定位主表行的 _id,如果嵌套子查询的关联条件写错,UPDATE 文档时数据库不知道改哪行,会报映射错误。开发时小步快跑、先验证单文档读写。
  4. 创新版的版本号陷阱mysql:9 这种 tag 会随创新版滚动到 9.6、9.7……你的 DISTANCE 用了 EUCLIDEAN 就得确保镜像 ≥9.7,否则函数不存在。生产镜像请钉死具体小版本,例如 mysql:9.6.0
  5. container-aware=ON 不会自动调大缓冲池,它只是让 MySQL 读 cgroup limit。如果你在 4G 容器里手动设了 innodb_buffer_pool_size=20G,依然会 OOM。正确姿势是让它自动,或显式设一个小于 cgroup limit 的值。
  6. 向量全表扫的延迟随行数线性增长:没有 ANN 索引时,从 10 万行扫到 1000 万行,延迟大约翻 100 倍。所以"先过滤后检索"不是优化技巧,是必选项。

六、总结与展望:MySQL 9.x 到底该不该上

回到开头那个核心判断:MySQL 9.x 的价值不在于"它比 Milvus 快"或"它比 MongoDB 灵活",而在于它把三种工作负载收敛到了一套存储、一套备份、一套运维里。

适合用 MySQL 9.x 统一收敛的场景:

  • 已有 MySQL 业务,想加一点语义搜索/RAG,数据量在千万行以内,不想引入新组件;
  • 前端苦于"关系表拼 JSON"的胶水代码,想要文档式读写但底层仍是关系模型、要事务保证;
  • 团队小、运维人力紧张,多一套专用存储的边际成本高于它带来的性能收益。

暂时不适合、该继续用专用方案的场景:

  • 向量规模上亿、需要真正的 HNSW/IVF 近似索引和亚毫秒检索——这时候 Milvus / pgvector(带 ivfflat/hnsw)仍是更专业的选择;
  • 文档结构极度动态、完全无模式(schema-less)需求强烈的——MongoDB 的文档原生模型更顺手;
  • 对版本稳定性要求极高、不敢碰创新版的生产核心库——等 9.x 出 LTS 或升到对应 LTS 再迁移。

一个务实的建议:不要为了"追新"把核心交易库升级到 9.x 创新版;但完全可以在一条只读副本 / 新业务线上先试点向量检索和 JSON Duality,用最低的试错成本验证"少一套存储"是否真的省事。等尝到甜头、踩完坑,再决定是否往核心迁移。

为了更直观地判断"该不该用 MySQL 9.x",给出一张取舍表,按数据规模和团队阶段对号入座:

你的现状推荐方案理由
已有 MySQL,想加语义搜索,数据 < 千万行MySQL 9.x 向量列零新组件,过滤+向量同库完成
已有 MySQL,前端苦于拼 JSONJSON Duality Views关系底+文档面,双向同步有事务
向量上亿、要亚毫秒 ANNMilvus / pgvector(hnsw)需要真正的近似最近邻索引
文档完全无模式、结构天天变MongoDB原生文档模型更顺手
核心交易库、稳定性压倒一切维持 8.0 LTS,另起 9.x 副本试点创新版不适合直接碰核心

最后给一条迁移路径建议,按风险从低到高:

  1. 影子读:在主库挂一个 9.x 只读副本,把语义搜索流量先打到副本,验证召回质量与延迟;
  2. 双写验证:新业务写入时同时落 9.x 向量列和老方案,跑一致性对账;
  3. 切读:确认无误后,把读路径从老方案切到 9.x;
  4. 收老:观察一个周期无问题,再下线外置向量/文档存储。

展望未来,MySQL 9.x 的路线图里最值得期待的,是向量索引——一旦官方补上 ANN 索引(类 HNSW),"过滤 + 向量"的混合检索就能从"先扫后算"进化到"索引直查",那才是它真正叫板专用向量库的时刻。在那之前,它是一个"足够好、且足够省心"的折中方案,而折中,恰恰是绝大多数工程团队真正需要的那把尺子。


参考要点(便于溯源):原生 VECTOR 类型与 TO_VECTOR/VECTOR_DIM/DISTANCE(COSINE/DOT,9.7 加 EUCLIDEAN)出自 MySQL 9.0+ 官方手册;JSON Duality Views 语法与双向映射见 MySQL 9.6 Reference Manual 第 27.7 节;container_aware、GTID 结构升级、InnoDB rowid/恢复/Redo 日志增强、审计日志组件化与 classic_hashing 出自 MySQL 9.6 发布说明。生产使用前请以你目标版本的官方文档为准。

推荐文章

api远程把word文件转换为pdf
2024-11-19 03:48:33 +0800 CST
程序员茄子在线接单