大数据量中的模糊查询优化方案

本文介绍如何利用Lucene全文检索技术优化大数据量下的模糊查询性能,通过整合Hibernate+Lucene并结合IKAnalyzer分词器,实现了在SSH框架下的高效模糊搜索功能。
----------------------------------------------------------------------------------------------
[版权申明:本文系作者原创,转载请注明出处] 
文章出处: http://blog.youkuaiyun.com/sdksdk0/article/details/52589761
作者:朱培      ID:sdksdk0     

--------------------------------------------------------------------------------------------

对工作单使用 like模糊查询时,实际上 数据库内部索引无法使用 ,需要逐条比较查询内容,效率比较低在数据量很多情况下, 提供模糊查询性能,我们可以使用lucene全文索引库技术。本文示例是在SSH框架中进行使用。使用Hibernate Search (用来整合 Hibernate + Lucene),工作单搜索功能。


1、首先可以在我们的maven工程中引入需要的jar包,

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-search</artifactId>
			<version>3.4.2.Final</version>
		</dependency>

2、导入IKAnalyzer分词器。因为IKAnalyzer在maven中没有,所以我们需要手动下载这个jar包,当然了,在http://mvnrepository.com/网站上面可以找到。

下载好之后可以装载到你自己的maven仓库中或者直接放在你工程的lib目录中,然后来引用:例如我的是在


		<dependency>
	        <groupId>org.wltea</groupId>
	        <artifactId>IKAnalyzer</artifactId>
	        <version>2012_u6</version>
	        <scope>system</scope>
	        <systemPath>E:\myeclipse_work\BOS\src\main\webapp\WEB-INF\lib\IKAnalyzer2012_u6.jar</systemPath>
   		</dependency>

3、在resource目录中新建stopword.dic文件,内容为:

a
an
and
are
as
at
be
but
by
for
if
in
into
is
it
no
not
of
on
or
such
that
the
their
then
there
these
they
this
to
was
will
with

4、新建一个IKAnalyzer.cfg.xml文件,内容为:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 
	<entry key="ext_dict">ext.dic;</entry> 
	-->
	<!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords">stopword.dic;</entry> 
	
</properties>

5、在spring中进行配置:在配置SessionFactory中加入一行:当然了,这个时候需要自己去D盘目录中新建一个文件夹DBIndex


<!-- 配置索引库 -->
				<prop key="hibernate.search.default.indexBase">d:/DBIndex</prop>

完整的如下:


<!-- 配置SessionFactory  -->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<!-- 配置hibernate 属性 ,参考 hibernate.properties 文件 -->
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<!-- 配置索引库 -->
				<prop key="hibernate.search.default.indexBase">d:/DBIndex</prop>
			</props>
		</property>
		<!-- 映射hbm -->
		<property name="mappingDirectoryLocations" value="classpath:cn/tf/bos/domain"></property>
	</bean>

6、在想要实现查询功能的那个domain中添加注解:想要搜索哪个字段就在哪个字段上面加上@Field注解,注意导入的是IKAnalyzer的分词器,不是hibernate-search的分词器。


@Indexed
@Analyzer(impl = IKAnalyzer.class)
public class WorkOrderManage implements java.io.Serializable {

	// Fields
	@DocumentId
	private String id;
	@Field
	private String arrivecity;  //到达城市
	@Field
	private String product;


分词的效果如下:

使用 Luke 工具,查询索引文件内容 !  在cmd中运行  java  -jar   lukeall-3.5.0.jar,即可打开下图这个页面,查看具体的索引信息。





7、在界面中添加搜索框,我这里使用的是easyui,so...

<div data-options="region:'north'">
		<!-- 编写搜索框 -->
		<!--
			 prompt 默认提示内容
			 menu 搜索条件下拉选项 
			 searcher 点击搜索按钮执行js函数名称
		 -->
		<input id="ss" class="easyui-searchbox" style="width:300px" 
			data-options="prompt:'请输入您的查询内容',menu:'#nm',searcher:doSearch"/>
			
		<div id="nm">
			<div data-options="name:'arrivecity'">按照到达地搜索</div>
			<div data-options="name:'product'">按照货物名称搜索</div>
		</div>
	</div>

8、写doSeach这个js函数

	function doSearch(value,name){
		//将查询条件缓存到datagrid
		$('#grid').datagrid('load',{
			conditionName:name,
			conditionValue:value
		});
	}

9、在action中接收页面传过来的name和value属性的值,然后进行处理:

public String findByPage(){
		
		if(conditionName!=null && conditionName.trim().length()>0 && conditionValue!=null && conditionValue.trim().length()>0){
			//有条件查询
			PageResponseBean pageResponseBean=workordermanagerService.findByLucene(conditionName,conditionValue,page,rows);
			ActionContext.getContext().put("pageResponseBean", pageResponseBean);
			
		}else{
			DetachedCriteria detachedCriteria=DetachedCriteria.forClass(WorkOrderManage.class);
			PageRequestBean  pageRequestBean=initPageRequestBean(detachedCriteria);
			
			PageResponseBean pageResponseBean=workordermanagerService.findByPage(pageRequestBean);
			
			ActionContext.getContext().put("pageResponseBean", pageResponseBean);
		}
		return "findByPage";
	}
	
	private String conditionName;
	private String conditionValue;

	public void setConditionName(String conditionName) {
		this.conditionName = conditionName;
	}

	public void setConditionValue(String conditionValue) {
		this.conditionValue = conditionValue;
	}
返回值之后如何处理这里我就不在说了。

10、在service中进行处理,经过service和serviceImpl之后,就会到达dao中,所以我们可以在dao中进行处理。


//luence查询
	@Override
	public PageResponseBean findByLucene(String conditionName,
			String conditionValue, int page, int rows) {
		Session session=this.getSession();
		FullTextSession  fullTextSession=new FullTextSessionImpl(session);
		
		Query query=new WildcardQuery(new Term(conditionName,"*"+conditionValue+"*"));
		
		//获得全文检索的query
		FullTextQuery  fullTextQuery=fullTextSession.createFullTextQuery(query);
		PageResponseBean  pageResponseBean=new PageResponseBean();
		pageResponseBean.setTotal(fullTextQuery.getResultSize());
		
		//当前页数据
		int firstResult=(page-1)*rows;
		int maxResults=rows;
		List  list=fullTextQuery.setFirstResult(firstResult).setMaxResults(maxResults).list();
		pageResponseBean.setRows(list);
		
		return pageResponseBean;
	}

11、在页面中查看搜索的效果






这样我们整个开发流程就完成了。使用luence对大数据量中的模糊查询是非常实用的功能,当然了,luence只适用于站内搜索,对于模糊查询的支持还是非常好的。





<think>针对大数据量下的模糊查询性能优化,我们可以采取多种策略,包括索引优化、查询重写、使用全文搜索、引入缓存等。下面将详细介绍这些方法,并提供相应的代码示例。 ###1.使用索引优化对于模糊查询,传统的B树索引对`LIKE '%keyword%'`这种模式无效,但可以优化特定模式的查询: -**前缀匹配**:`LIKE'keyword%'`可以使用索引。- **后缀匹配**:`LIKE '%keyword'`无法使用索引,但可以反转字符串并建立索引。- **固定格式匹配**:如`code`字段格式固定(如`XX-XX-XX`),可考虑拆分字段。**示例:为后缀匹配创建反转索引**```sql--添加反转字符串列ALTER TABLEitems ADDCOLUMN code_reverseVARCHAR(20)GENERATEDALWAYSAS (REVERSE(code)) STORED; --创建索引CREATEINDEX idx_code_reverse ONitems(code_reverse); --查询优化:查找以'01'结尾的codeSELECT codeFROMitemsWHERE code_reverseLIKE REVERSE('01')||'%';--相当于codeLIKE '%01'``` ###2.使用全文索引(FULLTEXT)MySQL的全文索引适用于自然语言搜索,可快速查找包含关键词的记录。**步骤:** 1.创建全文索引(仅限InnoDB/MyISAM,且字段类型为CHAR/VARCHAR/TEXT)。2.使用`MATCH()AGAINST()`进行查询。**示例:** ```sql--创建全文索引ALTERTABLE itemsADD FULLTEXTINDEX idx_fulltext_code (code);--查询包含"B"的记录SELECT codeFROMitemsWHERE MATCH(code) AGAINST('B' INBOOLEANMODE); ```> **注意**:全文索引有最小词长度限制(默认4字符),可通过修改`ft_min_word_len`配置调整。###3.引入缓存对于频繁访问的模糊查询结果,可使用缓存(如Redis)存储查询结果。**示例(伪代码):** ```pythonimport redisimportjsonimport mysql.connector#连接Redis和MySQLr=redis.Redis(host='localhost', port=6379,db=0)db =mysql.connector.connect(user='user',password='pass',database='mydb')def search_items(keyword):#尝试从缓存获取cache_key= f"search:{keyword}"cached_result= r.get(cache_key)if cached_result:returnjson.loads(cached_result)#缓存未命中,查询数据库cursor =db.cursor()query= "SELECT codeFROM itemsWHERE codeLIKE %s"cursor.execute(query, (f'%{keyword}%',))result =[row[0]for rowin cursor.fetchall()]#存入缓存(设置过期时间)r.setex(cache_key,3600,json.dumps(result))#缓存1小时returnresult``` ###4.分页查询避免一次性返回大量数据,使用分页(`LIMIT`)减少单次查询负载。**示例:**```sqlSELECTcodeFROM itemsWHEREcode LIKE'%B%'LIMIT20 OFFSET0;--第一页```###5.使用外部搜索引擎对于海量数据,可集成Elasticsearch、Solr等专业搜索引擎。**Elasticsearch示例:** 1.将数据同步到Elasticsearch。2.使用模糊查询API:```jsonGET /items/_search{"query": {"wildcard": {"code":"*B*"}}} ```###6.其他优化技巧-**减少返回字段**:只查询必要字段。- **避免`%`开头的模糊查询**:尽量使用`keyword%`模式。 -**分区表**:按时间或范围分区,缩小扫描范围。 ###总结优化策略需根据实际场景选择: -小数据量:尝试索引优化(前缀索引、反转索引)。 -中等数据量:使用全文索引或缓存。 -大数据量:引入外部搜索引擎。--- §§相关问题§§: 1. MySQL全文索引的最小词长度如何修改?2.如何实现Elasticsearch与MySQL的数据同步?3.除了Redis,还有哪些缓存方案适用于查询结果缓存? 4.在分页查询中如何优化深度分页(如第1000页)的性能?</think># 大数据量下模糊查询性能优化方案大数据量场景下,模糊查询(特别是以通配符开头的`LIKE '%value%'`查询)会面临严重的性能问题。以下是多种优化策略及其实现方案: ## 1. 使用前缀索引优化 ```sql -- 创建前缀索引 ALTER TABLE your_table ADD INDEX idx_code_prefix (code(5)); -- 优化后的查询 SELECT code FROM your_table WHERE code LIKE 'B%'; -- 使用前缀匹配 ``` **优化原理**: - 前缀索引只对字段的前N个字符建立索引 - 将`%value%`查询改为`value%`查询,可以利用索引 - 适用于字段值有明确前缀模式的场景 ## 2. 使用生成列和索引 ```sql -- 添加生成列存储小写版本 ALTER TABLE your_table ADD COLUMN code_lower VARCHAR(50) AS (LOWER(code)) STORED; -- 在生成列上创建索引 CREATE INDEX idx_code_lower ON your_table(code_lower); -- 优化后的查询 SELECT code FROM your_table WHERE code_lower LIKE '%b%'; -- 使用小写查询 ``` **优化原理**: - 生成列预先计算并存储转换后的值 - 对小写版本建立索引,避免函数调用 - 支持大小写不敏感的模糊查询 ## 3. 使用全文索引(FULLTEXT) ```sql -- 创建全文索引 ALTER TABLE your_table ADD FULLTEXT INDEX idx_fulltext_code (code); -- 使用全文索引查询 SELECT code FROM your_table WHERE MATCH(code) AGAINST('+B*' IN BOOLEAN MODE); ``` **优化原理**: - 全文索引使用倒排索引结构,查询效率高 - 支持布尔模式搜索(`+B*`表示以B开头的词) - 适用于文本内容的模糊匹配 ## 4. 使用反向索引(Reverse Index) ```sql -- 添加反转字段 ALTER TABLE your_table ADD COLUMN code_reverse VARCHAR(50) AS (REVERSE(code)) STORED; -- 创建反转字段索引 CREATE INDEX idx_code_reverse ON your_table(code_reverse); -- 优化后缀查询 SELECT code FROM your_table WHERE code_reverse LIKE REVERSE('%B'); -- 等价于 code LIKE 'B%' ``` **优化原理**: - 反转字符串使后缀查询变为前缀查询 - 适用于需要`%value`后缀匹配的场景 ## 5. 使用N-Gram分词索引 ```sql -- 创建N-Gram解析器 CREATE TABLE ngram_table ( id INT PRIMARY KEY, code VARCHAR(50), FULLTEXT INDEX idx_ngram_code (code) WITH PARSER ngram ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 查询示例 SELECT code FROM ngram_table WHERE MATCH(code) AGAINST('B'); ``` **优化原理**: - 将文本分解为N个字符的分词单元 - 对每个分词单元建立索引 - 特别适合短文本和编码的模糊匹配 ## 6. 应用层缓存优化 ```python from functools import lru_cache import mysql.connector @lru_cache(maxsize=1024) def fuzzy_search(keyword): """带LRU缓存的模糊查询""" conn = mysql.connector.connect(user='user', password='pass', database='db') cursor = conn.cursor() query = "SELECT code FROM your_table WHERE code LIKE %s" cursor.execute(query, (f'%{keyword}%',)) results = [row[0] for row in cursor.fetchall()] cursor.close() conn.close() return results # 使用缓存查询 print(fuzzy_search('B')) # 第一次查询数据库 print(fuzzy_search('B')) # 第二次从缓存读取 ``` **优化原理**: - 使用LRU缓存存储常用查询结果 - 减少重复查询数据库的次数 - 适用于查询模式相对固定的场景 ## 7. 分区表优化 ```sql -- 按首字母范围分区 CREATE TABLE your_table ( id INT AUTO_INCREMENT, code VARCHAR(50), PRIMARY KEY (id, code) ) PARTITION BY RANGE COLUMNS(code) ( PARTITION p0 VALUES LESS THAN ('F'), PARTITION p1 VALUES LESS THAN ('K'), PARTITION p2 VALUES LESS THAN ('P'), PARTITION p3 VALUES LESS THAN (MAXVALUE) ); -- 查询特定分区 SELECT code FROM your_table PARTITION (p0) WHERE code LIKE '%B%'; ``` **优化原理**: - 将大表分割为多个小分区 - 查询时只扫描相关分区 - 适用于有明确分区键的场景 ## 性能对比策略 | 优化方法 | 适合场景 | 索引大小 | 查询复杂度 | 适用模式 | |---------|---------|---------|-----------|---------| | 前缀索引 | 前缀匹配 | 小 | O(log n) | `value%` | | 生成列 | 大小写不敏感 | 中 | O(log n) | `%value%` | | 全文索引 | 文本搜索 | 大 | O(1) | 布尔搜索 | | 反向索引 | 后缀匹配 | 中 | O(log n) | `%value` | | N-Gram | 短文本模糊 | 很大 | O(1) | 任意位置 | | 缓存 | 重复查询 | 可变 | O(1) | 所有模式 | | 分区 | 大表范围查询 | 分散 | O(log n/k) | 分区键相关 | ## 综合建议 1. **优先考虑查询模式优化**: - 尽量避免`%value%`写法,改用`value%` - 使用更精确的查询条件缩小范围 2. **索引策略选择**: ```sql -- 组合优化方案示例 ALTER TABLE your_table ADD COLUMN code_prefix VARCHAR(10) AS (LEFT(code, 5)) STORED, ADD COLUMN code_lower VARCHAR(50) AS (LOWER(code)) STORED; CREATE INDEX idx_combo ON your_table(code_prefix, code_lower); SELECT code FROM your_table WHERE code_prefix LIKE 'B%' AND code_lower LIKE '%b%'; ``` 3. **架构级优化**: - 对于TB级数据,考虑使用Elasticsearch等专用搜索引擎 - 实施读写分离,将复杂查询路由到只读副本 - 使用列式存储数据库如ClickHouse处理分析型查询 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值