1 Elasticsearch的基本概念
1.1 索引
在使用传统的关系型数据库时,如果对数据有存取和更新操作,需要建立一个数据库。相应地,在ES中则需要建立索引。用户的数据新增、搜索和更新等操作的对象全部对应索引。
1.2 文档
在使用传统的关系型数据库时,需要把数据封装成数据库中的一条记录,而在ES中对应的则是文档。ES的文档中可以有一个或多个字段,每个字段可以是各种类型。用户对数据操作的最细粒度对象就是文档。ES文档操作使用了版本的概念,即文档的初始版本为1,每次的写操作会把文档的版本加1,每次使用文档时,ES返回给用户的是最新版本的文档。另外,为了减轻集群负载和提升效率,ES提供了文档的批量索引、更新和删除功能。
1.3 字段
一个文档可以包含一个或多个字段,每个字段都有一个类型与其对应。除了常用的数据类型(如字符串型、文本型和数值型)外,ES还提供了多种数据类型,如数组类型、经纬度类型和IP地址类型等。ES对不同类型的字段可以支持不同的搜索功能。例如,当使用文本类型的数据时,可以按照某种分词方式对数据进行搜索,并且可以设定搜索后的打分因子来影响最终的排序。再如,使用经纬度的数据时,ES可以搜索某个地点附近的文档,也可以查询地理围栏内的文档。在排序函数的使用上,ES也可以基于某个地点按照衰减函数进行排序。索引、文档和字段的逻辑关系如图1.1所示,从图中可以看出它们之间的包含关系。
1.4 映射
建立索引时需要定义文档的数据结构,这种结构叫作映射。在映射中,文档的字段类型一旦设定后就不能更改。因为字段类型在定义后,ES已经针对定义的类型建立了特定的索引结构,这种结构不能更改。借助映射可以给文档新增字段。另外,ES还提供了自动映射功能,即在添加数据时,如果该字段没有定义类型,ES会根据用户提供的该字段的真实数据来猜测可能的类型,从而自动进行字段类型的定义。
1.5 集群和节点
在分布式系统中,为了完成海量数据的存储、计算并提升系统的高可用性,需要多台计算机集成在一起协作,这种形式被称为集群,如图1.2所示。这些集群中的每台计算机叫作节点。ES集群的节点个数不受限制,用户可以根据需求增加计算机对搜索服务进行扩展。
1.6 分片
在分布式系统中,为了能存储和计算海量的数据,会先对数据进行切分,然后再将它们存储到多台计算机中。这样不仅能分担集群的存储和计算压力,而且在该架构基础上进一步优化,还可以提升系统中数据的高可用性。在ES中,一个分片对应的就是一个Lucene索引,每个分片可以设置多个副分片,这样当主分片所在的计算机因为发生故障而离线时,副分片会充当主分片继续服务。索引的分片个数只能设置一次,之后不能更改。在默认情况下,ES的每个索引设置为5个分片。
1.7 副分片
为了提升系统索引数据的高可用性并减轻集群搜索的负载,可以启用分片的副本,该副本叫作副分片,而原有分片叫作主分片。在一个索引中,主分片的副分片个数是没有限制的,用户可以按需设定。在默认情况下,ES不会为索引的分片开启副分片,用户需要手动设置。副分片的个数设定后,也可以进行更改。一个分片的主分片和副分片分别存储在不同的计算机上,如图1.3所示为一个3个节点的集群,某个索引设置了3个主分片,每个主分片分配2个副分片。图1.3中深色方框中的P表示该分片为主分片,R表示该分片为副分片,P和R后面的数字表示其编号。在极端情况下,当只有一个节点时,如果索引的副分片个数设置大于1,则系统只分配主分片,而不会分配副分片。
2 Elasticsearch和关系型数据库的对比
业界主流的数据产品分为两类,一类是传统的关系型数据库,另一类是非关系型数据库。ES属于非关系型数据库。在判断该使用ES还是关系型数据库之前,要先比较一下这两种不同类别的产品。
2.1 索引方式
关系型数据库的索引大多是B-Tree结构,而ES使用的是倒排索引,两种不同的数据索引方式决定了这两种产品在某些场景中性能和速度的差异。例如,对一个包含几亿条数据的关系型数据表执行最简单的count查询时,关系型数据库可能需要秒级的响应时间,如果数据表的设计不合理,甚至有可能把整个关系型数据库拖垮,影响其他的数据服务;而ES可以在毫秒级别进行返回,该查询对整个集群的影响微乎其微。再例如,一个需求是进行分词匹配,关系型数据库需要依靠其他的组件才能完成这种查询,查询的结果只能是满足匹配,但是不能按照匹配程度进行打分排序;ES建立在Lucene基础之上,与生俱来就能完成分词匹配,并且支持多种打分排序算法,还支持用户自定义排序脚本。
2.2 事务支持
事务是关系型数据库的核心组成模块,而ES是不支持事务的。ES更新文档时,先读取文档再进行修改,然后再为文档重新建立索引。如果同一个文档同时有多个并发请求,则极有可能会丢失某个更新操作。为了解决这个问题,ES使用了乐观锁,即假定冲突是不会发生的,不阻塞当前数据的更新操作,每次更新会增加当前文档的版本号,最新的数据由文档的最新版本来决定,这种机制就决定了ES没有事务管理。因此,如果你的需求是类似商品库存的精准查询或者金融系统的核心并发业务的支持,那么关系型数据是不错的选择。
2.3 扩展方式
假设随着业务的增长,我们的数据也迅速膨胀了几倍甚至几十倍。这时需要考虑数据产品扩展方式的难易程度。关系型数据库的扩展,需要借助第三方组件完成分库分表的支持。分库分表即按照某个ID取模将数据打散后分散到不同的数据节点中,借此来分摊集群的压力。但是分库分表有多种策略,需要使用人员对业务数据特别精通才能进行正确的选择。另外,分库分表会对一些业务造成延迟,如查询结果的合并及多表Join操作。ES本身就是支持分片的,只要初期对分片的个数进行了合理的设置,后期是不需要对扩展过分担心的,即使现有集群负载较高,也可以通过后期增加节点和副分片的方式来解决。
2.4 数据的查询速度
在少量字段和记录的情况下,传统的关系型数据库的查询速度非常快。如果单表有上百个字段和几十亿条记录,则查询速度是比较慢的。虽然可以通过索引进行缓解,但是随着数据量的增长,查询速度还是会越来越慢。ES是基于Lucene库的搜索引擎,可以支持全字段建立索引。在ES中,单个索引存储上百个字段或几十亿条记录都是没有问题的,并且查询速度也不会变慢。
2.5 数据的实时性
关系型数据库存储和查询数据基本上是实时的,即单条数据写入之后可以立即查询。为了提高数据写入的性能,ES在内存和磁盘之间增加了一层系统缓存。ES响应写入数据的请求后,会先将数据存储在内存中,此时该数据还不能被搜索到。内存中的数据每隔一段时间(默认为1s)被刷新到系统缓存内,此时数据才能被搜索到。因此,ES的数据写入不是实时的,而是准实时的。
3 Elasticsearch的架构原理
3.1 节点职责
节点按照职责可以分为master节点、数据节点和协调节点,每个节点类型可以进行单独配置。默认情况下,集群不会对节点角色进行划分,所有节点都是平等的,可以担任所有的职责。但是在生产环境中需要对这些节点的角色进行最优划分,否则在高并发请求的情况下,集群容易出现服务阻塞超时甚至服务崩溃的隐患。
master节点负责维护整个集群的相关工作,管理集群的变更,如创建/删除索引、节点健康状态监测、节点上/下线等。master节点是由集群节点通过选举算法选举出来的,一个集群中只有一个节点可以成为master节点,但是可以有一个或多个节点参与master节点的选举。在默认情况下,任意节点都可以作为master的候选节点,可以通过配置项node.master对当前节点是否作为master的候选节点进行控制。
数据节点主要负责索引数据的保存工作,此外也执行数据的其他操作,如文档的删除、修改和查询操作。数据节点的很多工作是调用Lucene库进行Lucene索引操作,因此这种节点对于内存和I/O的消耗比较大,生产环境中应多注意数据节点的计算机负载情况。
客户端可以向ES集群的节点发起请求,这个节点叫作协调节点。在默认情况下,协调节点可以是集群中的任意节点,此时它的生命周期是和一个单独的请求相关的。也就是说,当客户端向集群中的某个节点发起请求时,此时该节点被称为当前请求的协调节点;当它将响应结果返回给客户端后,该协调节点的生命周期就结束了。如图1.4所示的左右两图分别表示两次不同的请求,因为请求时客户端指定的请求地址不同,所以左图中的请求协调节点是node1,右图中的请求协调节点是node3。协调节点根据具体情况将请求转发给其他节点,并将最终的汇总处理结果返回给客户端。
当然,为了降低集群的负载,可以设置某些节点作为单独的协调节点。在节点的配置文件中设置node.master和node.data配置项为false,此时,这个节点就不会被选中为master节点并且不再担任数据节点,而客户端就可以把这类节点作为协调节点来使用,把所有的请求都分发到这些节点上,如图1.5所示。
3.2 主分片和副分片
ES为了支持分布式搜索,会把数据按照分片进行切分。一个索引由一个或者多个分片构成,并且每个分片有0个甚至多个副分片。多个分片分布在不同的节点中,通过这种分布式结构提升了分片数据的高可用性和服务的高并发支持。那么ES是如何提升分片数据的高可用性的呢?集群中的索引主分片和副分片在不同的计算机上,如果某个主分片所在的节点宕机,则原有的某个副分片会提升为主分片继续对外进行服务。例如,在如图1.3所示的集群中,如果node1发生故障宕机,集群感知到分片0的主分片P0将要丢失,此时集群会立即将其他节点(如node3)上的分片0对应的副分片R0作为主分片P0进行服务。集群中由node2和node3对外提供服务,所有的分片相关的服务不受影响,如图1.6所示。
如果node1恢复了服务并加入集群中,因为在node1上还保留有分片0的数据,此时node1上的分片P0会变成副分片R0,在此期间缺失的数据会通过node3上的主分片P0进行补充。并且node1上的分片R1和R2也会分别从node3和node2上对应的P1和P2分片上补充数据,如图1.7所示。
那么ES又是如何提升服务的高并发性能的呢?
当客户端对某个索引的请求被分发到ES的协调节点时,协调节点会将请求进行转发,转发的对象是包含这个索引的所有分片的部分节点。协调节点中有一份分片-节点路由表,该表主要存放分片和节点的对应关系。协调节点采用轮询算法,选取该索引的主/副分片所在的节点进行请求转发。一个索引的主分片设定后就不能再修改,如果想继续提升索引的并发性能,则可以增加索引的副分片个数,此时协调节点会将这些副分片加入轮询算法中。
3.3 路由计算
当客户端向一个ES协调节点发送一条数据的写入请求时,协调节点如何确认当前数据应该存储在哪个节点的哪个分片上呢?
协调节点根据数据获取分片ID的计算公式如下:
shard=hash(routing)%number_of_primary_shards
式中,routing代表每条文档提交时的参数,该值是可变的,用户可以自定义,在默认情况下使用的是文档的_id值;number_of_primary_shards是索引中主分片的个数。计算routing的哈希值后,除以索引的主分片数再取余,就是当前文档实际应该存储的分片ID。获取到分片ID后,根据分片-节点路由表获取该分片的主/副分片节点列表,然后再转发请求进行分片内的数据写入。
通过上面的公式可以观察到,主分片个数作为取余的分母不能进行更改,否则分片ID计算就会发生错误,进而导致找不到存储节点,这也是ES索引的主分片个数不能更改的原因。
3.4 文档读写过程
当ES协调节点接收到来自客户端对某个索引的写入文档请求时,该节点会根据一定的路由算法将该文档映射到某个主分片上,然后将请求转发到该分片所在的节点。完成数据的存储后,该节点会将请求转发给该分片的其他副分片所在的节点,直到所有副分片节点全部完成写入,ES协调节点向客户端报告写入成功。
如图1.8所示为一个包含3个节点的ES集群,假设索引中只有3个主分片和6个副分片,客户端向节点1发起向索引写入一条文档的请求,在本次请求中,节点1被称为协调节点。节点1判断数据应该映射到哪个分片上。假设将数据映射到分片1上,因为分片1的主分片在节点2上,因此节点1把请求转发到节点2上。节点2接收客户端的数据并进行存储,然后把请求转发到副分片1所在的节点1和节点3上,当所有副分片所在的节点全部完成存储后,协调节点也就是节点1向客户端返回成功标志。
当ES协调节点接收到来自客户端的获取某个索引的某文档的请求时,协调节点会找到该文档所在的所有分片,然后根据轮询算法在主/副分片中选择一个分片并将请求转发给该分片所在的节点,该节点会将目标数据发送给协调节点,协调节点再将数据返回给客户端。
如图1.9所示为一个包含3个节点的ES集群,假设索引中只有3个主分片和6个副分片,客户端向节点1发起向索引获取文档的请求,在本次请求中,节点1被称为协调节点。节点1判断数据应该映射到哪个分片上。假设将数据映射到分片1上,分片1有主/副两种分片,分别在节点2、节点1和节点3上。假设此时协调节点的轮询算法选择的是节点3,那么它会将请求转发到节点3上,然后节点3会把数据传输给协调节点,也就是节点1,最后由节点1向客户端返回文档数据。
3.5 搜索引擎
毫无疑问,ES最擅长的是充当搜索引擎,在这类场景中较典型的应用领域是垂直搜索,如电商搜索、地图搜索、新闻搜索等各类站内搜索。
创建索引时,业务系统模块把数据存储到数据库中,第三方数据同步模块(如Canal)负责将数据库中的数据按照业务需求同步到ES中。搜索时,前端应用先向搜索模块发起搜索请求,然后搜索模块组织搜索DSL向ES发起请求,ES响应搜索模块的请求开始搜索,并将搜索到的商品信息(如名称、价格、地理位置等)进行封装,然后把数据传送给搜索模块,进而数据再由搜索模块传递到前端进行展现,如图1.10所示。
3.6 推荐系统
ES在高版本(7.0及以上版本)中引入了高维向量的数据类型。可以把推荐模型算法计算的商品和用户向量存储到ES索引中,当实时请求时,加载用户向量并使用ES的Script Score进行查询,使每个文档最终的排序分值等于当前用户向量与当前文档向量的相似度。为同时满足实时向量计算和实时数据过滤的需求,可以在Script Score查询中添加filter(即过滤条件,如库存、上下架状态等)。ES推荐系统架构如图1.11所示。
3.7 日志分析
ES具有很强的查询能力,支持任意字段的各种组合查询,同时它又具有很强大的数据统计和分析能力,因此也可以当作数据分析引擎。ES官方提供的ELK(Elasticsearch+Logstash+Kibana)全家桶可以完成日志采集、索引创建再到可视化的数据分析等工作,使用户可以0代码完成搭建工作。ES支持的日志分析类型可以是多种多样的,生产中的用户行为日志、Web容器日志、接口调用日志及数据库日志等都可以通过ELK进行分析。如图1.13所示为ES官网提供的ELK-Stack架构层级,其中的Beats是新加入的成员,定位为轻量型的单一功能数据采集器。