借助 EMR Serverless StarRocks 实现电商平台图搜图、文搜图场景
åå©Â EMR Serverless StarRocks å®ç°çµåå¹³å°å¾æå¾ãææå¾åºæ¯
é¿éäºå¤§æ°æ®AIææ¯ 2026-08-14 0 é 读8åéä¸ãèæ¯
çµåæç´¢è¿äºå¹´èµ°è¿ä¸ä¸ªé¶æ®µï¼å ³é®è¯æç´¢ãè¯ä¹æç´¢ã夿¨¡ææç´¢ãä»å¤©ç¨æ·çæå¾ å·²ç»å¾ç´æ¥ââæä¸å¼ å¾å°±è¦æ¾å°åæ¬¾ï¼æä¸å¥"ç±³ç½è²å®½æ¾æ£éº»è¡¬è¡«"å°±è¦åºå¯¹çè´§ã
è¿èåæ¯ä¸¤ä¸ªé«é¢åºæ¯ã
-
徿å¾ï¼ç¨æ·ä¸ä¼ æææä¸å¼ ååå¾ï¼ç³»ç»æ¾åºå款ä¸ç¸ä¼¼æ¬¾ï¼æ¯ææç §è´ç©ãæ¯ä»·è´ç©ã以徿¾åº
-
ææå¾ï¼ç¨æ·è¾å ¥ä¸å¥èªç¶è¯è¨æè¿°ï¼ç³»ç»ç´æ¥å¹é 符åæè¿°çåå主å¾ï¼å³ä½¿ååæ é¢éæ ¹æ¬æ²¡æ"æ£éº»""宽æ¾"è¿äºè¯ã
é®é¢å¨äºï¼è¿ä¸¤ä»¶äºä¼ ç»ä¸é½å¾é¾è½å¨åä¸å¥ç³»ç»éã
-
åå䏻徿å åä¸å¼ ãåå¨Â OSSï¼
-
åéè¦å¦å»ºä¸å¥åéåºï¼
-
æç´¢ç»æåå¿ é¡»åä»·æ ¼ãåºåãç±»ç®ãè¯åè¿äºä¸å¡å±æ§èåè¿æ»¤ââ"æ¾ç¸ä¼¼æ¬¾"没ææä¹ï¼"æ¾æè´§ã300 å 以å ãè¯å 4 å以ä¸çç¸ä¼¼æ¬¾"æææä¹ã
äºæ¯å¢éç»´æ¤ä¸¤å¥ç³»ç»ãå两å¥é¾è·¯ï¼åéåºé没æä¸å¡å段ï¼OLAP åºé没æåéï¼ä¸é´é åºç¨å±æ¥åææ°æ®æ¼ç»æãå忢äºä¸»å¾ï¼åéæ´æ°å»¶è¿å å°æ¶ï¼æç´¢åºæ¥è¿æ¯æ§æ¬¾ã
äºãæ´ä½ææ¯æ¶æ
åºäºÂ EMR Serverless StarRocksï¼ç¨Â StarRocks AI Function å¨åºå ç´æ¥è°ç¨å¤æ¨¡ææ¨¡åï¼æ"OSS å¾ç â åé âÂ æ··åæ£ç´¢Â â ä¸å¡è¿æ»¤ä¸æåº"æ´æ¡é¾è·¯æ¶æææ å SQLãå¾çåå¾ä¸åºÂ OSSï¼ç¨Â Object Table æè½½å¢éæä»¶ï¼åéä¸ååä¸å¡å±æ§ååºåå¨ï¼åéæ£ç´¢ä¸ç»æåè¿æ»¤å¨ä¸æ¡Â SQL éåçèåã
æ ¸å¿è½åæï¼
-
ai_embed_multimodal(file, 'image') æåå主å¾ç¼ç æåéï¼ -
ai_embed_multimodal(text, 'text') æç¨æ·çèªç¶è¯è¨æ¥è¯¢ç¼ç å°åä¸åé空é´ââè¿æ¯ææå¾è½è·¨æ¨¡æå½ä¸å¾çåéçå ³é®ï¼ -
approx_cosine_similarity å¨åéç´¢å¼ä¸å ANN æ£ç´¢ï¼é åÂWHERE éçä»·æ ¼ãåºåãç±»ç®æ¡ä»¶ï¼ä¸æ¡Â SQLÂ åºæç»ç»æã
æ¯æçç®æ³ä¸ç´¢å¼éå
| æ°æ®æº | ç´¢å¼ç±»å | è·ç¦»åº¦é | å ¸åç¹ç¹ | æ¨èåºæ¯ |
|---|---|---|---|---|
| StarRocks å 表 | HNSW | L2ãä½å¼¦ç¸ä¼¼åº¦ | ä½å»¶è¿ãé«å¬åï¼ç´¢å¼ä½ç§¯åå åå ç¨ç¸å¯¹è¾é« | 线ä¸ä¸»æãç§çº§ååºç徿å¾/ææå¾ |
| StarRocks å 表 | IVFPQ | L2ãä½å¼¦ç¸ä¼¼åº¦ | éè¿èç±»åä¹ç§¯éåéä½ç´¢å¼ä½ç§¯ï¼é常éè¦ç²¾æä»¥æé«æç»ç²¾åº¦ | 亿级 SKUãææ¬ä¸å åææ |
| DLF Paimon æ¹è¡¨ | Lumina Global Index | L2ãä½å¼¦ç¸ä¼¼åº¦ãå 积 | æ°æ®ä¿ç卿¹è¡¨ï¼æ¯æåçå¹¶è¡å¬ååç»æå Global Index é¢è¿æ»¤ | å ¨éååèµäº§çæ¹ãå¤å¼æå ±äº«ã离线大æ¹éå¬å |
ç¨æ·ä¾§
âââ ä¸ä¼ å¾çï¼å¾æå¾ï¼
âââ è¾å
¥æåæè¿°ï¼ææå¾ï¼
â
â¼
åºç¨æå¡å±
âââ å¾çé¢å¤çï¼è£åª / å½ä¸åï¼
âââ è°ç¨ StarRocks AI Function 宿¶åéå
â
â¼
StarRocks Stella 2.2
âââ åå夿¨¡æè¡¨ï¼SKU ä¿¡æ¯ + å¾ååé + ææ¬åéï¼
âââ ANN åéç´¢å¼ï¼HNSW / IVFï¼
âââ AI Functionï¼AI_EMBED(image / text, model)
â
â¼
ç»æå¤çå±
âââ Top-K ç¸ä¼¼ååå表
âââ èåä¸å¡è§åè¿æ»¤ï¼ä»·æ ¼åºé´ / åºå / ç±»ç®ï¼
âââ ç»ææåºä¸å±ç¤º
ä¸ãå®è·µæ¥éª¤
ç¬¬ä¸æ¥ï¼ç¨Â Object Table æè½½Â OSS ååå¾ç
ä¸»å¾æç±»ç®/å¹´æç®å½æ²æ·å¨Â OSSï¼å»ºÂ Object Table æå¯¹è±¡æ å°è¿æ¥ï¼file æè¿°ç¬¦ç´æ¥äº¤ç»Â AIÂ å½æ°ãç¾åå°åç±ç³»ç»èªå¨çæï¼SQL éä¸åºç°Â AK/SKã
SET CATALOG default_catalog;
CREATE DATABASE IF NOT EXISTS ec_search;
CREATE OBJECT TABLE ec_search.obj_product_images PROPERTIES (
"path" = "oss://<BUCKET>/product/images/",
"aliyun.oss.endpoint" = "oss-cn-hangzhou-internal.aliyuncs.com",
"aliyun.oss.public_endpoint" = "oss-cn-hangzhou.aliyuncs.com",
"aliyun.oss.access_key" = "<AK>",
"aliyun.oss.secret_key" = "<SK>",
"recursive" = "true",
"file_pattern" = ".*\.(jpg|jpeg|png|webp)$"
);
REFRESH OBJECT TABLE ec_search.obj_product_images;
Object Table åºå®Â 8 åï¼object_uri/etag/size/last_modified/content_type/storage_class/refresh_id/fileï¼ï¼å
¶ä¸Â etag æ¯åå¢éæ°´ä½çå
³é®ââ主å¾è¢«æ¿æ¢å etag ååï¼ä¼èªå¨è§¦åéæ°åéåã
ç¬¬äºæ¥ï¼å»ºåå夿¨¡æè¡¨å¹¶è§ååéç´¢å¼
线ä¸ä¸»æç¨å
表ï¼åéåå¿
é¡»æ¯Â NOT NULL ç ARRAY<FLOAT>ï¼è¡¨æ¨¡å为 Primary Key æÂ Duplicate Keyï¼
CREATE TABLE ec_search.product_image_search (
sku_id BIGINT NOT NULL,
spu_id BIGINT,
title VARCHAR(1024),
category VARCHAR(64),
brand VARCHAR(64),
price DECIMAL(10,2),
stock INT,
rating FLOAT,
sales_30d BIGINT,
object_uri VARCHAR(1024), -- å
³è OSS 主å¾
etag VARCHAR(256), -- 主å¾çæ¬ï¼åå¢éå¹ç
image_vector ARRAY<FLOAT> NOT NULL, -- 夿¨¡æå¾ååéï¼2560 ç»´
updated_at DATETIME,
INDEX idx_image_vec (image_vector) USING VECTOR (
"index_type" = "hnsw",
"dim" = "2560",
"metric_type" = "cosine_similarity",
"is_vector_normed" = "false",
"M" = "16",
"efconstruction" = "200"
)
) PRIMARY KEY (sku_id)
DISTRIBUTED BY HASH(sku_id) BUCKETS 32;
ææ£ç´¢è·¯å¾å表ï¼product_image_search æ¿è½½å¾ååéï¼æå¡å¾æå¾Â + ææå¾ï¼å ä¸ºææ¬æ¥è¯¢åéä¸å¾åå空é´ï¼ï¼å¦å»ºÂ product_text_search ç¨Â ai_embed ç 1024Â ç»´ææ¬åéæå¡"æææ"ã
è¥å ¨éåååéè¦ç卿¹ä¸ä¾å¤å¼æå ±äº«ï¼åè½Â DLF Paimon 表ã
-- StarRocks å¯åï¼parquet + partial-updateï¼ä¾¿äºåªåååéæä¸å¡å
CREATE TABLE dlf_catalog.`default`.product_vectors (
sku_id BIGINT, object_uri STRING, etag STRING,
category STRING, brand STRING, price DECIMAL(10,2), stock INT,
image_vector ARRAY<FLOAT>, updated_at TIMESTAMP
) ENGINE = paimon
PROPERTIES ("file.format"="parquet","primary-key"="sku_id",
"merge-engine"="partial-update","bucket"="4");
ç¬¬ä¸æ¥ï¼æ¹éåéåå ¥åºï¼å»éå¨Â AIÂ å½æ°ä¹å
æÂ Object Table ç file 交ç»Â ai_embed_multimodalï¼å¹¶ç¨Â object_uri + etag å LEFT ANTI JOINÂ è¿æ»¤å·²å¤ç主å¾ï¼
INSERT INTO ec_search.product_image_search
(sku_id, spu_id, title, category, brand, price, stock, rating, sales_30d,
object_uri, etag, image_vector, updated_at)
SELECT
p.sku_id, p.spu_id, p.title, p.category, p.brand, p.price, p.stock, p.rating, p.sales_30d,
o.object_uri, o.etag,
ai_embed_multimodal(o.file, 'image') AS image_vector,
now()
FROM ec_search.obj_product_images o
JOIN ec_search.dim_product p
ON p.image_key = regexp_extract(o.object_uri, '.*/(.+)$', 1)
LEFT ANTI JOIN ec_search.product_image_search t
ON t.object_uri = o.object_uri AND t.etag = o.etag
WHERE object_file_is_image(o.file);
-- æ ¡éªï¼åéé空ã维度ä¸ç´¢å¼ dim ä¸è´
SELECT COUNT(*) AS total,
SUM(IF(image_vector IS NOT NULL AND array_length(image_vector)=2560,1,0)) AS valid_vec
FROM ec_search.product_image_search;
ç¬¬åæ¥ï¼å¾æå¾
å ³é®ç¹ï¼è¦å½ä¸åéç´¢å¼ï¼æ¥è¯¢åéå¿ é¡»æ¯ä¼åé¶æ®µå¯ç¡®å®ç叏鿰ç»ãæä»¥çº¿ä¸æ¯ä¸¤æ¥ââåºç¨å±å è°ä¸æ¬¡åéåæ¿å°æ°ç»ï¼åä½ä¸ºåé¢éæ¼è¿æ£ç´¢Â SQLï¼
-- â å
åæ¥è¯¢åéï¼ç¨æ·ä¸ä¼ å¾çå
¬è¯»æç¾å URLï¼
SELECT ai_embed_multimodal('https://<BUCKET>.oss-cn-hangzhou.aliyuncs.com/upload/q.jpg', 'image');
-- â¡ ç¨å¸¸éåéå ANN æ£ç´¢ + ä¸å¡æ¡ä»¶è¿æ»¤ï¼ä½å¼¦éåº + LIMITï¼
SELECT sku_id, title, category, price, rating, object_uri,
approx_cosine_similarity(image_vector, [0.0131, -0.0247, /* â¦2560 ç»´åé¢é⦠*/]) AS score
FROM ec_search.product_image_search
WHERE stock > 0
AND category = '女è£
'
AND price BETWEEN 100 AND 500
AND rating >= 4.0
ORDER BY score DESC
LIMIT 20;
ç¬¬äºæ¥ï¼ææå¾ï¼è·¨æ¨¡æï¼
ç¨æ·è¾å
¥çæåç¨åä¸ä¸ªå¤æ¨¡ææ¨¡åç¼ç æÂ 2560 维åéï¼ç´æ¥ä¸ååç image_vector æ¯ç¸ä¼¼åº¦ââè¿å°±æ¯è·¨æ¨¡ææ£ç´¢ï¼
-- â ææ¬ç¼ç å°ä¸å¾ååä¸ç©ºé´ï¼2560 ç»´ï¼
SELECT ai_embed_multimodal('ç±³ç½è²å®½æ¾æ£éº»è¡¬è¡«', 'text');
-- â¡ ç¨ææ¬åéæ£ç´¢å¾ååéï¼è·¨æ¨¡æï¼+ 屿§è¿æ»¤
SELECT sku_id, title, brand, price, object_uri,
approx_cosine_similarity(image_vector, [/* ä¸ä¸æ¥ç 2560 维常é */]) AS score
FROM ec_search.product_image_search
WHERE stock > 0
AND category IN ('衬衫','女士衬衫')
AND price < 2000
ORDER BY score DESC
LIMIT 20;
妿è¦å"æææ"ï¼ææ é¢ã屿§æè¿°å忍¡æè¯ä¹æç´¢ï¼ï¼ç¨Â ai_embed ç 1024 维åéãå¨ç¬ç«çææ¬åéè¡¨ä¸æ£ç´¢ââä¸è¦ä¸Â 2560 维å¾ååéæ··ç®ã
é«é¢æ¥è¯¢è¯çæ¥è¯¢åé建议å¨åºç¨å±ç¼åï¼é¿å éå¤è°ç¨æ¨¡åã
ç¬¬å æ¥ï¼èåæåºä¸å¤è·¯å¬å
åéç¸ä¼¼åº¦åªæ¯æåºä¿¡å·ä¹ä¸ï¼çº¿ä¸é常è¿è¦å å è¯åãééãæ´»å¨æéï¼
SELECT sku_id, title, price, rating, sales_30d,
0.7 * approx_cosine_similarity(image_vector, [/* qv */])
+ 0.2 * (rating / 5.0)
+ 0.1 * LOG(sales_30d + 1) / 10 AS final_score
FROM ec_search.product_image_search
WHERE stock > 0
ORDER BY final_score DESC
LIMIT 20;
注æï¼è¦å½ä¸Â TopK åéç´¢å¼ï¼
ORDER BY åªè½å å«å个è¿ä¼¼è·ç¦»è¡¨è¾¾å¼ãä¸é¢è¿ç§å æèåä¼ä½¿ç´¢å¼ä¸çæï¼éåä½ä¸ºç²¾æå±ââæ£ç¡®åæ³æ¯å ç¨çº¯Âapprox_cosine_similarity 索å¼å¬å Top-200 å°Â CTEï¼åå¨Â CTE ä¸åå æéæã
å¤è·¯å¬åï¼å¾ååéå¬å +Â ææ¬åéå¬å + å
³é®è¯åæå¬åï¼ç¨Â RRF èåï¼åè·¯åèªÂ ROW_NUMBER()Â åºæååæÂ SUM(1/(k+rank)) æåï¼ORDER BY ç¨è£¸å«åï¼ä¸è¦å¸¦è¡¨åç¼ï¼
WITH img_recall AS (
SELECT sku_id, ROW_NUMBER() OVER (ORDER BY approx_cosine_similarity(image_vector,[/* qv_img */]) DESC) AS r
FROM ec_search.product_image_search WHERE stock>0 ORDER BY r LIMIT 200),
txt_recall AS (
SELECT sku_id, ROW_NUMBER() OVER (ORDER BY approx_cosine_similarity(text_vector,[/* qv_txt */]) DESC) AS r
FROM ec_search.product_text_search WHERE stock>0 ORDER BY r LIMIT 200),
fused AS (
SELECT sku_id, SUM(1.0/(60+r)) AS rrf
FROM (SELECT sku_id,r FROM img_recall UNION ALL SELECT sku_id,r FROM txt_recall) u
GROUP BY sku_id)
SELECT f.sku_id, p.title, p.price, ROUND(f.rrf,5) AS rrf_score
FROM fused f JOIN ec_search.product_image_search p ON f.sku_id=p.sku_id
ORDER BY rrf_score DESC LIMIT 20;
ç¬¬ä¸æ¥ï¼æ¹è¡¨æ£ç´¢ä¸å¢éæ´æ°
å¨Â DLF Paimon æ¹è¡¨ä¸æ£ç´¢æ¶åæ ·ç¨è¿ä¼¼å½æ°ä¸å¸¸éåéï¼æ¥è¯¢åéè¦ç¨åçæ°ç»åé¢éï¼ å ·å¤å ¼å®¹Â Bitmap/BTree Global Index çç»æåæ¡ä»¶ä¼ä½ä¸ºÂ PrefilterÂ ä¸æ¨ï¼å ¶ä½ä½ä¸ºÂ Postfilter æ§è¡ï¼
SELECT sku_id, approx_cosine_similarity(image_vector, [/* 常éåé */]) AS score
FROM dlf_catalog.`default`.product_vectors
WHERE category = '女è£
' AND stock > 0
ORDER BY score DESC LIMIT 20;
å¢éç»´æ¤ä¸ï¼æ°å䏿¢å¾é½é  object_uri + etag ç anti-join èªå¨çº³å
¥ï¼å¨æè°åº¦æ§è¡Â REFRESH OBJECT TABLE â åéå INSERT å³å¯ï¼ANNÂ ç´¢å¼æ¯æå¢éæå»ºï¼æ éå
¨éé建ï¼å¤§æ¹éå填建议ä½å³°æ§è¡ã
åã宿½ææ
æ¹æ¡è½å°åï¼æç´æ¥çå忝ååå¾çç¬¬ä¸æ¬¡åæäºå¯è¢«è¯ä¹æ£ç´¢çèµäº§ï¼è䏿£ç´¢å¤©ç¶å¸¦ä¸å¡çº¦æã
-
徿å¾ä¸ææå¾å ±ç¨åä¸ä»½å¾ååéä¸å䏿¡æ£ç´¢é¾è·¯ï¼ä¸éè¦ä¸ºè·¨æ¨¡æåç¬å»ºåºï¼
-
"æè´§Â + 类ç®Â +Â ä»·æ ¼åºé´Â + è¯å"è¿äºæ¡ä»¶å¨å¬åé¶æ®µå°±çæï¼ä¸ååºç°å¬å 20 æ¡è¿æ»¤å©Â 3 æ¡çç©ºç»æï¼å忢䏻å¾åé  etag èªå¨è§¦åéæ°åéåï¼æç´¢ç»æä¸åæ»åï¼
-
åéä¸ä¸å¡å±æ§ååºï¼è¿ç»´ä»ä¸¤å¥ç³»ç»æ¶æä¸ºä¸å¥ã
å½çº³èµ·æ¥åç¹æ¶çã
-
æ¶æä¸ï¼åéåºä¸Â OLAP åºåä¸ï¼åå°ä¸å¥ç³»ç»çé¨ç½²ã忥䏿 éé¢ã
-
ææä¸ï¼åéæ£ç´¢ä¸ç»æåè¿æ»¤åçèåï¼æç´¢ç»æç´æ¥å¯ç¨äºå±ç¤ºä¸æåºã
-
ææ¬ä¸ï¼
object_uri + etag å¢éæ°´ä½ä¿è¯åªå¯¹æ°å¢åæ¢å¾ç SKU è°ç¨æ¨¡åï¼åéä¸é夿¨çãå®å ¨ä¸ï¼ååå¾çä¸å¯¼åºå°ç¬¬ä¸æ¹ï¼å¨åºå 宿å 工䏿£ç´¢ã
äºã æ»ç»
éè¿Â StarRocksï¼æå¤æ¨¡ææ¨¡åè½å以 SQLÂ å½æ°çå½¢å¼åµå ¥æ··åæç´¢é¾è·¯ï¼æå»ºäº"OSS å¾çæè½½Â â åéåå ¥åºÂ â ANN 索å¼Â âÂ å¾æå¾Â / ææå¾Â â ä¸å¡è¿æ»¤ä¸èåæåº"çå ¨é¾è·¯éç¯ã
å¾çä¸åºÂ OSSï¼åéä¸ä¸å¡æ°æ®ååºï¼æ¨ç峿¥è¯¢ââç¨ä¸å¥Â SQLÂ åæ¶æ¯ææç §è´ç©ä¸èªç¶è¯è¨æ¾è´§ã
åä¸å¥æ¨¡æ¿è¿å¯å¹³ç§»å°Â UGCÂ å¾æå¹é ã以徿¾åºãçå¾ä¸ä¾µææ£æµã忬¾æ¯ä»·ãç´ æå»éçåºæ¯ã对äºåæ ·é¢ä¸´"å¾çå¨å¯¹è±¡åå¨ãæ£ç´¢è¦å¸¦ä¸å¡æ¡ä»¶ãè¿ä¸æ³å¤ç»´æ¤ä¸å¥åéåº"çå¢éï¼StarRocks æä¾äºä¸æ¡æ¸ æ°çè½å°è·¯å¾ââä¼å SQLï¼å°±è½å夿¨¡ææç´¢ã
Aitishiku.com