ES+HBase实现仿百度搜索引擎-1
1 企业中快速复杂查询痛点分析
大数据领域海量数据存储现状
1 | 首先来分析一下目前大数据领域中的一些数据存储系统:HDFS、HBase、Kudu |

1 | -HDFS:是一个分布式文件系统,适合文本类型数据存储,不支持修改删除,适合一次写入,多次读取的场景。借助于Hive可以实现基于SQL的海量数据分析。HDFS在实际工作中是最常见的。 |
大数据领域常见的SQL分析引擎
1 | 目前大数据领域常见的SQL分析引擎有以下这些:Hive、Impala、Kylin。 |

1 | Hive:Hive是一个支持SQL的数据查询引擎,适合通过SQL对HDFS中的海量文本数据进行分析,不适合单条数据的随机查询,因为每一次查询底层都需要执行MapReduce任务,单条数据查询效率比较低。 |
1 | 目前大数据领域中这几个数据分析引擎都是不擅长快速复杂查询的,因为他们侧重的其实都是数据分析,而不是快速复杂查询。 |
目前常见的全文检索引擎
1 | 目前常见的全文检索引擎主要是这几个:Lucene、Solr、Elasticsearch |

1 | lucene:Lucene是Java家族中最为出名的一个开源搜索引擎,缺点是使用起来比较繁琐,并且不支持分布式,无法应用在海量数据场景下。 |
海量数据存储+快速复杂查询需求
1 | 假设现在遇到一个需求,企业里面有一套爬虫程序,每天都会到互联网上抓取海量的文章数据,针对这些文章数据: |

1 | 在这里能不能直接使用Elasticsearch实现海量数据存储和快速复杂查询呢? |
海量数据存储+快速复杂查询的解决方案
1 | 所以这个需求只使用一个技术组件是无法完美解决的,最好的解决方案是将HBase和Elasticsearch整合到一起,利用HBase适合海量数据存储、基于Rowkey查询效率高的特性,以及Elasticsearch适合快速复杂查询的特性。 |

2 仿百度搜索引擎项目架构设计
项目概览
1 | 首先看一下项目最终的界面效果 |

项目整体架构流程

1 | 1:项目的数据来源可以是通过爬虫到互联网上采集的数据,也可以是企业数据库中的内部数据 |
ES和HBase数据同步的三种方案
1 | 针对此项目,有一个核心功能点,如何在ES中同步对HBase中的数据建立索引? |

1 | 1:方案1,在将原始数据入库HBase的时候,同时在ES中对数据建立索引,此时可以把入库HBase和ES的代码放在一个事务中,保证HBase和ES的数据一致性。 |

1 | 2:方案2,在将原始数据入库HBase的时候,通过HBase中的协处理器实现数据同步,让协处理器监听HBase中的新增和删除操作,在协处理器内部操作ES,实现对数据建立索引的功能。 |

1 | 3:方案3,在将原始数据入库HBase的时候,同时在Redis中使用list数据类型模拟一个队列,存储数据的Rowkey。此时将入库HBase和Redis的操作放在一个事务里面,保证数据的一致性。然后再通过另外一个同步程序,从Redis的list队列中读取Rowkey,根据Rowkey到HBase中获取数据的详细信息,在ES中建立索引,将HBase中数据的Rowkey作为ES中数据的ID。 |
项目整体执行流程
1 | 接下来分析一下项目底层细节流程 |

1 | 1:通过入库程序向HBase中入库数据,同时在Redis中存储数据的Rowkey。 |
3 ES高级特性扩展
1 | 在具体开发项目之前,先来了解一下ES中的几个特性: |
ES中的_source字段

1 | 在ES中包含一个特殊的字段:_source |
ES中字段的index和store特性
1 | 我们在向ES中添加数据的时候,ES底层针对每个字段其实还会涉及到index和store这两个属性: |
1 | 下面来看一个例子: |
1 | 是否建立索引 是否存储 |
1 | 解释: |
1 | 注意:在实际工作中,针对某个字段来说,需要建立索引,但是不需要存储,这种场景也是存在的,因为某一些字段内容如果比较大,并且没有必要从ES中返回,其实就没必要在ES中存储了,否则会额外占用ES的存储空间,也会影响ES的查询效率。 |
1 | 由于默认情况下_source字段会存储所有字段的内容,所以需要在_source字段中过滤掉不需要存储的字段。 |
1 | 针对这个案例,创建一个索引库:stuinfo |
1 | 查询mappings信息。 |
1 | 向索引库中添加一条数据 |
1 | 发现返回的_source字段中确实没有address字段的内容了。 |

1 | 根据address可以查询到数据,说明address的index属性生效了 |
1 | 如果我们使用sex字段进行查询,会看到报错信息,因为sex字段没有建立索引是无法作为查询字段的。 |
1 | [root@bigdata01 ~]# curl -H "Content-Type: application/json" -XGET 'http://bigdata01:9200/stuinfo/_search?pretty' -d'{"query":{"match":{"sex":"man"}}}' |
4 开发仿百度搜索引擎项目
1 | 这个搜索引擎项目主要涉及到数据采集、数据存储、建立索引和数据展现环节。 |

1 | 我们在开发这个搜索引擎项目的时候会重点实现数据存储、建立索引这两个环节。 |
1 | 在具体开发项目之前,我们首先分析一下项目中的数据和具体的查询要求,这样就知道ES中索引库的mapping应该如何设计了。 |

1 | 那针对我们这里的文章数据,还有一个时间字段,其实也可以显示在列表页面中,具体显示哪些字段,可以根据工作中的具体需求来定。 |

1 | 文章ID:需要建立索引,并且存储,这是ES中ID字段必须要具备的特性。 |
1 | 接下来我们来手工指定一下索引库的settings和mapping参数。 |
1 | 解释: |

1 | dynamic具体选择哪个参数,就需要根据需求来定了,在这里不希望在ES中保存未知字段,所以使用strict。 |
调用接口获取数据导入HBase和Redis
1 | 在Idea中直接打开已有项目:db_fullsearch,里面有一个web_fullsearch子model项目。 |
1 | <dependencies> |
1 | 在子Model项目data_manager中添加log4j.properties配置文件。 |
1 | 在子Model项目data_manager中创建package:com.imooc.core和com.imooc.utils |
com.imooc.utils
HBaseUtil
1 | package com.imooc.utils; |
1 | 把这个构造方法私有化的意义是为了防止外部类创建RedisUtil的实例对象。这样可以保证RedisUtil是一个单例类,只有一个实例存在。这样可以节省内存空间,避免多个实例之间的同步问题,提高性能。😊 |
RedisUtil
1 | package com.imooc.utils; |
com.imooc.core
DataImport
1 | DataImport代码如下: |
1 | package com.imooc.core; |
通过ES对HBase中的数据建立索引
1 | 在com.imooc.core中创建类:DataIndex,负责实现在ES中对数据建立索引。 |
com.imooc.utils
EsUtil
1 | package com.imooc.utils; |
HBaseUtil
1 | HBaseUtil工具类代码如下: |
1 | package com.imooc.utils; |
com.imooc.core
DataIndex
1 | DataIndex代码如下: |
1 | package com.imooc.core; |
对接Web项目,提供页面检索功能
1 | 完善web_fullsearch项目中和大数据相关的代码。 |
1 | /** |
从0~1运行项目
1 | 首先要确保Hadoop、Zookeeper、HBase、Redis、Elasticsearch这些服务都是正常的。 |
1 | 2:在ES中创建索引库:article,通过article.json文件指定索引库的settings和mapping。 |
1 | 注意:需要确认ES集群中已经集成了ik分词器,否则这里执行会报错。 |
1 | 3:在本地执行data_manager项目中的DataImport代码,将数据导入到HBase和Redis。 |
1 | 4:在本地执行data_manager项目中的DataIndex代码,在ES中建立索引。 |
1 | 重点关注_source字段中是否包含content字段,如果包含此字段内容说明前面的mapping配置有问题,如果不包含此字段内容说明是正确的。 |
1 | 5:对web_fullsearch项目打包并运行。 |
1 | 使用之前在学习ES课程的时候在bigdata04上部署的tomcat,将war包上传到tomcat的webapps目录下: |
1 | 启动tomcat: |

1 | 6:验证搜索功能。 |


5 项目中遇到的典型问题
1 | 单索引库查询效率降低的问题 |
1 | 如果确实需要查询历史以来所有的数据,在查询的时候可以通过索引库通配符实现所有数据查询,使用这个索引库通配符即可:article_*,这样可以查询所有以article_开头的索引库。 |


1 | 自定义词库导致的历史数据查询异常问题 |
1 | ES和HBase数据一致性问题 |
1 | 针对这个项目能不能只用ES? |
1 | ES和HBase中存储的数据有什么区别? |
