elasticsearch之搭建篇

Elasticsearch是一个实时分布式搜索分析引擎,主要支持3种功能:

  • 全文搜索
  • 结构化搜索
  • 分析

本文的目标

ES作为一个检索系统,本身具有一定的领域复杂度,因此很难一次性将所有理论知识掌握透彻,为了更快更轻松的开始使用ES,本文将采用目标驱动的方式对ES进行一个初步的掌握。

  1. 搭建一个分布式的ES集群。
  2. 以移动版糯米m.nuomi.com的美食频道为例,看看该如何基于ES实现一个类似的东西:

搭建ES

先切换到root用户。

安装JAVA

jre

jdk

安装Maven

maven官网下载一个binary格式最新版本,解压后整个目录移动到/usr/local目录下:

并修改其配置使用阿里云镜像加速

安装git

用于后续下载代码:

安装unzip

用于后续解压文件:

修改用户进程/线程数限制

默认centos对普通用户限制1024个进程数,ES要求不低于2048。

下载ES

切换到普通用户,我这里使用work用户。

官方找到最新版本,我下载的是5.2.2版本:

我们搭建3个节点的分布式集群,它们均身兼master调度节点和data数据节点两个身份。

为此我们需要拷贝三份ES代码,虽然它们位于同一台服务器上,但是我要将按照分布式配置它们:

为它们创建各自的日志和数据目录,最终看起来是这样的:

配置ES

现在我们首先配置elasticsearch0,而elasticsearch1和elasticsearch2可以拷贝它并稍作修改即可。

config/elasticsearch.yml

  • cluster.name:集群名,防止其他集群的ES误入本集群。
  • node.name:确保每个ES节点有独一无二的名字,方便追查问题。
  • path.data:数据存储目录。
  • path.logs:日志存储目录。
  • network.host:服务的监听IP地址。
  • http.port:提供给客户端的RESTful Http端口。
  • transport.tcp.port:ES节点之间互相通讯。
  • discovery.zen.ping.unicast.hosts:启动后访问这些节点来加入集群
  • discovery.zen.minimum_master_nodes:至少有2个备选master节点参与选举才能产生master。

关于ES的上述核心配置,可以通过这个博客学习与理解,我只说明一下比较难理解的配置项:

  • discovery.zen.minimum_master_nodes:
    • ES节点默认同时扮演备选master调度节点和data数据节点两个身份,我的集群里目前有3个备选master,它们同时也是data节点,真正的master通过它们3个选举产生。
    • 如果后续扩充集群节点,应该明确指定它们仅扮演数据节点!
    • 这个选项配置为2,表示master必须在至少2个备选master之间产生,这是为了防止master之间网络分割造成”脑裂”。
      • 如果配置为1,那么不同网络分区内的备选master都认为自己是真正的master;
      • 如果配置为3,那么一旦发生网络割裂,谁都无法成为master,均是不合理的;
      • 如果配置为2,那么当2个备选master进入一个网络分区时,它们可以选出分区内的master,而另一个网络分区因为只有1个master,所以无法进行选举,因此可以防止”脑裂”。
  • discovery.zen.ping.unicast.hosts:
    • ES节点启动后必须加入到集群,新版的ES会访问配置的ES服务器列表并逐个尝试加入,因此保证列表内服务器至少有一个是可用的很重要。
    • 这里配置成3个master节点,因为在我规划的集群里只有3个ES节点的身份是备选master,通常会保障它们的可用性远高于后续追加的普通data节点。
config/jvm.options

默认的ES配置分配了2G的jvm堆内存,由于我在虚拟机环境下测试内存有限,所以将其修改为512M即可:

至此,你可以把上述2个配置文件拷贝到其他2个ES目录中,并对其他2个配置文件稍作修改,最终结果如下:

elasticsearch1

elasticsearch2

安装中文分词

我们使用这个开源项目实现中文分词,它是ES的一个插件。

首先下载代码:

之后切换到对应ES版本的release分支,我的ES是5.2.2版本,因此:

现在用mvn编译这个插件:

我的编译产物存储在:

将其解压到3个ES目录的插件目录下:

启动ES

分别进去3个ES目录并启动它们:

验证ES

访问ES的健康状态接口:

返回结果表明nuomi-search集群的状态是green(健康的),一共有3个节点,其中3个作为datanodes。

确认IK分词插件已经正常加载:

安装PHP客户端

用命令行CURL访问ES服务太过于麻烦,为了更贴近生产环境,我会使用PHP的ES客户端来实现功能。

因此,我们需要先安装php(ES的5.x版本客户端要求php版本为5.6或者7.0):

之后我们需要安装composer,目的是通过它安装ES客户端,先创建一个项目目录叫做nuomi-search:

在目录内下载composer主程序(如果你不懂composer并且感兴趣,可以看我的这篇博客):

为composer替换国内镜像:

现在安装ES官方的PHP客户端(官方文档):

安装完成后应该可以看到如下目录结构:

我们创建测试用的php文件,通过客户端连接到我们的ES集群,查看集群健康状况以及当前有哪些数据库(index):

执行程序,应该可以看到如下输出:

至此,ES服务与PHP客户端都搭建完成,本文最后将分析一下需求。

需求分析

经过对”糯米美食”频道的观察,发现它的搜索功能有4个核心能力:

  • 搜索”肯德基”,会列出所有的肯德基”商铺“。
  • 搜索”鱼香肉丝”,会列出所有售卖”鱼香肉丝”的”商铺“。
  • 在搜索的基础上,可以按距离筛选一定范围内的”商铺“。
  • 在搜索的基础上,”商铺“可以按照评分/距离…等进行排序。

在搜索方面可以得出一个结论:用户搜索的关键字,无论其含义是一个”商铺”名称还是一个”商品”名称,最终都应该返回相关联的”商铺”列表。

很明显,”商铺”和”商品”之间存在一个关联关系,1个”商铺”有N个”商品”,并且搜索时应该能基于”商品”进行筛选得到满足条件的”商铺”,类似于mysql中的一次JOIN+WHERE。

那么,在ES中有2种功能都是支持这种关联查询的,它们分别是:

  • 嵌套关系(Nested),它的优缺点如下:
    • 优点:
      • 对于主要实体(如商铺)加上有限数量的紧密相关实体(如商品)来说,可以很快的根据商品信息筛选出店铺信息。
      • 可以同时基于商铺与商铺的商品两部分信息进行筛选。
    • 缺点
      • 增删改任意嵌套的商品,需要重新索引整个商铺的数据。
      • 总是返回商铺与其所有商品的完整数据。
  • 父子关系(Parent-Child)
    • 优点:
      • 修改店铺数据,无需重新索引商品数据。
      • 增删改查商品数据,无需重新索引商铺的数据。(这对于商品数量庞大的场景很重要)
    • 缺点:
      • 比嵌套查询慢5-10倍。
      • 商品检索时关联的商铺_id会放在内存里计算,有内存限制。
      • 只能基于商品信息筛选出商铺,或者基于商铺信息筛选出商品。(无法做到这样的效果:基于商铺与其售卖的商品共同筛选出店铺)

看完各自的优缺点后,我们知道ES也不是万能药,必然有其技术限制,因此怎么将自己的业务场景转换到ES的存储上来就是我们必须考虑的问题了。

ES不是Mysql,我们知道Mysql的表之间彼此JOIN查询是很寻常的事情;但是ES作为一个检索系统更强调扁平化,并不强力的支持JOIN类查询,我们应该更多的利用数据冗余的思路将需要JOIN的数据打平(冗余)到一个表中,这样就可以基于一个表的检索完成本来需要JOIN多个表的需求了。

对于上述的嵌套关系(Nested)来讲,其实就是一种扁平化思路,商铺与属于商铺的商品将被存储在同一个表(type)中,可以很方便基于商铺信息与所属的商品信息完成搜索。

对于上述的父子关系(Parent-Child)来讲,其实就是一种JOIN思路,店铺和商品直接隔离在两个表(type)中各自独立索引,同时ES会在商品表中维护一个_parent字段指向所属的商铺记录,从而满足一些类似JOIN的查询任务。

那么,我们要模仿的糯米检索应该使用哪种技术呢?

商品数量不是很多,增删改查的频率不是很高,同时希望同时按照商铺名称和商品名称完成筛选,似乎嵌套关系(Nested)更加适合,但是父子关系就一定不行了吗?

当然不是,借助数据冗余的思路,我们完全可以把商铺的各种信息(例如:商铺名称,商铺评分,商铺人均消费…等)全部冗余到每个商品记录中去,这样就可以先对商品表进行”商铺名称”+”商品名称”的一次检索,找出最匹配的若干”商铺名称”,之后再去”商铺表”将这些”商铺名称”对应的详细”商铺”信息取回来即可。不过缺点也很明显,每次商铺信息变动,要对属于它的每个商品进行一次冗余信息更新,这就是扁平化的代价。

实践环节

简化后的糯米检索包含如下2类数据:

  • 商铺:
    • 商铺名称
    • 商铺图片
    • 商铺类型
    • 用户评分
    • 人均价格
    • 地理坐标
  • 商品:
    • 商品名称
    • 商品图片
    • 商品类型
    • 商品价格
    • 已售数量

为了更顺畅的阅读后面的博客,建议你先学习一遍官方文档

具体内容未来请见:《elasticsearch之实例篇

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~