Skip to content

hedon-java-road/tiny-search

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Tiny Search

这是一个非常令人兴奋的工程挑战。要实现一个“由浅入深”且最终达到“工业级”的 Java 搜索系统,我们不能一开始就堆砌微服务。

建议采用 “演进式架构”(Evolutionary Architecture),将开发过程分为五个阶段。每个阶段都是对上一个阶段的重构和升级。


第一阶段:单机核心版 —— 理解 Lucene 与倒排索引

目标: 抛开网络和并发,彻底搞懂数据是怎么存进去、怎么查出来的。不使用 Elasticsearch,直接操作 Lucene SDK。

  • 架构形态: 简单的 Java CLI 工具或单元测试。

  • 核心任务:

    1. Schema 设计: 定义一个 Item 类(ID, Title, Description, Price)。

    2. 构建索引(Write): 使用 org.apache.lucene 包。

      • 配置 Analyzer(分词器),初期可以用标准分词,马上引入 HanLP 做中文分词。
      • 使用 IndexWriter 写入 Document。

      • 观察磁盘上生成的 Segment 文件结构(.doc, .pos, .tim),直观理解倒排索引。

    3. 基础检索(Read):

      • 使用 IndexSearcherQueryParser

      • 实现 TermQuery(精确查)和 BooleanQuery(组合查)。

  • 关键代码点:

    • Directory (FSDirectory vs RAMDirectory)。
    • commit() vs flush() 的区别。

第二阶段:服务化与读写分离 —— 引入 Spring Boot

目标: 让搜索变成一个 Web 服务,并处理并发读写冲突。

  • 架构形态: Spring Boot 单体应用。

  • 核心任务:

    1. API 封装: 提供 POST /api/index/addGET /api/search 接口。

    2. 单机读写分离:

      • 写线程: 维护全局唯一的 IndexWriter,负责写入。这里要设计一个阻塞队列(BlockingQueue)作为简单的内存缓冲。

      • 读线程: 使用 SearcherManager。这是工业级关键点,它能通过 NRT (Near Real-Time) 机制,让写入的数据在毫秒级内可见,而不需要昂贵的 commit 操作。

    3. 对象池化: 搜索对象很重,不要每次请求都 new

  • 关键技术:

    • Spring Boot, Lucene SearcherManager, ControlledRealTimeReopenThread.

第三阶段:数据流与异构同步 —— 引入 MySQL 与 Canal

目标: 模拟真实工业场景,数据不再由 API 手动录入,而是自动从业务数据库同步。

  • 架构形态: 数据源 -> 中间件 -> 搜索服务。

  • 核心任务:

    1. 准备数据源: 在本地 MySQL 建一张 products 表,模拟电商商品。

    2. CDC 接入: 部署 Canal Server,监听 MySQL Binlog。

    3. 消费与写入:

      • 在 Spring Boot 中集成 Kafka Consumer(或者直接连 Canal Client)。

      • 全量同步: 写一个脚本,把 MySQL 历史数据扫入索引。

      • 增量同步: 监听 Binlog 的 INSERT/UPDATE/DELETE 事件,实时操作 Lucene IndexWriter

  • 关键技术:

    • MySQL Binlog, Alibaba Canal, Kafka (可选,作为缓冲层), 最终一致性保证。

第四阶段:查询理解与混合检索 —— 引入 NLP 与 向量

目标: 解决“搜不准”的问题,引入语义搜索。

  • 架构形态: 增加“查询理解模块”和“向量索引”。

  • 核心任务:

    1. Query Understanding (QU) 模块:

      • 在 Controller 层之前拦截 Query。

      • 集成 HanLP 进行命名实体识别(NER),识别出“品牌”和“品类”。

      • 实现同义词扩展(Synonym),维护一个简单的 Map 映射。

    2. 向量化(Embedding):

      • 引入 DJL (Deep Java Library)ONNX Runtime

      • 加载一个轻量级的 BERT 模型(如 m3e-base)。

      • 在写入时,将标题转为 float[] 向量。

    3. 混合检索(Hybrid Search):

      • 升级 Lucene 到 9.x+,使用 KnnVectorQuery

      • 实现:(关键词匹配 score * 0.7) + (向量相似度 score * 0.3) 的简单融合逻辑。

  • 关键技术:

    • DJL/ONNX, Lucene HNSW Graph, HanLP.

第五阶段:工业级重构 —— 排序与并发架构

目标: 支撑中量并发,优化延迟,实现精细化排序。

  • 架构形态: 仿微服务架构(即便部署在单机,也要逻辑解耦)。

  • 核心任务:

    1. Scatter-Gather 模式:

      • 模拟分片:在本地创建 3 个不同的 Lucene Directory(Shard 0, 1, 2)。

      • 构建 Aggregator 层:收到请求后,使用 CompletableFuture 并发向 3 个 Shard 查询,最后合并结果。

    2. 精排服务(Ranking Service):

      • 特征快照: 将商品的静态分(销量、评分)加载到堆外内存(Off-heap)或 Redis 中。

      • 算分逻辑: 在合并结果后,对 Top 100 商品应用自定义打分公式(如 Log(Sales) * Relevance)。可以尝试加载一个简单的 PMML 或 XGBoost 模型进行打分。

    3. 性能压测与调优:

      • 使用 JMeterGatling 进行压测。

      • 针对 GC(垃圾回收)进行调优,因为搜索产生大量临时对象。

  • 关键技术:

    • Java Concurrency (CompletableFuture), Netty (可选,做高性能 RPC), Redis, XGBoost4J.

项目目录结构预览 (Maven/Gradle)

tiny-search-engine/
├── tiny-search-core/       # 核心索引与检索逻辑 (Lucene 封装)
├── tiny-search-api/        # Web 接口层 (Spring Boot)
├── tiny-search-data/       # 数据同步模块 (Canal/Kafka Client)
├── tiny-search-nlp/        # NLP 与 向量模型服务 (DJL, HanLP)
├── tiny-search-rank/       # 排序逻辑与特征加载
└── docker-compose.yml      # 环境编排 (MySQL, Kafka, Redis)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages