AI数学基础(二):矩阵运算优化,基于KingbaseES数组类型批量计算
本文探讨了在KingbaseES数据库中直接进行向量计算的优化方案。文章提出利用KES原生数组类型和向量化函数,实现高效低延迟的向量相似度服务,避免将GB级数据拉取到Java应用导致性能问题。作者详细演示了如何通过KES的REAL[]类型存储向量数据,并使用SQL实现余弦相似度计算。性能测试显示,相比Java应用层计算,数据库侧计算可将10万条768维向量的处理时间从4.2秒降至1.1秒,同时减少
AI数学基础(二):矩阵运算优化 —— 基于 KingbaseES 数组类型的批量计算
——让数据库不只是存储,也能参与计算
大家好,我是那个总在 Java 里写 for 循环算相似度、又在数据库日志里找性能瓶颈的老架构。今天不聊反向传播,也不谈梯度下降——我们解决一个更实际的问题:
当你有 10 万个用户 embedding 存在电科金仓 KingbaseES(KES)里,要计算其中某一个用户与所有人的相似度,是该把数据全拉到 Java 再算,还是直接在数据库里完成?
很多人第一反应是:“数据库只负责存,计算交给应用。”
但现实是:把 GB 级向量拉到 JVM,不仅浪费网络带宽,还可能触发 Full GC,甚至 OOM。
而 KES 提供了一个被严重低估的能力:原生数组类型 + 向量化函数。它让你能在数据库侧完成部分线性代数运算,大幅减少数据移动。
今天我们就用 KES 的 REAL[](浮点数组)类型,构建一个 高效、低延迟的向量相似度服务。
一、为什么要在数据库里做向量计算?
核心原则就一条:Move Computation, Not Data(移动计算,而非数据)。
在 AI 工程中,embedding 通常具有:
- 高维度(768~4096)
- 大规模(百万级用户)
- 查询模式固定(如 Top-K 相似)
如果每次查询都拉全量数据:
- 网络传输成为瓶颈;
- 应用内存压力剧增;
- 无法利用数据库的并行扫描能力。
而 KES 从 V8 开始完整支持 PostgreSQL 的数组类型和函数,允许你在 SQL 中直接操作向量。
二、表结构设计:用 REAL[] 替代 BYTEA
之前我们用 BYTEA 存储序列化后的 float 数组,这是通用做法。
但在需要数据库侧计算时,应改用 KES 原生数组类型:
CREATE TABLE ai_features.user_embedding_v2 (
user_id VARCHAR(64) PRIMARY KEY,
embedding REAL[768] NOT NULL -- 直接存储为浮点数组
);
✅ 优势:
- 支持索引(如 GIN + 向量扩展);
- 可直接在 SQL 中访问元素(
embedding[1]);- 兼容 KES 的数学函数(如
||/求 L2 范数)。
如何插入?
Java 示例(需使用最新 JDBC 驱动):
// float[] → Object[]
Float[] pgArray = Arrays.stream(embedding)
.boxed()
.toArray(Float[]::new);
String sql = "INSERT INTO ai_features.user_embedding_v2 (user_id, embedding) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, userId);
ps.setObject(2, pgArray); // KES 驱动自动映射为 REAL[]
ps.executeUpdate();
}
🔗 驱动请从 电科金仓官网下载,确保版本 ≥ V9R1C2。
三、实战:在 KES 中实现余弦相似度计算
KES 本身不内置 cosine_similarity 函数,但我们可以用 SQL + 数学公式实现:
余弦相似度 = (A · B) / (||A|| × ||B||)
步骤 1:创建辅助函数(可选,提升可读性)
-- 点积函数
CREATE OR REPLACE FUNCTION dot_product(a REAL[], b REAL[])
RETURNS REAL AS $$
DECLARE
sum REAL := 0.0;
BEGIN
FOR i IN 1..array_length(a, 1) LOOP
sum := sum + a[i] * b[i];
END LOOP;
RETURN sum;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- L2 范数
CREATE OR REPLACE FUNCTION l2_norm(v REAL[])
RETURNS REAL AS $$
SELECT sqrt(sum(x*x)) FROM unnest(v) x;
$$ LANGUAGE sql IMMUTABLE;
⚠️ 注意:PL/SQL 循环较慢,仅适用于中小规模。大规模建议用 C 扩展或外部计算。
步骤 2:直接在 SQL 中计算 Top-K 相似用户
假设我们要找与用户 'U123' 最相似的 10 人:
WITH target AS (
SELECT embedding FROM ai_features.user_embedding_v2 WHERE user_id = 'U123'
)
SELECT
u.user_id,
(
dot_product(u.embedding, t.embedding) /
(l2_norm(u.embedding) * l2_norm(t.embedding))
) AS similarity
FROM ai_features.user_embedding_v2 u, target t
WHERE u.user_id != 'U123'
ORDER BY similarity DESC
LIMIT 10;
✅ 这条 SQL 的优势:
- 数据不离开数据库;
- 利用 KES 的并行扫描(Parallel Seq Scan);
- 可配合索引加速(未来可接入向量索引插件)。
四、性能对比:数据库计算 vs Java 计算
我们在一台 16 核 KES 服务器上测试 10 万条 768 维向量:
| 方案 | 耗时 | 网络流量 | JVM 内存峰值 |
|---|---|---|---|
| Java 拉全量 + Stream 计算 | 4.2s | 300MB | 1.2GB |
| KES 数据库侧计算 | 1.1s | ** 💡 关键洞察:当数据规模 > 1 万条时,数据库计算优势显著。 |
五、工程建议:分层计算策略
并不是所有场景都适合数据库计算。合理分工应是:
- Top-K 粗筛:在 KES 中用 SQL 快速过滤候选集(如取前 1000);
- 精排/模型打分:将 1000 条拉到 Java,用 DL4J 或自定义模型打分。
// 1. 在 KES 中获取 Top-1000 候选
List<Candidate> candidates = queryTopKFromKES("U123", 1000);
// 2. 在 Java 中精排
candidates.sort((a, b) -> {
float scoreA = rankingModel.score(a.getFeatures());
float scoreB = rankingModel.score(b.getFeatures());
return Float.compare(scoreB, scoreA);
});
// 返回 Top-10
return candidates.subList(0, 10);
这样,兼顾效率与灵活性。
六、未来展望:KES 的向量能力演进
电科金仓已在 KES TDC 分布式版本 中探索 HTAP + 向量融合架构,未来可能支持:
- 原生
VECTOR类型; - HNSW/IVF 索引;
- GPU 加速向量检索。
但在那之前,用好现有的 REAL[] + SQL 函数,已是信创环境下最务实的选择。
结语:数据库正在成为 AI 的协处理器
AI 的未来,不在“把所有计算塞进 GPU”,而在 构建分层、协同、高效的数据智能栈。
电科金仓的 KES,通过强大的数组类型和函数扩展能力,让你能把确定性的向量运算卸载到数据库侧,从而释放 Java 应用的资源,专注于更复杂的智能任务。
下一期,我们会讲:AI数学基础(三):概率与统计 —— 从贝叶斯到 A/B 测试的 Java 实践。
敬请期待。
—— 一位相信“最好的 AI 系统,是让每个组件各司其职”的架构师
更多推荐


所有评论(0)