最开始使用ElasticSearch时,一般都是创建一个索引,导入数据,然后发送查询命令检索数据。我们确信系统运行庚子,至少在最开始,数据量不大而且QPS(Query Per Second)也不高的时候运行良好。在幕后,ElasticSearch创建了一些分片来存储数据,也可能还会创建分片副本(例如,如果用默认配置),而且用户在配置方面也不用过多地操心。
当应用程序规模增长起来,越来越多的数据需要进行索引,QPS也变得越来越高。这个时候,量变就引起质变了。慢慢地问题也就出现了(读者可以阅读 知识的灵活运用一节的内容来了解应用程序规模增长的对应方法) 。这时候,就该思想如何规划索引数据、优化配置使得应用程序的处理能力与规模增长相同步。在本章,作者将给出一些应对相关问题的指导方针。然而对应问题并没有一个统一的标准,不同的应用场景有着不同的特点和需求,这些特点和需求不仅体现在索引结构上,同时也体现在配置上面。例如:整个索引的数据规模、查询类型、预期的吞吐量都是不尽相同的。
关于索引分片,在第一章 认识ElasticSearch里面已经有介绍,这里简单回顾一下。索引分片就是把索引数据切分成多个小的索引块,这些小的索引块能够分发到同一个集群中的不同节点。在检索时,检索的结果是该索引每个分片上检索结果的总和(尽管在某些场景中“总和”并不成立:单个分片有可能存储了所有的目标数据)。默认情况下,ElasticSearch会为每个索引创建5个主分片,就算是单结点集群亦是如此。像这样的冗余就称为过度分配:这种场景下,这种分配方式似乎是多此一举,而且只会在索引数据(把文档分发到多个分片上)和检索(必须从多个分片上查询数据然后全并结果)的时候增加复杂度,乐享其成的是,这些复杂的事情是自动处理的,但是为什么ElasticSearch要这样做呢?
假定我们有一个构建在单个分片上的索引。这意味着如果我们的应用程序规模增长到单机无法处理的情况下时,问题就来了。当索引中有数据时,当前版本的ElasticSearch是无法将分片中的数据切分成多个小块的:我们只能在创建索引的时候指定好分片的数量。唯一的解决办法就是重新创建一份多个分片的索引,然后将原来的数据重新索引(动词)到新的索引(名词)中。然而这样的处理方案需要时间和服务器资源,比如CUP时间、内存、大容量存储器。而且有可能我们并没有时间和上面提到的那些资源。换个角度,通过使用过度分配策略,我们可以在必要的时候添加一台新的安装了ElasticSearch的服务器。当新的节点添加进来时,ElasticSearch会自动对集群进行负载均衡,在不需要重新索引数据的前提下将部分索引数据转移到新的机器上。ElasticSearch作者设置的默认配置(5个主分片和一个分片副本)是权衡了数据增长的可能性和合并不同分片数据的最终开之后的最终选择。
默认配置适用于大部分场合。那么问题来了:当我们在什么时个需要增加或者减少分片数量,让分片数量尽可能少一些?
第一个问题的答案显而易见。如果数据集的大小有限制而且严格定义好的,可以只使用一个分片。如果不是的,大拇指法则表明最佳的分片数量取决于节点数量。所以,如果计划将会到10个节点,那么就需要配置10个分片。需要记住的重点是:考虑到高可用性和吞吐量,分片副本也是需要声配置的。分片副本像普通分片一样也占用空间。如果为每个分片指定一个拷贝(number_of_shards =1 ),那么就需要20个分片:10个主分片和10个分片副本。这个简单的公式可以总结如下:
Max number of nodes = Number of shards * (number of replicas +1)
换句话说,如果你计划用10个分片和2个分片副本,那么最大的节点数是30。
如果读者仔细阅读了本章前面部分的内容,应该确信配置最小数量的分片是明智的选择。但是有时分配更多的分片却比较方便,因为每个分片都是独立的Lucene索引,更多的分片意味着在单个较小的索引上进行操作(特别是数据索引操作)会比较高效。对于有的应用场景来说,上文是选择多分片不错的理由。当然多分片同时也带了额外的开销:分发搜索命令到每个分片以及分片结果的合并。这个开销在某些特定的应用场景是可以避免的,这类应用场景的一个特点是:查询命令总是会被具体的参数过滤。像多租户系统,每个查询命令都被限定在确定用户上下文中。解决问题的思想很简单:把每个用户的数据都存储在一个分片上,而且只在查询的时候使用该分片。这也是ElasticSearch的路由功能大显身手的地方(我们将在本章的 路由功能浅谈 一节详细讨论这一知识点)。
也许读者会感到疑惑,如果说分片实际上是一段小的Lucene索引,那么真实的ElasticSearch索引是什么呢?它们之间的区别是什么呢?从技术上来说,它们是一样的,但是会有一个额外的特性只能工作在多索引 或者 多分片结构上。对于数据分片,可以通过路由功能或者在指定的分片上执行查询命令功能实现将查询命令定位到特殊的分片上去。对于多索引结构,更通用的机制在于多索引地址的处理上,查询语句可以通过/index1,index2.../符号联合多个索引进行查询。在查询时,用户也可以通用使用别名(aliasing)特性将多个索引以一个索引的方式呈现给用户,索引分片时也可以用别名这个功能。在负载均衡处理逻辑上,容易发现两者之间更多的不同点,然而自动化程度较低的多索引可以在索引数据时,以通过人工操作强行将多个索引部署在一个节点上的方式部分地隐藏这一点。
索引分片机制用来存储超过单个节点存储容量的数据,分片副本用来应对不断攀升的吞吐量以及确保数据的安全性。当一个节点的主分片丢失,ElasticSearch可以把任意一个可用的分片副本推举为主分片。在默认情况下,ElasticSearch会创建一个分片副本。然而分片副本的数量可以通过设置相关的API随时更新,这一点与主分片是不同的。分片副本的动态更新功能使得创建应用程序时十分方便,查询吞吐量可以随着分片副本数量的增加而增长,与此同时,使用分片副本还可以处理查询的发并量。使用分片副本的缺点也是显而易见的:额外的存储空间开销,从主分片复制数据到分片副本时的开销。选定分片副本的数量时,还需要考虑到现阶段需要多少副本。如果分片副本的数量太多,那么就会浪费存储空间和ElasticSearch的资源,实际上一些分片副本并没有起作用。相反地,如果没有设置分片副本,如果主分片出现了故障,数据就会丢失。