ElasticSearch进阶篇

x33g5p2x  于2020-11-02 转载在 ElasticSearch  
字(10.1k)|赞(0)|评价(0)|浏览(1092)

1、IK分词器

1.1 IK Analyzer概述

IK Analyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。

做搜索技术的不可能不接触分词器。为什么搜索引擎无法被数据库所替代的原因主要有两点,一个是在数据量比较大的时候,搜索引擎的查询速度快,第二点在于,搜索引擎能做到比数据库更理解用户。第一点好理解,每当数据库的单个表大了,就是一件头疼的事,还有在较大数据量级的情况下,你让数据库去做模糊查询,那也是一件比较吃力的事(当然前缀匹配会好得多),设计上就应当避免。关于第二点,搜索引擎如何理解用户,肯定不是简单的靠匹配,这里面可以加入很多的处理,甚至加入各种自然语言处理的高级技术,而比较通用且基本的方法就是靠分词器来完成,而且这是一种比较简单而且高效的处理方法。

分词技术是搜索技术里面的一块基石。很多人用过,如果你只是为了简单快速地搭一个搜索引擎,你确实不用了解太深。但一旦涉及效果问题,分词器上就可以做很多文章。例如, 在我们实际用作电商领域的搜索的工作中,类目预判的实现就极须依赖分词,至少需要做到可以对分词器动态加规则。再举一个简单的例子,如果你的优化方法就是对不同的词分权重,提高一些重点词的权重的话,你就需要依赖并理解分词器。

1.2 IK分词器的安装和配置

Maven安装

由于需要使用到Maven对IK分词器进行打包,因此,要先安装和配置Maven,下载地址http://maven.apache.org/download.cgi/#。这里以最新版本apache-maven-3.6.1-bin.tar为例,将Maven安装包上传到主机hadoop221的/root/tools目录中。进入到/root/tools目录,运行命令 tar -zxvf apache-maven-3.6.1-bin.tar.gz -C /root/training/,将Maven安装包解压到/root/training目录下。运行命令vi /root/.bash_profile,编译环境变量,在文件末尾添加如下内容:

MAVEN_HOME=/root/training/apache-maven-3.6.1

export MAVEN_HOME

PATH=$MAVEN_HOME/bin:$PATH

export PATH

保存退出后,运行命令source /root/.bash_profile,使环境变量生效。再运行命令mvn -version,输出的信息如下图所示,表明Maven安装成功。

IK Analyzer安装

这里以elasticsearch-analysis-ik-master.zip安装包为例,下载地址为https://github.com/medcl/elasticsearch-analysis-ik,注意,IK分词器的安装过程需要能够连接网络。

将IK安装包上传到主机hadoop221的/root/tools目录中,运行命令unzip elasticsearch-analysis-ik-master.zip -d /root/training/,将IK安装包解压到/root/training目录下。进入到/root/training/elasticsearch-analysis-ik-master目录下,运行命令mvn package -Pdist,native -DskipTests -Dtar,使用Maven进行打包,打包完成之后,在/root/training/elasticsearch-analysis-ik-master/target/releases目录下会出生成elasticsearch-analysis-ik-5.6.1.zip文件,直接运行命令unzip elasticsearch-analysis-ik-5.6.1.zip,解压到当前目录,运行命令cp -r elasticsearch /root/training/elasticsearch-5.6.1/plugins/,将解压得到的文件拷贝到elasticsearch-5.6.1/plugins/目录下。

最后,需要修改plugin-descriptor.properties文件,将其中的es版本号改为当前使用的版本号,即完成IK分词器的安装。进入到/root/training/elasticsearch-5.6.1/plugins/elasticsearch目录,运行vi plugin-descriptor.properties,将其中elasticsearch.version=5.6.1修改为当前ES的版本即可。

至此,完成IK分词器的安装,重启ElasticSearch即可。

1.3 IK分词器的使用

默认中文分词器

针对词条查询(TermQuery),查看默认中文分词器的效果如下图所示。

ik_smart模式

使用ik_smart模式查看中文分词的效果如下图所示。

ik_max_word模式

使用ik_max_word模式查看中文分词器的效果如下图所示。

1.4 IK分词器Java API操作

初始化ElasticSearch客户端连接

创建索引

创建使用IK分词器的mapping

以map形式创建文档

词条查询

2、ElasticSearch的分布式架构原理

ElasticSearch的分布式架构如下图所示。

要点总结如下:

对于一个索引,其中的数据会被拆分成多个shard(分片),每个shard存储一部分数据;
1.
对于每个shard的数据,实际会有多个备份,也就是说每个shard都有一个primary shard,负责写入数据,还有几个replica shard,仅用于读数据。primary shard写入数据之后,会将数据同步到其他几个replica shard上去;
1.
通过这个replica方案,每个shard的数据都有多个备份,如果某个机器宕机了,还有别的数据副本可供访问,从而实现了高可用;
1.
对于ES集群中的多个节点,会根据配置文件中的属性配置自动选举一个节点作为master节点,这个master节点主要就是干一些管理的工作,比如维护索引元数据、负责切换primary shard和replica shard身份等等。要是master节点宕机了,那么会重新选举一个节点为master节点;
1.
如果是非master节点宕机了,那么会由master节点,将那个宕机节点上的primary shard身份转移到其他机器上的replica shard。接着,要是修复了那个宕机机器,重启之后,master节点会控制将缺失的replica shard分配过去,并同步后续修改的数据,让集群恢复正常。

3、ES写入数据及查询数据的工作原理

下面这幅图总结了ElasticSearch写入数据及查询数据的工作流程。

各个过程的详细说明如下:

(1)ES写入数据流程

客户端选择一个node发送请求过去,这个node作为coordinating node(协调节点);
*
coordinating node对document进行路由,将请求转发给对应的node(有primary shard的node);
*
实际上由node上的primary shard处理写入请求,然后将数据同步到其他node上(包含对应replica shard的node);
*
coordinating node,如果发现primary node和所有replica node都完成数据写入后,就返回响应结果给客户端。

(2)ES读取数据过程

ES读取数据,本质上是查询(GET)某一条数据,当写入某条document,这个document会被自动分配一个全局唯一的id,即doc id,同时ES也是根据doc id进行hash路由到对应的primary shard上面去。当然,也可以手动指定doc id,比如使用订单id或用户id。可以通过doc id来查询,根据doc id进行hash,判断出先前把该doc id分配到了哪个shard上,进而从那个shard去查询。

客户端发送请求到任意一个node,作为coordinate node;
*
coordinate node对该请求进行路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica shard中随机选择一个进行处理,从而让读请求实现负载均衡;
*
接收请求的node返回document给coordinate node;
*
coordinate node返回document给客户端。

(3)ES搜索数据过程

ES最强大的功能是做全文检索,比如有三条数据:

Java是一门简单易学的编程语言

Java编程好容易上手

J2ee当前非常流行

根据Java关键词来进行搜索,将包含Java的document全部给搜索出来,ES就会给你返回:Java是一门简单易学的编程语言,Java编程好容易上手。

客户端发送请求到一个node,作为coordinate node;
*
coordinate node将搜索请求转发给所有的shard对应的primary shard或某个replica shard;
*
query phase:每个shard将自己的搜索结果(其实就是一些doc id),返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,生成最终结果;
*
fetch phase:接着由协调节点,根据doc id去各个节点上拉取实际的document数据,返回给客户端。

(4)ES写数据底层原理

ES写入数据时,是先将数据写入内存缓冲区buffer中,当数据在buffer里的时候是搜索不到的,同时也会将数据写入translog日志文件中;
*
如果buffer快满了,或者到一定时间,就会将buffer中的数据refresh到一个新的segment file文件中,但是此时数据不是直接写入segment file的磁盘文件,而是先写入os cache中,这个过程就是refresh;

默认每隔1秒钟,ES将buffer中的数据写入一个新的segment file,每秒钟会生生一个新的磁盘文件segment file,这个segment file中就存储最近1秒内buffer中写入的数据。但是,如果buffer里面没有数据,那就不会执行refresh操作,避免创建一个空的segment file,如果buffer里面有数据,默认1秒钟执行一次refresh操作,刷入一个新的segment file中。

操作系统里面,磁盘文件其实都有一个缓存,叫做os cache,即操作系统缓存,当数据写入磁盘文件之前,会先写入os cache,先写入操作系统级别的一个内存缓存中。只要buffer中的数据被refresh操作,刷入os cache中,就表明这个数据可以被搜索到了。这也就是为什么称ES是准实时的(NRT,near real-time,默认每隔1秒refresh一次,写入的数据1秒之后才能被搜索到)。可以通过ES的restful api或者java api,手动执行一次refresh操作,手动将buffer中的数据刷入os cache中,让数据可以立马被搜索到。只要数据被刷入os cache中,buffer就会被清空,因为不需要保留buffer了,数据在translog里面也已经被持久化到磁盘里了。

只要数据刷入os cache中,就可以让这个segment file的数据对外提供搜索了;
*
重复上面3个步骤,新的数据不断进入buffer和translog,不断将buffer中的数据写入一个又一个新的segment file中去,每次refresh完buffer清空,translog保留。随着这个过程的推进,translog会变得越来越大。当translog达到一定长度的时候,就会触发commit操作;

buffer中的数据,每隔1秒就被刷到os cache中去,然后这个buffer就被清空了。所以说这个buffer的数据始终是可以保持不会填满ES进程的内存。每次一条数据写入buffer,同时会写入一条日志到translog日志文件中,所以这个translog日志文件会不断变大,当translog日志文件大到一定程度的时候,就会执行commit操作。

commit操作发生的第一步,就是将buffer中现有数据refresh到os cache中,清空buffer,并将一个commit point写入磁盘文件,里面标识着这个commit point对应的所有segment file;
*
强制将os cache中目前所有的数据都fsync到磁盘文件中;

translog日志文件的作用是什么?就是在执行commit操作之前,数据要么是停留在buffer中,要么是停留在os cache中,无论是buffer还是os cache都是内存,一旦这台机器宕机,内存中的数据就会全部丢失。所以需要将数据对应的操作写入一个专门的日志文件,translog日志文件中,一旦此时机器宕机,再次重启的时候,ES就会自动读取translog日志文件中的数据,恢复到内存buffer和os cache中。commit操作:写commit point--->将os cache数据fsync强刷到磁盘中--->清空translog日志文件;

将现有的translog文件清空,然后再次重新启用一个translog文件,此时commit操作完成。默认每隔30分钟会自动执行一次commit,但是如果translog过大,也会触发commit。整个commit的过程,叫做flush操作。可以手动执行flush操作,会将所有os cache数据强制刷到磁盘文件中。

ES中的flush操作,对应着commit的全过,也可以通过ES API,手动执行flush操作,手动将os cache中的数据fsync强刷到磁盘中,记录一个commit point,清空translog日志文件。

translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中,所以默认情况下,可能有5秒的数据会仅仅停留在buffer或者translog文件的os cache中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须直接fsync到磁盘中,但是性能会差很多。

如果希望一定不能丢失数据的话,可以设置相应的参数。每次写入一条数据,都是写入buffer,同时写入磁盘上的translog,但是这会导致写入性能、写入吞吐量下降一个数量级。本来一秒钟可以写2000条,现在可能一秒钟就只能写200条,都是有可能的。

如果是删除操作,commit的时候会生成一个.del文件,里面将某个doc标识为deleted状态,那么搜索的时候根据.del文件就知道这个doc被删除了;
*
如果是更新操作,就是将原来的doc标识为deleted状态,然后新写入一条数据;
*
buffer每refresh一次,就会产生一个segment file,所以默认情况下是1秒钟一个segment file,segment file的数量会越来越多,对于这种情况,ES会自动或定期执行merge操作;

每次merge的时候,会将多个segment file合并成一个,同时会将标识为deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里也会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segment file。ES的写流程,有4个底层的核心概念,refresh、flush、translog、merge。

4、ElasticSearch性能优化

下图简单给出了ElasticSearch查询数据的流程,可通过该图体会ES的性能瓶颈在什么地方。

(1)性能优化的杀手锏——filesystem cache

os cache,操作系统缓存,往ES里写的数据,实际上都写到磁盘文件里去了,磁盘文件里的数据操作系统会自动将里面的数据缓存到os cache中。ES的搜索引擎严重依赖于底层的filesystem cache,如果给filesystem cache更多的内存,尽量让内存可以容纳所有的indx segment file索引数据文件,那么搜索的时候就基本都是走内存的,性能会非常高。性能差距可以有多大,如果走磁盘,一般需要花费的时间会达到秒级别,1秒,5秒,10秒。但是如果走filesystem cache,走纯内存,那么一般来说性能比走磁盘要高一个数量级,基本上就是毫秒级的,从几毫秒到几百毫秒不等。

比如说,ES节点有3台机器,每台机器,看起来内存很多,64G,总内存,64 /* 3 = 192G,每台机器给ES JVM堆栈32G内存,那么剩下来留给filesystem cache就是每台机器才32G,总共集群里给filesystem cache就是32 /* 3 = 96G内存。如果此时,磁盘上索引数据文件,在3台机器上,一共占用了1T的磁盘容量,ES数据量是1T,每台机器的数据量是300G,你觉得性能会好吗?filesystem cache的内存才100G,十分之一的数据可以放在内存中,其他数据都在磁盘,然后执行搜索操作,大部分操作都是走磁盘,性能肯定非常差。

归根结底,要让ES性能好,最佳的情况下,就是机器的内存,至少可以容纳总数据量的一半。比如说,一共要在ES中存储1T的数据,那么所有机器留给filesystem cache的内存加起来至少要达到512G,至少半数的情况下,搜索是走内存的,性能一般可以到几秒钟,2秒,3秒,5秒。最佳的情况下,是仅仅在ES中就存少量的数据,也就是要用来搜索的那些索引数据,留给filesystem cache的内存就100G,那么就控制在100G以内,相当于,搜索的数据几乎全部走内存,性能会非常高,一般可以在1秒以内。

仅仅只是写入ES中要用来检索的少数几个字段就可以了,然后可以把其他的字段数据存在mysql里面,一般是建议用ES + HBase的架构。HBase的特点是适用于海量数据的在线存储,就是对HBase可以写入海量数据,不要做复杂的搜索,仅做很简单的一些根据id或者范围进行查询的操作就可以。ElasticSearch减少数据量,仅仅存放要用于搜索的几个关键字段即可,尽量写入ES的数据量跟ES机器的filesystem cache差不多,其他不用来检索的数据放HBase里,或者MYSql中。

(2)数据预热

假如说,哪怕就按照上述的方案去做,ES集群中每个机器写入的数据量还是超过了filesystem cache一倍,比如说写入一台机器60G数据,结果filesystem cache就30G,还是有30G数据留在了磁盘上。举个例子,比如说,微博,可以把一些大v,平时看的人很多的数据给提前刷到filesystem cache中,可以后台搞个系统,每隔一段时间,这个后台系统去搜索一下热数据,刷到filesystem cache中,后面用户实际来看这个热数据的时候,就是直接从内存里搜索了,速度会很快。电商,可以将平时查看最多的一些商品,比如说Iphone 8等热数据提前刷到filesystem cache中。

对于那些觉得比较热的,经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据,每隔一段时间,就提前访问一下,让数据进入filesystem cache里面去。这样下次别人访问的时候,一定性能会好很多。

(3)冷热分离

关于ES性能优化,数据拆分,将大量不搜索的字段,拆分到别的存储中。ES可以做类似于MYSql的水平拆分,就是说将大量的访问很少,频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在filesystem os cache里,不会让冷数据给冲刷掉。

假设有6台机器,2个索引,一个放冷数据,一个放热数据,每个索引3个shard,3台机器放热数据index;另外3台机器放冷数据index。这样的话,大多数时候是在访问热数据index,热数据可能就占总数据量的10%,此时数据量很少,几乎全都保留在filesystem cache里面了,就可以确保热数据的访问性能是很高的。但是对于冷数据,是存放在别的index里,跟热数据index都不在相同的机器上,大家互相之间都没什么联系。如果有人访问冷数据,可能大量数据是在磁盘上,此时性能会差点,但就只有10%的人去访问冷数据;90%的人去访问热数据。

(4)document模型设计

在ES里尽量不要用复杂的关联查询语法,一旦用了,性能一般都不太好。如果不可避免这种情况,在负责写入ES的Java系统中,提前就完成关联操作,将关联好的数据直接写入ES中,搜索的时候,就不需要利用ES的搜索语法去完成join等操作来搜索了。

document模型设计是非常重要的,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。ES能支持的操作就只有那么多,不要考虑用ES做一些它不擅长操作的事情。如果真的有那种操作,尽量在document模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都比较差。

1)在写入数据的时候,就设计好模型,加几个字段,把处理好的数据写入增加的字段中;

2)用Java程序封装,ES能做的,就用ES来做,搜索出来的数据,在Java程序里面去做,比如说,基于ES,用Java封装一些特别复杂的操作。

(5)分页性能优化

ES的分页是比较坑的,为啥呢?举个例子,假如每页是10条数据,现在要查询第100页,实际上是会把每个shard上存储的前1000条数据都查到一个协调节点上,如果有5个shard,那么就有5000条数据,接着协调节点对这5000条数据进行一些合并、处理,再获取到最终第100页的10条数据。

分布式的,要查第100页的10条数据,不可能说从5个shard,每个shard就查2条数据,最后到协调节点合并成10条数据。必须得从每个shard都查1000条数据过来,然后根据需求进行排序、筛选等操作,最后再次分页,拿到里面第100页的数据。翻页的时候,翻的越深,每个shard返回的数据就越多,而且协调节点处理的时间越长。非常坑爹。所以用ES做分页的时候,会发现越翻到后面,就越慢。如果非要使用ES做分页,需要遵循如下两个条件:

1)不允许深度分页/默认深度分页性能很差

2)类似于app里的推荐商品不断下拉出来一页一页的

类似于微博中,下拉刷微博,刷出来一页一页的,你可以用scroll api,scroll会一次性生成所有数据的一个快照,然后每次翻页就通过游标移动,获取下一页数据,性能会比上面说的那种分页性能高很多很多。针对这个问题,可以考虑用scroll来进行处理,scroll的原理实际上是保留一个数据快照,然后在一定时间内,如果不断的滑动往后翻页,那么就用scroll不断通过游标获取下一页数据,这个性能是很高的,比ES实际翻页要好的多得多。但是唯一的一点就是,这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。同时这个scroll是要保留一段时间内的数据快照,需要确保用户不会持续不断翻页翻几个小时之久。无论翻多少页,性能基本上都是毫秒级的,因为scroll api是只能一页一页往后翻的,不能说,先进入第10页,然后去120页,再回到58页,不能随意乱跳页。所以现在很多产品,都是不允许随意翻页的,有些app,也有一些网站,做的就是只能往下拉,一页一页的翻。

5、ElasticSearch生产集群的部署架构

下面列举一个基本的实际生产环境中的ElasticSearch集群结构。

(1)ES生产集群部署5台机器,每台机器是8核64G的,集群总内存是320G;

(2)ES集群的每日增量数据大概是2000万条,占用空间大概是500M,每月增量数据大概是6亿条,占用空间15G。系统已经运行了几个月,ES集群里数据总量大概是100G左右。

(3)线上有5个索引,每个索引的数据量大概是20G,在这个数据量之内,每个索引分配8个shard,比默认的5个shard多了3个shard。

相关文章