首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >瞧不上AI的, 都会被时代抛弃

瞧不上AI的, 都会被时代抛弃

作者头像
用户4035096
发布2026-07-09 20:19:12
发布2026-07-09 20:19:12
80
举报

瞧不上AI的, 等着被时代抛弃吧

最近发的文章有AI的重度参与, 马上就有人下面留言评论有AI味了, AI味咋了? 用AI咋了? 用AI遭人嫌弃?

真的别看不上AI, 我的态度: 所有人都能用AI提效, 但是能把AI发挥到极致的人, 本身肯定也是这个领域的半个专家. 不信你等着瞧

就举个简单的例子吧:因为最近的大学生实数据库实践课教学需要, 需生成一些符合正态分布的向量数据(不能纯随机, 否则就失去了现实意义, 例如群体的聚集性、语义相似排序的意义).

用 AI 一下就搞定了, 我只提供了话术, 以及后续的稍作修改, 搞出一个适合的版本. 并行的话就不劳烦函数了, pgbench 开启压测模式即可.

(不过重点是你得懂业务啊, 你不懂业务怎么也不可能生成复合业务的数据, 至少得和AI高低多几轮对话, 走弯路对吧!)

prompt :

我要在PostgreSQL中创建一个存储16维向量的表, 需要生成测试数据, 写一个PostgreSQL函数, 生成1000个(可变量输入)离散随机的向量, 然后再以这1000个向量为中心, 生成2000个(可变量输入)离散向量, 离中心点距离越近出现概率越高, 越远概率越低.


来看看效果吧:AI 抢答

在 PostgreSQL 中处理向量数据,通常推荐安装 pgvector 插件(目前已经是向量数据库的事实标准)。为了满足你“中心点聚合”和“概率分布”的需求,我们需要结合高斯分布(正态分布)的逻辑来编写函数。

1. 环境准备

首先,确保你的数据库已经安装了 pgvector 插件。

代码语言:javascript
复制
CREATE EXTENSION IFNOTEXISTS vector;    
    
-- 创建存储 16 维向量的表    
CREATETABLEIFNOTEXISTS vector_test (    
    idSERIAL PRIMARY KEY,    
    embedding vector(16),    
    is_center BOOLEAN-- 标记是否为中心点,方便后续查询    
);    

2. 核心函数逻辑

我们需要利用 PostgreSQL 的内置函数 random()。为了实现“离中心点越近概率越高”,最科学的方法是使用Box-Muller 变换来生成符合 正态分布(Normal Distribution) 的随机数。

编写函数:generate_vector_data
代码语言:javascript
复制
CREATE ORREPLACEFUNCTION generate_vector_data(    
    center_count INT,     -- 中心点数量 (例如 1000)    
    points_per_center INT-- 每个中心点周围生成的点数 (例如 2, 总计 2000)    
) RETURNSTEXTAS $$    
DECLARE    
    center_id INT;    
    center_vec FLOAT8[];    
    new_vec FLOAT8[];    
    std_dev FLOAT8 := 0.1; -- 标准差,值越小点越密集,越大离散度越高    
    i INT;    
    j INT;    
    u1 FLOAT8;    
    u2 FLOAT8;    
    z0 FLOAT8;    
BEGIN    
    -- 1. 生成原始的离散随机中心点    
    FOR i IN1..center_count LOOP    
        center_vec := ARRAY(SELECT random() FROM generate_series(1, 16));    
        INSERTINTO vector_test (embedding, is_center)     
        VALUES (center_vec::vector, TRUE)    
        RETURNINGidINTO center_id;    
    
        -- 2. 以该中心点为基准,生成服从正态分布的周边点    
        FOR j IN 1..points_per_center LOOP    
            new_vec := '{}';    
            -- 对 16 个维度分别进行高斯扰动    
            FOR k IN 1..16 LOOP    
                -- Box-Muller 变换生成正态分布随机数    
                u1 := random();    
                u2 := random();    
                -- 核心公式:z0 是均值为0,标准差为1的随机数    
                z0 := sqrt(-2.0 * ln(u1)) * cos(2.0 * pi() * u2);    
                    
                -- 将扰动叠加到中心点的对应维度上    
                new_vec := array_append(new_vec, center_vec[k] + (z0 * std_dev));    
            ENDLOOP;    
                
            INSERTINTO vector_test (embedding, is_center)     
            VALUES (new_vec::vector, FALSE);    
        ENDLOOP;    
    ENDLOOP;    
    
    RETURN 'Successfully generated ' || (center_count + center_count * points_per_center) || ' vectors.';    
END;    
$$ LANGUAGE plpgsql;    

3. 如何使用

执行该函数生成数据:

代码语言:javascript
复制
-- 生成 1000 个中心点,每个中心点生成 2 个周边点(共 1000 + 2000 = 3000 条数据)    
SELECT generate_vector_data(1000, 2);    

验证数据分布:

代码语言:javascript
复制
-- 查看数据总量    
SELECT is_center, count(*) FROM vector_test GROUP BY is_center;    
    
-- 查询距离某个中心点最近的 5 个点,看看是不是刚才生成的“追随点”    
SELECT * FROM vector_test     
ORDER BY embedding <=> (SELECT embedding FROM vector_test WHERE is_center = true LIMIT 1)     
LIMIT 5;    

💡 关键点解析

  • 概率分布模型:代码中使用了正态分布(高斯分布)。正态分布的特性是数值出现在均值(这里是中心点维度)附近的概率最大,越往外概率呈指数级下降,完美符合你“近处多、远处少”的需求。
  • Box-Muller 变换:这是在计算机中通过两个均匀分布的随机数生成正态分布随机数的标准算法。
  • 控制离散度:变量 std_dev(标准差)控制了点的“紧凑程度”。
    • 如果 std_dev = 0.01,点会非常贴近中心点。
    • 如果 std_dev = 0.5,点会显得非常散乱。

加餐, 标准差详解

很多初学者对标准差的概念不理解,我们可以用一个 “射击/打靶” 的比喻让你瞬间秒懂:

1. 标准差 ( ) 是你的“准星精度”(尺子)

想象你在打靶,靶心就是平均值(Mean)

  • 标准差不是百分比,而是你偏移靶心的平均物理距离(比如 1 厘米、5 厘米)。
  • 它是一把特制的尺子。每个数据集的尺子长短不一样。有的班级考试分数波动大,它的“尺子”一格就是 20 分;有的波动小,一格就是 2 分。

2. 那 68%、95%、99.7% 是什么?

这些百分比是 “覆盖率” ,也就是你“子弹落在这个范围内”的概率

  • 1 个标准差( ): 相当于你拿着那把“尺子”,从靶心向左、向右各量出 1 格的距离。在这个圈圈里,你会发现你 68% 的子弹都落在里面了。
  • 2 个标准差( ): 从靶心向左、向右各量出 2 格。这个圈变大了,你能圈住 95% 的子弹。
  • 3 个标准差( ): 量出 3 格。这几乎覆盖了整个靶子,99.7% 的子弹都在这了。剩下的 0.3% 就是所谓的“奇葩”或“极端异常值”。

3. 用前面提到的 16 维向量举例

假设你生成向量时,某一个维度的平均值是 0.5 (例子中使用random()函数生成的数据, 平均值就是0.5, 因为random()返回的是0到1之间随机分布的值.),你设置的标准差 std_dev0.1

  • 这把尺子的 1 格 = 0.1。
  • 68% 的概率: 你生成的数值会落在 之间( )。
  • 95% 的概率: 你生成的数值会落在 之间( )。
  • 99.7% 的概率: 你生成的数值会落在 之间( )。

💡 总结对比表

概念

到底是什么

形象理解

平均值 (Mean)

数据的中心点

靶心

标准差 ( )

单位长度

尺子上的“一格”有多长

68% / 95%...

概率/面积

这个圈里能装下多少个数据点

所以,当你看到“3个标准差”时,把它想成“3倍距离”,而不是“3%”。

在工业界(比如“六西格玛”管理),追求的就是 的精度,意味着 100 万个产品里只有 3.4 个次品,这就是把“圈子”拉得极大,让错误发生的概率变得极小。


人工修改

我故意留了个维度/标准差可调, 总要东东过脑子, 否则全让AI嘎了, 一点意思都没有.

1、维度可调整, 标准差可调整

代码语言:javascript
复制
CREATE ORREPLACEFUNCTION generate_vector_data(    
    center_count INT,     -- 中心点数量 (例如 1000)    
    points_per_center INT, -- 每个中心点周围生成的点数 (例如 2, 总计 2000)    
    dims int,  -- 维度    
    i_std_dev float8 default0.1-- 标准差,值越小点越密集,越大离散度越高    
) RETURNSTEXTAS $$    
DECLARE    
    center_id INT;    
    center_vec FLOAT8[];    
    new_vec FLOAT8[];    
    std_dev FLOAT8 := i_std_dev; -- 标准差,值越小点越密集,越大离散度越高    
    i INT;    
    j INT;    
    u1 FLOAT8;    
    u2 FLOAT8;    
    z0 FLOAT8;    
BEGIN    
    -- 1. 生成原始的离散随机中心点    
    FOR i IN1..center_count LOOP    
        center_vec := ARRAY(SELECT random() FROM generate_series(1, dims));    
        INSERTINTO vector_test (embedding, is_center)     
        VALUES (center_vec::vector, TRUE)    
        RETURNINGidINTO center_id;    
    
        -- 2. 以该中心点为基准,生成服从正态分布的周边点    
        FOR j IN 1..points_per_center LOOP    
            new_vec := '{}';    
            -- 对每个维度分别进行高斯扰动    
            FOR k IN 1..dims LOOP    
                -- Box-Muller 变换生成正态分布随机数    
                u1 := random();    
                u2 := random();    
                -- 核心公式:z0 是均值为0,标准差为1的随机数    
                z0 := sqrt(-2.0 * ln(u1)) * cos(2.0 * pi() * u2);    
                    
                -- 将扰动叠加到中心点的对应维度上    
                new_vec := array_append(new_vec, center_vec[k] + (z0 * std_dev));    
            ENDLOOP;    
                
            INSERTINTO vector_test (embedding, is_center)     
            VALUES (new_vec::vector, FALSE);    
        ENDLOOP;    
    ENDLOOP;    
    
    RETURN 'Successfully generated ' || (center_count + center_count * points_per_center) || ' vectors.';    
END;    
$$ LANGUAGE plpgsql;    

创建插件和测试表

代码语言:javascript
复制
CREATE EXTENSION IFNOTEXISTS vector;    
    
-- 创建存储 8 维向量的表    
CREATETABLEIFNOTEXISTS vector_test (    
    idSERIAL PRIMARY KEY,    
    embedding vector(8),    
    is_center BOOLEAN-- 标记是否为中心点,方便后续查询    
);    

接下来 pgbench 登场

2、并行插入

pgbench 并行写入脚本 1.sql

代码语言:javascript
复制
SELECT generate_vector_data(:center_count, :points_per_center, :dims, :i_std_dev);    

例如, 开10个并行, 每个负责300个中心点, 共3000个中心点. 每个中心点周围100条记录. 一共30万条记录.

代码语言:javascript
复制
pgbench -M prepared -n -r -f 1.sql -D center_count=300 -D points_per_center=100 -D dims=8 -D i_std_dev=0.1 -c 10 -j 10 -t 1    

返回结果类似这样

代码语言:javascript
复制
pgbench (18.1 (Debian 18.1-1.pgdg12+2))    
transaction type: 1.sql    
scaling factor: 1    
query mode: prepared    
number of clients: 10    
number of threads: 10    
maximum number of tries: 1    
number of transactions per client: 1    
number of transactions actually processed: 10/10    
number of failed transactions: 0 (0.000%)    
latency average = 1194.165 ms    
initial connection time = 6.721 ms    
tps = 8.374052 (without initial connection time)    
statement latencies in milliseconds and failures:    
      1173.833           0 SELECT generate_vector_data(:center_count, :points_per_center, :dims, :i_std_dev);    

数据样本如下

代码语言:javascript
复制
postgres=# select * from vector_test limit 10;    
 id |                                         embedding                                          | is_center     
----+--------------------------------------------------------------------------------------------+-----------    
  1 | [0.49148703,0.6685635,0.1687298,0.45180625,0.9960631,0.4571475,0.0034939128,0.13861603]    | t    
  2 | [0.5323792,0.5659391,0.12151991,0.4286459,1.0758449,0.6219371,0.004077115,0.034979522]     | f    
  3 | [0.47398087,0.7543278,0.4328688,0.5329106,0.986699,0.43134648,-0.052911315,-0.08437831]    | f    
  4 | [0.64157313,0.600284,0.10245401,0.31865734,0.8698451,0.3226676,0.08320826,0.22981888]      | f    
  5 | [0.47341624,0.7114847,0.17963693,0.40590957,0.94509363,0.51783043,-0.018367931,0.2058961]  | f    
  6 | [0.4226046,0.74574995,0.030126082,0.44892085,0.9216737,0.6136254,0.047382228,0.07710782]   | f    
  7 | [0.44220188,0.7145569,0.26640612,0.5712415,0.9839118,0.4472589,0.09518536,0.104707085]     | f    
  8 | [0.43854782,0.73702663,0.13283436,0.55481553,0.96476346,0.3992915,-0.054782305,0.15327796] | f    
  9 | [0.59758496,0.687787,0.16371523,0.55891865,1.13443,0.5273249,0.040264796,0.10090528]       | f    
 10 | [0.6344181,0.44873625,0.16037185,0.49058086,1.0713346,0.23812866,-0.092897125,0.16163382]  | f    
(10 rows)    

3、索引创建

使用并行创建

代码语言:javascript
复制
set maintenance_work_mem ='1GB';    
set max_parallel_workers=4;    
set max_parallel_workers_per_gather =4;    
set parallel_leader_participation =off;    
set parallel_setup_cost =0;    
set parallel_tuple_cost =0;    
set min_parallel_index_scan_size =0;    
set min_parallel_table_scan_size =0;    
alter table vector_test set (parallel_workers =4);    
create index on vector_test using hnsw (embedding vector_ip_ops) with (m=16, ef_construction=64);    

注意: 不同ops对应的order by op不同.

4、查询性能

代码语言:javascript
复制
set enable_seqscan=off;    
postgres=# explain SELECT * FROM vector_test     
ORDER BY embedding <#> (SELECT embedding FROM vector_test WHERE id = 1)     
LIMIT 10;    
                                                   QUERY PLAN                                                       
----------------------------------------------------------------------------------------------------------------    
 Limit  (cost=142.56..144.77 rows=10 width=50)    
   InitPlan 1    
     ->  Index Scan using vector_test_pkey on vector_test vector_test_1  (cost=0.42..8.44 rows=1 width=37)    
           Index Cond: (id = 1)    
   ->  Index Scan using vector_test_embedding_idx on vector_test  (cost=134.12..133984.20 rows=607010 width=50)    
         Order By: (embedding <#> (InitPlan 1).col1)    
(6 rows)    

效果如下

代码语言:javascript
复制
postgres=# SELECT * FROM vector_test     
ORDER BY embedding <#> (SELECT embedding FROM vector_test WHERE id = 4000)     
LIMIT 10;    
   id   |                                      embedding                                       | is_center     
--------+--------------------------------------------------------------------------------------+-----------    
 406227 | [0.82450694,1.2055361,1.0173812,0.18316184,0.9584693,0.9418835,0.55990416,1.1273926] | f    
 201201 | [1.2767193,0.80783665,1.140189,0.8567347,-0.045661494,1.161669,0.92515004,0.9140229] | f    
 201944 | [1.1149577,0.7975786,1.1849692,0.9560142,0.20817277,1.0397915,1.1407607,0.9614646]   | f    
 489547 | [0.9052291,1.0715504,1.093908,0.5560693,0.69307494,1.1439734,0.86572605,0.7646792]   | f    
 200394 | [1.1373323,0.8256339,1.1670817,0.8769953,0.062202193,1.1290536,1.1055098,0.7922026]  | f    
 495815 | [0.7111623,1.0081569,0.9748361,0.20044677,0.53446907,1.0939667,0.6770205,1.2081655]  | f    
 295982 | [1.1235836,0.93014675,1.0391827,0.6902082,0.61107963,0.8564477,0.3975433,1.0953082]  | f    
 406741 | [0.77345675,1.1682146,1.1521189,0.3500558,0.9883157,0.794485,0.49730656,1.0099597]   | f    
 221776 | [1.1425776,0.89666414,0.6612843,0.7402642,0.8535777,0.975557,0.9979798,1.0486325]    | f    
 489642 | [0.8964323,1.1836767,0.8508141,0.70250076,0.7395261,1.2460623,0.9538984,0.67182827]  | f    
(10 rows)    
    
Time: 2.982 ms    

再次升级需求

代码语言:javascript
复制
> 我要在PostgreSQL中创建一个存储向量的表, 写一个PostgreSQL函数, 生成测试数据, 需求如下:    
每个维度的取值区间为[0,1), 经过扰动得到的值也必须落在这个区间, 如果不能落在这个区间, 截取为上界或下界.    
16维(可变量输入).    
生成1000个(可变量输入)完全离散随机的向量.    
然后再以这1000个向量为中心, 生成2000个(可变量输入)离散向量.    
每个中心点周围数据的聚集性用标准差(通过下面的方法计算得出, 均值+扰动, 且必须为正值)来表示, 离中心点距离越近出现概率越高, 越远概率越低.     
每个中心点聚集起来的数据看作一类, 不同类的聚集性强弱也呈现正态分布. 代表聚集强弱的标准差的均值可变量输入(例如 0.1), 分布强弱可变量输入(例如0.01);    
    
下面是模板, 基于它修改    
    
```sql    
CREATE OR REPLACE FUNCTION generate_vector_data(    
    center_count INT,     -- 中心点数量 (例如 1000)    
    points_per_center INT, -- 每个中心点周围生成的点数 (例如 2, 总计 2000)    
    dims int,  -- 维度    
    i_std_dev float8 default 0.1  -- 标准差,值越小点越密集,越大离散度越高    
) RETURNS TEXT AS $$    
DECLARE    
    center_id INT;    
    center_vec FLOAT8[];    
    new_vec FLOAT8[];    
    std_dev FLOAT8 := i_std_dev; -- 标准差,值越小点越密集,越大离散度越高    
    i INT;    
    j INT;    
    u1 FLOAT8;    
    u2 FLOAT8;    
    z0 FLOAT8;    
BEGIN    
    -- 1. 生成原始的离散随机中心点    
    FOR i IN 1..center_count LOOP    
        center_vec := ARRAY(SELECT random() FROM generate_series(1, dims));    
        INSERT INTO vector_test (embedding, is_center)     
        VALUES (center_vec::vector, TRUE)    
        RETURNING id INTO center_id;    
    
        -- 2. 以该中心点为基准,生成服从正态分布的周边点    
        FOR j IN 1..points_per_center LOOP    
            new_vec := '{}';    
            -- 对每个维度分别进行高斯扰动    
            FOR k IN 1..dims LOOP    
                -- Box-Muller 变换生成正态分布随机数    
                u1 := random();    
                u2 := random();    
                -- 核心公式:z0 是均值为0,标准差为1的随机数    
                z0 := sqrt(-2.0 * ln(u1)) * cos(2.0 * pi() * u2);    
                    
                -- 将扰动叠加到中心点的对应维度上    
                new_vec := array_append(new_vec, center_vec[k] + (z0 * std_dev));    
            END LOOP;    
                
            INSERT INTO vector_test (embedding, is_center)     
            VALUES (new_vec::vector, FALSE);    
        END LOOP;    
    END LOOP;    
    
    RETURN 'Successfully generated ' || (center_count + center_count * points_per_center) || ' vectors.';    
END;    
$$ LANGUAGE plpgsql;    
```    

AI 又一把过了:

你的需求涉及以下几个关键点:

  1. 向量维度可变(默认16)。
  2. 每个中心点完全离散随机(取值 ∈ [0,1))。
  3. 围绕每个中心点生成聚集点
    • 使用正态分布扰动(均值=中心点值,标准差=某个类内标准差)。
    • 扰动后值必须 ∈ [0,1),否则截断到 [0,1)。
  4. 每个中心点的类内标准差本身服从正态分布
    • 均值 = std_dev_mean(如 0.1)
    • 标准差 = std_dev_sigma(如 0.01)
    • 且标准差必须为正(截断到 > 0)。
  5. 支持 PostgreSQL vector 类型(需安装 pgvector 扩展)。

✅ 修改说明

  • 修改函数签名增加 std_dev_meanstd_dev_sigma
  • 在每轮生成中心点时,为该中心点生成独立的类内标准差local_std_dev
  • 对每个扰动后的维度值做 [0,1) 截断
  • 使用 GREATEST(0, LEAST(1 - 1e-9, value)) 确保严格在 [0,1)。
  • 提前检查 vector 类型是否存在(否则会报错)。

✅ 修改后的完整函数

代码语言:javascript
复制
-- 确保已安装 pgvector 扩展(如未安装,请先执行:CREATE EXTENSION IF NOT EXISTS vector;)    
-- 表结构示例(请先创建):    
-- CREATE TABLE vector_test (    
--     id SERIAL PRIMARY KEY,    
--     embedding VECTOR,    
--     is_center BOOLEAN,    
--     center_id INT    
-- );    
    
CREATEORREPLACEFUNCTION generate_vector_data(    
    center_count INT,          -- 中心点数量 (例如 1000)    
    points_per_center INT,     -- 每个中心点周围生成的点数 (例如 2 → 总计 2000)    
    dims INT,                  -- 向量维度(例如 16)    
    std_dev_mean FLOAT8 DEFAULT0.1,    -- 类内标准差的均值    
    std_dev_sigma FLOAT8 DEFAULT0.01   -- 类内标准差的分布标准差(即“聚集性强弱”的波动)    
) RETURNSTEXTAS $$    
DECLARE    
    center_id INT;    
    center_vec FLOAT8[];    
    new_vec FLOAT8[];    
    local_std_dev FLOAT8;      -- 每个中心点自己的类内标准差    
    perturbed_val FLOAT8;    
    i INT;    
    j INT;    
    k INT;    
    u1 FLOAT8;    
    u2 FLOAT8;    
    z0 FLOAT8;    
    z_std FLOAT8;              -- 用于生成 local_std_dev 的正态扰动    
BEGIN    
    -- 1. 生成 center_count 个完全离散随机的中心点    
    FOR i IN1..center_count LOOP    
        -- 生成 d 维 [0,1) 随机向量作为中心    
        center_vec := ARRAY(SELECT random() FROM generate_series(1, dims));    
    
        -- 为当前中心点生成其“类内标准差”:N(std_dev_mean, std_dev_sigma),但必须 > 0    
        LOOP    
            u1 := random();    
            u2 := random();    
            -- 避免除零    
            IF u1 = 0 THEN u1 := 1e-10; ENDIF;    
            z_std := sqrt(-2.0 * ln(u1)) * cos(2.0 * pi() * u2);    
            local_std_dev := std_dev_mean + z_std * std_dev_sigma;    
            EXIT WHEN local_std_dev > 0;  -- 确保标准差为正    
        ENDLOOP;    
    
        -- 插入中心点    
        INSERTINTO vector_test (embedding, is_center)    
        VALUES (center_vec::vector, TRUE)    
        RETURNINGidINTO center_id;    
    
        -- 2. 围绕该中心点生成 points_per_center 个聚集点    
        FOR j IN 1..points_per_center LOOP    
            new_vec := '{}';    
            FOR k IN 1..dims LOOP    
                -- 生成标准正态扰动    
                u1 := random();    
                u2 := random();    
                IF u1 = 0 THEN u1 := 1e-10; ENDIF;    
                z0 := sqrt(-2.0 * ln(u1)) * cos(2.0 * pi() * u2);    
    
                -- 扰动 = 中心值 + N(0, local_std_dev)    
                perturbed_val := center_vec[k] + z0 * local_std_dev;    
    
                -- 截断到 [0, 1)    
                -- 使用 1 - 1e-9 避免等于 1(因 random() 本身 <1,但扰动可能 >=1)    
                perturbed_val := GREATEST(0.0, LEAST(1.0 - 1e-9, perturbed_val));    
    
                new_vec := array_append(new_vec, perturbed_val);    
            ENDLOOP;    
    
            -- 插入非中心点,并可选记录 center_id(便于后续分析)    
            INSERTINTO vector_test (embedding, is_center, center_id)    
            VALUES (new_vec::vector, FALSE, center_id);    
        ENDLOOP;    
    ENDLOOP;    
    
    RETURN 'Successfully generated ' ||    
           (center_count + center_count * points_per_center)::TEXT ||    
           ' vectors (' || center_count || ' centers + ' ||    
           (center_count * points_per_center) || ' neighbors).';    
END;    
$$ LANGUAGE plpgsql;    

✅ 使用示例

代码语言:javascript
复制
-- 生成 1000 个中心点,每个中心生成 2 个邻居(共 3000 条记录)    
-- 16 维,类内标准差均值=0.1,波动=0.01    
SELECT generate_vector_data(    
    center_count => 1000,    
    points_per_center => 2,    
    dims => 16,    
    std_dev_mean => 0.1,    
    std_dev_sigma => 0.01    
);    

✅ 注意事项

pgvector 扩展必须安装

代码语言:javascript
复制
CREATE EXTENSION IF NOT EXISTS vector;    

表结构需包含 center_id 字段(用于追踪每个点属于哪个中心):

代码语言:javascript
复制
CREATE TABLE vector_test (    
    id SERIAL PRIMARY KEY,    
    embedding VECTOR,    
    is_center BOOLEAN,    
    center_id INT    
);    

性能提示:生成 3000 条记录很快,但若数量极大(如 100 万),建议分批或使用更高效方法(如 generate_series + CROSS JOIN LATERAL),但当前函数结构清晰,适合中小规模测试数据。

截断处理:使用 1 - 1e-9 确保最终值严格小于 1,符合 [0,1) 要求。


重新插入压测

假设以上函数和vector插件都已安装.

1、建表

代码语言:javascript
复制
  drop table if exists vector_test;    
    
-- 创建存储 8 维向量的表    
   CREATE TABLE vector_test (    
       id SERIAL PRIMARY KEY,    
       embedding VECTOR(8),    
       is_center BOOLEAN,    
       center_id INT    
   );    

接下来 pgbench 登场

2、并行插入

pgbench 并行写入脚本 1.sql

代码语言:javascript
复制
SELECT generate_vector_data(    
    :center_count,    
    :points_per_center,    
    :dims,    
    :std_dev_mean,    
    :std_dev_sigma    
);    

例如, 开10个并行, 每个负责300个中心点, 共3000个中心点. 每个中心点周围100条记录. 一共30万条记录.

代码语言:javascript
复制
pgbench -M prepared -n -r -f 1.sql -D center_count=300 -D points_per_center=100 -D dims=8 -D std_dev_mean=0.1 -D std_dev_sigma=0.01 -c 10 -j 10 -t 1    

返回结果类似这样, 又是1秒, 完成30万写入.

代码语言:javascript
复制
pgbench (18.1 (Debian 18.1-1.pgdg12+2))    
transaction type: 1.sql    
scaling factor: 1    
query mode: prepared    
number of clients: 10    
number of threads: 10    
maximum number of tries: 1    
number of transactions per client: 1    
number of transactions actually processed: 10/10    
number of failed transactions: 0 (0.000%)    
latency average = 1275.373 ms    
initial connection time = 7.123 ms    
tps = 7.840843 (without initial connection time)    
statement latencies in milliseconds and failures:    
      1254.682           0 SELECT generate_vector_data(    

数据样本如下

代码语言:javascript
复制
postgres=# select * from vector_test limit 10;    
 id |                                         embedding                                          | is_center | center_id     
----+--------------------------------------------------------------------------------------------+-----------+-----------    
  1 | [0.17105481,0.111848384,0.9117258,0.5306894,0.11807599,0.14844997,0.46155128,0.30024847]   | t         |              
 11 | [0.03813838,0.16296498,0.8372399,0.54524916,0.028416384,0.12848008,0.32547304,0.1947173]   | f         |         1    
 12 | [0,0.044231616,1,0.58943826,0.22524321,0,0.41637287,0.48733392]                            | f         |         1    
 13 | [0.18737471,0,0.9368977,0.54700387,0,0.18523008,0.43912503,0.40527853]                     | f         |         1    
 14 | [0.076122984,0.07140395,0.9845732,0.42619815,0.05465484,0.45531052,0.34961346,0.31409928]  | f         |         1    
 15 | [0.11690144,0.0030454656,0.89362085,0.4849689,0.2529286,0.19872168,0.48759928,0.39344427]  | f         |         1    
 17 | [0.090135045,0.19393651,1,0.62053615,0.1905218,0.005123274,0.5673879,0.24511307]           | f         |         1    
 19 | [0.25191787,0.29603916,0.914537,0.5292521,0,0.10356969,0.4150836,0.2541691]                | f         |         1    
 20 | [0.22760004,0.22169718,1,0.3778331,0,0.21047452,0.29598284,0.29152873]                     | f         |         1    
 22 | [0.013735059,0.12628518,0.7749661,0.44492537,0.13536552,0.065305255,0.48517403,0.25436395] | f         |         1    
(10 rows)    

3、索引创建

使用并行创建

代码语言:javascript
复制
set maintenance_work_mem ='1GB';    
set max_parallel_workers=4;    
set max_parallel_workers_per_gather =4;    
set parallel_leader_participation =off;    
set parallel_setup_cost =0;    
set parallel_tuple_cost =0;    
set min_parallel_index_scan_size =0;    
set min_parallel_table_scan_size =0;    
alter table vector_test set (parallel_workers =4);    
create index on vector_test using hnsw (embedding vector_l2_ops) with (m=16, ef_construction=64);    

注意: 不同ops对应的order by op不同.

4、查询性能

代码语言:javascript
复制
set enable_seqscan=off;    
explainSELECT * FROM vector_test     
ORDERBY embedding <-> (SELECT embedding FROM vector_test WHEREid = 11)     
LIMIT10;    
    
                                                  QUERY PLAN                                                       
---------------------------------------------------------------------------------------------------------------    
 Limit  (cost=136.12..138.32 rows=10 width=54)    
   InitPlan 1    
     ->  Index Scan using vector_test_pkey on vector_test vector_test_1  (cost=0.42..8.44 rows=1 width=37)    
           Index Cond: (id = 11)    
   ->  Index Scan using vector_test_embedding_idx on vector_test  (cost=127.68..66880.00 rows=303000 width=54)    
         Order By: (embedding <-> (InitPlan 1).col1)    
(6 rows)    

效果如下, 符合预期, 有一些外围的点可能离其他中心点更近, 例如11明显离3057中心点的值更近.

代码语言:javascript
复制
SELECT * FROM vector_test     
ORDERBY embedding <-> (SELECT embedding FROM vector_test WHEREid = 11)     
LIMIT10;    
    
  id  |                                         embedding                                          | is_center | center_id     
------+--------------------------------------------------------------------------------------------+-----------+-----------    
   11 | [0.03813838,0.16296498,0.8372399,0.54524916,0.028416384,0.12848008,0.32547304,0.1947173]   | f         |         1    
 3196 | [0,0.09366788,0.836875,0.58805025,0,0.21278407,0.35584888,0.20035924]                      | f         |      3057    
 3307 | [0.04267441,0.15442768,0.78668106,0.651847,0,0.19336444,0.26290196,0.24046999]             | f         |      3057    
  216 | [0.11530078,0.17141855,0.97688466,0.53638417,0.05041965,0.078720815,0.3156914,0.19801213]  | f         |         1    
 3335 | [0.047988825,0.11678319,0.83546025,0.65561557,0.13624333,0.12582546,0.27382517,0.27172136] | f         |      3057    
 3321 | [0,0.14111617,0.85898143,0.71950036,0.04036658,0.17692095,0.32247683,0.1792223]            | f         |      3057    
 3107 | [0.057837497,0.03539711,0.82344323,0.6507534,0,0.052208096,0.2913429,0.18517697]           | f         |      3057    
 3475 | [0.09501218,0.2220129,0.81840885,0.61479205,0.15981476,0.18752795,0.38180515,0.18915245]   | f         |      3057    
 3327 | [0.13987818,0.21751888,0.702564,0.58499944,0,0.19195952,0.33919933,0.1812846]              | f         |      3057    
 3083 | [0.035972517,0.28071767,0.77004033,0.61820525,0.15739723,0.12367443,0.3413193,0.2037563]   | f         |      3057    
(10 rows)    
    
    
SELECT * FROM vector_test     
ORDERBY embedding <-> (SELECT embedding FROM vector_test WHEREid = 1)     
LIMIT10;    
 id  |                                         embedding                                         | is_center | center_id     
-----+-------------------------------------------------------------------------------------------+-----------+-----------    
   1 | [0.17105481,0.111848384,0.9117258,0.5306894,0.11807599,0.14844997,0.46155128,0.30024847]  | t         |              
 193 | [0.16777094,0.1642298,0.901199,0.44624943,0.12807207,0.16980569,0.4750887,0.31381938]     | f         |         1    
 166 | [0.120463744,0.07280951,0.89771175,0.43667313,0.055246145,0.1371449,0.46776408,0.3631755] | f         |         1    
 152 | [0.2367722,0.14152765,1,0.5200618,0.1269453,0.085524425,0.40181518,0.2423237]             | f         |         1    
 158 | [0.14158644,0.22342859,0.955126,0.6240514,0.14849429,0.114813514,0.4535238,0.33608654]    | f         |         1    
 148 | [0.25239626,0.1525642,0.8130709,0.5330656,0.19565046,0.1114626,0.42059937,0.27454346]     | f         |         1    
 163 | [0.23277406,0.093285546,0.9140603,0.60804856,0.11022406,0.06361267,0.522467,0.38165015]   | f         |         1    
 199 | [0.24088658,0.25373772,0.88065875,0.52698815,0.06798484,0.13525315,0.4353099,0.28811255]  | f         |         1    
 188 | [0.18140465,0,1,0.5817445,0.13569641,0.15472418,0.5248301,0.352165]                       | f         |         1    
 165 | [0.13854088,0.083053984,1,0.5690327,0,0.07870037,0.4946139,0.31971732]                    | f         |         1    
(10 rows)    
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 digoal德哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 瞧不上AI的, 等着被时代抛弃吧
  • 来看看效果吧:AI 抢答
    • 1. 环境准备
    • 2. 核心函数逻辑
      • 编写函数:generate_vector_data
    • 3. 如何使用
    • 💡 关键点解析
  • 加餐, 标准差详解
    • 1. 标准差 ( ) 是你的“准星精度”(尺子)
    • 2. 那 68%、95%、99.7% 是什么?
    • 3. 用前面提到的 16 维向量举例
    • 💡 总结对比表
  • 人工修改
  • 再次升级需求
    • ✅ 修改说明
    • ✅ 修改后的完整函数
    • ✅ 使用示例
    • ✅ 注意事项
  • 重新插入压测
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档