行业资讯
【高速缓存】RedisVL 在 Redis 上使用 SQL 查询数据实践指南
Redis 本身并不原生支持 SQL但通过 RedisVL 提供的SQLQuery类你可以编写类似 SQL 的查询语句并自动翻译为 Redis 能够执行的底层命令。这不仅降低了学习曲线还能让你快速利用 Redis 的高级功能向量检索、地理空间、全文搜索等。本文将从零开始带你一步步掌握如何在 Redis 上使用 SQL 进行各种复杂查询并深入理解背后的工作原理。前置准备在开始之前请确保满足以下条件已安装 RedisVL 并启用 SQL 支持pipinstallredisvl[sql-redis]有一个正在运行的 Redis 实例建议 Redis 8 或使用 Redis Cloud且已安装 RediSearch 模块通常默认包含。概要使用SQLQuery编写 SQL 风格的查询语句将SELECT、WHERE、ORDER BY等子句翻译为 Redis 查询将 SQL 查询与向量搜索结合执行混合搜索使用聚合函数和分组GROUP BY使用geo_distance()查询地理数据使用YEAR()、MONTH()、DATE_FORMAT()等函数处理日期/时间整体工作流程下图展示了使用 SQLQuery 进行查询的完整流程定义 Schema创建样本数据创建 SearchIndex加载数据编写 SQL 查询执行查询获取结果查询类型条件查询文本搜索聚合查询向量搜索地理查询日期时间查询翻译为 FT.SEARCH / FT.AGGREGATE第一步定义 SchemaSchema模式定义了索引的结构包括字段名称、类型和属性。我们使用一个用户数据集包含用户信息、职位描述、地理位置和职位嵌入向量。fromredisvl.utils.vectorizeimportHFTextVectorizer# 使用 HuggingFace 的嵌入模型来生成文本向量hfHFTextVectorizer()schema{index:{name:user_simple,# 索引名称prefix:user_simple_docs,# 文档键前缀storage_type:json,# 存储类型JSON 或 Hash},fields:[{name:user,type:tag},# 标签字段可精确匹配{name:region,type:tag},{name:job,type:tag},{name:job_description,type:text},# 全文搜索字段{name:age,type:numeric},{name:office_location,type:geo},# 地理坐标{name:job_embedding,type:vector,attrs:{dims:len(hf.embed(get embed length)),# 向量维度distance_metric:cosine,# 余弦距离algorithm:flat,datatype:float32}}]}字段类型说明类型用途tag精确匹配如分类、枚举text全文搜索支持分词、模糊匹配numeric数值范围查询geo地理坐标经度,纬度vector向量嵌入用于相似性搜索第二步创建样本数据集我们准备一些模拟用户数据并为每个用户的职位描述生成向量嵌入。data[{user:john,age:34,job:software engineer,region:us-west,job_description:Designs, develops, and maintains software applications and systems.,office_location:-122.4194,37.7749# 旧金山},# ... 其他用户bill, mary, joe, stacy]# 生成向量将职位描述和职位名称拼接后嵌入data[{**d,job_embedding:hf.embed(f{d[job_description]}{d[job]}),}fordindata]注意地理坐标使用的是经度,纬度格式Redis 约定这与常见的地图坐标纬度在前相反务必牢记。第三步创建 SearchIndexSearchIndex是 RedisVL 中管理索引的核心类。你可以通过两种方式实例化方式一传入自定义 Redis 连接推荐用于生产环境fromredisvl.indeximportSearchIndexfromredisimportRedis clientRedis.from_url(redis://localhost:6379)indexSearchIndex.from_dict(schema,redis_clientclient)方式二让索引管理连接简单场景indexSearchIndex.from_dict(schema,redis_urlredis://localhost:6379)创建索引index.create(overwriteTrue,dropTrue)# 强制覆盖已有索引第四步加载数据将数据插入 Redis每个文档会以 JSON 格式存储根据 schema 中的storage_type。keysindex.load(data)# 返回每个文档的 keyprint(keys)验证提示RedisVL 默认使用 Pydantic 验证加载的数据是否符合 schema。但对于 GEO 字段由于格式约定经度在前验证可能暂不兼容我们在此忽略该选项未来版本会修正。第五步编写并执行 SQL 查询现在进入正题。我们使用SQLQuery类将 SQL 字符串转换为 Redis 命令字符串并执行。简单查询示例fromredisvl.queryimportSQLQuery sql_str SELECT user, region, job, age FROM user_simple WHERE age 17 sql_querySQLQuery(sql_str,sql_redis_options{schema_cache_strategy:lazy})sql_redis_options可配置缓存策略lazy默认按需加载 schema适合首次查询较慢但后续快速的场景load_all启动时预加载所有 schema适合频繁查询多样表的场景查看生成的 Redis 命令redis_querysql_query.redis_query_string(redis_urlredis://localhost:6379)print(redis_query)# 输出FT.SEARCH user_simple age:[(17 inf] RETURN 4 user region job age DIALECT 2执行查询resultsindex.query(sql_query)print(results)可以看到返回了所有年龄大于 17 的用户。查询类型详解1. 条件运算符AND、OR、INAND 条件SELECTuser,region,job,ageFROMuser_simpleWHEREage17ANDregionus-west翻译后的 Redis 命令会在age和region之间进行交集AND。OR 条件WHEREregionus-westORregionus-central翻译为((region:{us\-west})|(region:{us\-central}))使用并集。IN 运算符仅对 tag 字段WHEREjobIN(software engineer,engineer,pancake tester)翻译为job:{software engineer|engineer|pancake tester}。2. 文本搜索文本字段text支持多种匹配模式操作符说明SQL 示例Redis 翻译精确短语匹配自动去停用词WHERE job_description healthcare includingjob_description:healthcare includingLIKE sci%前缀匹配WHERE job_description LIKE sci%job_description:sci*LIKE %care后缀匹配WHERE job_description LIKE %carejob_description:*careLIKE %diagnose%包含匹配WHERE job_description LIKE %diagnose%job_description:*diagnose*fuzzy(column, ...)容错匹配允许拼写错误WHERE fuzzy(job_description, diagnose)使用%和~实现模糊搜索fulltext(column, ...)分词搜索支持布尔操作WHERE fulltext(job_description, healthcare OR diagnosing)标准全文搜索原理Redis 的FT.SEARCH支持通配符*和~实现模糊fulltext则是基于分词和倒排索引。3. 聚合查询GROUP BY 聚合函数聚合查询使用FT.AGGREGATE命令。支持以下聚合函数COUNT、COUNT_DISTINCTMIN、MAX、AVG、STDEVFIRST_VALUE、LAST_VALUEARRAY_AGG收集为列表QUANTILE(column, p)百分位数示例SELECTregion,COUNT(age)ascount_age,AVG(age)asavg_age,QUANTILE(age,0.99)asquantile_ageFROMuser_simpleGROUPBYregion这将按区域分组计算统计量。对应的 Redis 命令会生成GROUPBY和多个REDUCE子句。4. 向量搜索相似性检索使用cosine_distance()函数计算向量相似度并可按距离排序。SELECTuser,job,cosine_distance(job_embedding,:vec)ASvector_distanceFROMuser_simpleORDERBYvector_distanceASC这里:vec是参数占位符执行时需传入实际的向量二进制格式。Redis 会执行 KNN 搜索默认返回前 10 个结果。参数传递vechf.embed(looking for someone to use base principles to solve problems,as_bufferTrue)sql_querySQLQuery(sql_str,params{vec:vec})翻译后的命令使用*[KNN 10 job_embedding $vector AS vector_distance]。混合搜索Hybrid Search从 Redis 8.4 开始支持FT.HYBRID将全文搜索和向量搜索的结果融合排序。使用hybrid_vector_search()函数SELECTuser,job,job_description,hybrid_vector_search(cosine_distance(job_embedding,:vec),fulltext(job_description,principles solve problems),rrf()-- 或 linear())AShybrid_scoreFROMuser_simpleORDERBYhybrid_scoreDESCrrf()倒数排名融合平衡文本和向量权重linear()线性加权可指定权重这与预过滤在 WHERE 中加条件不同——混合搜索是两个独立的排序再融合而预过滤是在向量搜索前先限定文档集合。5. 地理查询GeospatialRedis 支持地理空间索引使用geo_distance()函数进行距离过滤或计算。过滤在 WHERE 中使用SELECTuser,job,regionFROMuser_simpleWHEREgeo_distance(office_location,POINT(-122.4194,37.7749),km)500POINT(lon, lat)经度在前纬度在后与 Redis 一致单位km、mi、m、ft翻译后生成GEOFILTER office_location -122.4194 37.7749 500.0 km。距离计算在 SELECT 中SELECTuser,geo_distance(office_location,POINT(-73.9857,40.7580))ASdistance_metersFROMuser_simple结果返回距离单位米。这里geo_distance不带单位参数默认返回米。结合其他条件可结合标签、数值、文本条件WHEREjobengineerANDgeo_distance(...)50这会在GEOFILTER之前加上job:{engineer}过滤。地理查询总结用法SQL 示例距离过滤WHERE geo_distance(field, POINT(lon, lat), km) radius距离计算SELECT geo_distance(field, POINT(lon, lat)) AS dist组合过滤WHERE age 30 AND geo_distance(...) 100多条件使用 AND/OR 与 TAG、NUMERIC 等组合6. 日期和时间查询Redis 使用 Unix 时间戳整数存储日期因此日期字段类型为numeric。RedisVL 支持日期字面量和日期函数。日期字面量WHEREcreated_at2024-01-012024-01-01会被自动转换为 Unix 时间戳1704067200。日期范围WHEREcreated_atBETWEEN2024-01-01AND2024-03-31日期函数YEAR(created_at)提取年份MONTH(created_at)提取月份返回 0~110 表示一月DATE_FORMAT(created_at, %Y-%m-%d)格式化为字符串SELECTname,YEAR(created_at)ASyear,MONTH(created_at)ASmonthFROMevents按日期分组SELECTYEAR(created_at)ASyear,COUNT(*)ASevent_countFROMeventsGROUPBYyear重要提示存储日期必须存储为整数时间戳如int(datetime.timestamp())时区日期字面量默认视为 UTCMONTH 返回 0~11与常见习惯不同需注意原生过滤器Native Filters除了 SQL 语法RedisVL 还提供了编程式的过滤器类可用于构建更复杂的组合条件尤其适合动态构建查询。fromredisvl.queryimportFilterQueryfromredisvl.query.filterimportGeo,GeoRadius,Tag,Num# 地理过滤器geo_filterGeo(office_location)GeoRadius(-87.6298,41.8781,100,km)# 标签过滤器job_filterTag(job)engineer# 数值过滤器age_filterNum(age)40combinedgeo_filterjob_filterage_filter queryFilterQuery(filter_expressioncombined,return_fields[user,job,age])resultsindex.query(query)这种方式更接近 Redis 原生语法适合在代码中灵活组合条件。异步支持如果你的应用是异步的如使用 FastAPIRedisVL 提供了AsyncSearchIndexfromredisvl.indeximportAsyncSearchIndexfromredisvl.queryimportSQLQuery async_indexAsyncSearchIndex.from_dict(schema,redis_urlredis://localhost:6379)sql_querySQLQuery(SELECT user, age FROM user_simple WHERE age 30)resultsawaitasync_index.query(sql_query)awaitasync_index.disconnect()异步版本与同步版本 API 一致仅执行方法为await。清理资源完成操作后可以删除索引以释放资源index.delete()结语通过SQLQuery你可以用熟悉的 SQL 语法操控 Redis 的强大功能而无需记忆复杂的 Redis 命令。本文涵盖了从安装、建表到各类查询的完整流程并深入解释了每个环节的底层原理。RedisVL 的 SQL 支持并非完全的 SQL 实现而是针对 Redis 特点的精心封装使得开发体验大大提升。尝试结合你的业务场景探索更多可能。
郑州网站建设
网页设计
企业官网