本项目由于之前的架构有些问题,关闭了一段时间,经过重构进行开放,也很期待各位提出宝贵的建议
一个轻量级的springboot项目索引构建工具,实现快速模糊搜索:
大部分项目都会涉及模糊搜索功能,本项目提供一个简单索引构建工具,使用注解即可实现索引自动创建与搜索,而不需要你手写该过程, 避免项目中大量使用like或者其他效率低的搜索机制,相比于ES(专业搜索引擎),这是一种轻量级的实现方式
特点:
- 无缝集成springboot,使用简单
- 注解实现索引创建和搜索,与业务逻辑无耦合
- 可用于大数据量模糊搜索,毫秒级响应
- 基于倒排索引,避免全量匹配和手动分词
使用100万行中文 创建索引后进行模糊搜索测试
i7 16g
存储模式(saver) | 平均耗时 | 最大耗时 | 最小耗时 |
---|---|---|---|
nio-fs(nio文件模式) | 45.93ms | 144.5ms | 15.04ms |
base-fs(常规文件系统) | 25.93ms | 97.32ms | 12.24ms |
memory-fs(nmap文件模式) | 23.75ms | 114.12ms | 8.4ms |
memory(内存模式) | 11.56ms | 42.35ms | 7.04ms |
使用1000万行类似的中文创建索引后进行模糊搜索测试
i7 16g
存储模式(saver) | 平均耗时 | 最大耗时 | 最小耗时 |
---|---|---|---|
nio-fs(nio文件模式) | 55.33ms | 151.5ms | 20.65ms |
base-fs(常规文件系统) | 63.02ms | 102.73ms | 15.34ms |
memory-fs(nmap文件模式) | 51.44ms | 180.38ms | 7.15ms |
memory(内存模式) | 12.98ms | 50.26ms | 8.04ms |
相对于文件系统,内存性能最好的无疑的,前提是内存有余,或者数据量不是很过分
测试的文本相对来说较短,如果是长文本,理论上上来说性能会下降一些
另外,以上结果仅仅作为一个参考
- 引入依赖
<dependency>
<groupId>cn.langpy</groupId>
<artifactId>simsearch</artifactId>
<version>2.0.0</version>
</dependency>
使用了@Aspect注解,未引入的自行引入,如aspectj或者spring-boot-starter-aop
- 配置信息
在application.properties中配置
#索引存储器 默认为内存 [memory,memory-fs,base-fs,nio-fs]
#内存富裕的情况下使用memory,如果是百万以上数据量选用fs系列
sim-search.saver=memory
#索引位置,saver!=memory时需配置
sim-search.dir=/data/indexlocation
#创建索引的核心线程数量,根据cpu自行决定,可不填,默认为5
sim-search.thread-core-size=5
#创建索引的最大线程数量,根据cpu自行决定,可不填,默认为200
sim-search.thread-max-size=10
#创建索引的线程队列容量,自行决定,可不填,默认为200000
sim-search.thread-queue-size=200000
#重启时是否要对之前的索引进行删除,默认为false
sim-search.index.init=true
#最大返回的搜索结果数量
sim-search.result.size=50
也可参考demo项目
- 在需要创建索引的实体上标注需要创建索引的字段
import cn.langpy.simsearch.annotation.IndexColumn;
import cn.langpy.simsearch.annotation.IndexId;
public class Student {
/*索引唯一id 必须*/
@IndexId
private String id;
/*需要创建索引的字段:用来模糊搜索*/
@IndexColumn
private String studentName;
@IndexColumn
private String schoolName;
private String age;
}
- 在需要创建索引的方法上加上创建索引的注解
import cn.langpy.simsearch.annotation.CreateIndex;
import cn.langpy.simsearch.annotation.DeleteIndex;
import cn.langpy.simsearch.annotation.SearchIndex;
@Service
public class StudentServiceImpl implements StudentService {
/*加上@CreateIndex后 异步创建索引,不影响正常业务的保存逻辑 indexParam:需要创建索引的参数*/
/*该注解包含了更新操作 有则更新 无则创建*/
@CreateIndex(indexParam = "student")
public boolean insert(Student student){
/*业务逻辑*/
}
}
- 在需要删除索引的方法上加上删除索引的注解
@Service
public class StudentServiceImpl implements StudentService {
/*加上@DeleteIndex后 异步删除索引,不影响正常业务的删除逻辑 indexParam:需要删除索引的参数*/
@DeleteIndex(indexParam = "student")
public boolean delete(Student student){
/*业务逻辑*/
}
}
- 搜索的时候自定义一个空的方法,加上注解即可
@Service
public class StudentServiceImpl implements StudentService {
/*根据studentName属性搜索Student 搜索的属性要和实体的属性保持一致 */
@SearchIndex(by = "studentName")
public List<Student> search(Student student){
/*方法内部什么都不需要写*/
return null;
}
/*根据schoolName属性搜索Student */
@SearchIndex(by = "schoolName")
public List<Student> search(Student Student){
/*方法内部什么都不需要写*/
/*如果再索引中未查到对应信息,可通过该方法设置默认查询,比如往数据库进行like模糊匹配*/
return searchWithLikeByName(schoolName);
}
}
注意:搜索结果仅仅是搜索出加上@IndexId和@IndexColumn的字段,具体内容自行往业务数据库查询
public class IndexManager{
/*为对象创建索引*/
public static void createIndex(Object entity);
public static void createIndexs(List<Object> entities);
/*创建索引*/
public static void createIndex(IndexContent indexContent);
/*删除索引*/
public static void deleteIndex(String idName, String idValue,Class entityClass);
/*搜索 详见源码的demo项目*/
public static <T> List<T> searchIndexIds(String name, String value,Class<?> entityClass);
/*搜索 详见源码的demo项目*/
public static <T> List<T> searchIndexObjects(String name, String value,Class entityClass);
public static void deleteAll();
}
V1.0-snapshots:提供基础索引创建、删除和检索功能
V1.1:增加重启索引初始化功能
V1.2:搜索时,如果未找到搜索,可走默认模式
V1.3:修复启动时索引目录未创建报错的bug
V2.0.0.BETA:全新的版本
- 本项目中使用了aspectjweaver依赖,如果项目中没有该依赖(或者spring-boot-starter-aop),自行引入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>xxx</version>
</dependency>
- 分布式项目中的索引同步,可以自行从数据库加载数据(或者使用canal进行同步),然后创建索引
public class Student {
/*索引唯一id 必须*/
@IndexId
private String id;
/*需要创建索引的字段:用来模糊搜索*/
@IndexColumn
private String studentName;
}
Student student = new Student();
...
IndexManager.createIndex(student);
- lucene
- aop
- spring