elasticsearch分布式工作原理前言Elasticsearch是分布式的,但是对于我们开发者来说,并没有过多的参与其中。我们只需要启动相应数量的节点,并给它们分配相同的cluster.name,让它们属于同一个集群即可。创建索引时,只需要指定索引的主从分片个数,剩下的交给ES内部实现。这和同源的数据库的分布和solr的分布是不一样的。数据库需要集群分布。从同步等,solr的分发也需要依赖zookeeper,但是Elasticsearch完全屏蔽了这些。虽然Elasticsearch本质上是分布式的,在设计时就屏蔽了分布式的复杂性,但我们还是需要知道它的内部原理。节点交互的原理和其他中间件的原理是一样的,比如主从模式的mysql、redis。es集群也会选举一个节点作为master节点。主节点的职责是维护全局集群状态,并在节点加入或离开集群时重新分发分片。所有文档级写操作都不会与主节点通信。主节点不需要涉及文档级别的更改和搜索。ES分布不像mysql的主从模式。mysql写在主库,然后同步数据到从库。但是es文档的写操作是在shard而不是node上的。它首先写在主分片上,然后将主分片同步到辅助分片。因为primaryshard可以分布在不同的节点上,当集群只有一个master节点的情况下,即使流量增加,也不会成为瓶颈。即使挂了,任何一个节点都有机会成为主节点。Read和Write可以请求任意节点,节点将请求转发给目的节点。比如添加一个文档,通过路由算法将文档分配到一个primaryshard,然后找到对应的节点将数据写入primaryshard,然后同步到secondaryshard。写入文档客户端向节点1发送新文档请求。节点通过文档的路由算法确定文档属于主分片-P0。因为主分片P0在node-3上,请求将被转发到node-3。该文档被添加到节点3的主分片P0上。添加成功后,请求转发到node-1和node-2对应的secondaryshard-R0。一旦所有二级分片都报告成功,node-3向node-1报告成功,node-1向客户端报告成功。读取文档客户端向节点1发送读取文档请求。在处理读请求时,node-1会针对每个请求轮询所有副本分片,以实现负载均衡。elasticsearch文档的路由原理前言当一个新的文档被添加时,该文档会存储在一个primaryshard中。Elasticsearch如何知道文档应该存储在哪个分片中?当我们创建一个文档时,它是如何决定该文档应该存放在shard1还是shard2中的呢?RoutingAlgorithm首先这个肯定不会是随机的,不然以后我们要获取文档的时候不知道去哪里找。其实这个过程是根据下面的公式来确定的:shardrouting是一个变量值,默认是文档的_id,也可以设置为自定义值。路由通过hash函数生成一个数,然后用这个数除以number_of_primary_shards(primaryshards的个数)得到余数。这个分布在0和number_of_primary_shards-1之间的剩余部分就是我们要查找的文档所在的分片的位置。这就解释了为什么我们在创建索引的时候一定要确定primaryshards的数量,并且永远不要改变这个数量:因为如果这个数量改变了,之前所有的路由值都会失效,文档也不会再找到。不再。添加一个新文档(指定id)PUT/nba/_doc/1{"name":"Harden","team_name":"FireArrows","position":"ShootingGuard","play_year":"10","jerse_no":"13"}检查文档在哪个分片上GET/nba/_search_shards?routing=1"nodes":{"V1JO7QXLSX-yeVI82WkgtA":{"name":"node-1","ephemeral_id":“_d96PgOSTnKo6nrJVqIYpw”,“传输地址”:“192.168.1.101:9300”,“属性”:{“ml.machine_memory”:“8589934592”,“xpack.installed”:“true”,“max.max_open_jobs”:“20”}},"z65Hwe_RR_efA4yj3n8sHQ":{"name":"node-3","ephemeral_id":"MOE_Ne7ZRyaKRHFSWJZWpA","transport_address":"192.168.1.101:9500","attributes":{"ml.machine_memory":"8589934592","ml.max_open_jobs":"20","xpack.installed":"true"}}},"indices":{"nba":{}},"shards":[{“state”:“STARTED”,“primary”:true,“node”:“V1JO7QXLSX-yeVI82WkgtA”,“relocating_node”:null,“shard”:2,“index”:“nba”,“allocation_id”:{“id":"leX_k6McShyMoM1eNQJXOA"}},{"state":"STARTED","primary":false,"node":"z65Hwe_RR_efA4yj3n8sHQ","relocating_node":null,"shard":2,"index":"nba","allocation_id":{"id":"6sUSANMuSGKLgcIpBa4yYg"}}]]}elasticsearch对乐观锁的简单分类分析,所以每次拿到数据的时候都会加锁,这样别人想拿到数据就会阻塞,直到拿到锁。传统的关系型数据库使用很多这样的锁机制,比如行锁、表锁、读锁、写锁等,都是先加锁再进行操作。乐观锁,顾名思义,就是非常乐观。每次去拿数据,你以为别人不会修改,所以不会上锁。但是更新的时候会判断这段时间别人有没有更新过数据。例如,可以使用版本号等机制。乐观锁适用于读次数较多的应用,可以提高吞吐量。因为我们的elasticsearch业务场景普遍要求少写多读,乐观锁在控制并发的同时可以有效提升性能。系统吞吐量。版本号乐观锁Elasticsearch会为文档的索引、GET和删除请求返回一个_version,当文档被修改时版本号会递增。所有文档更新或删除API都可以接受版本参数,这允许您在代码中使用乐观并发控制。这里注意版本号一定要大于旧版本号,加上version_type=external。获取文档GET/nba/_doc/1{"_index":"nba","_type":"_doc","_id":"1","_version":1,"_seq_no":4,"_primary_term":7、"found":true,"_source":{"name":"Harden","team_name":"FireArrows","position":"ShootingGuard","play_year":"10","jerse_no":"13"}}按版本号添加新文档(版本必须大于旧版本)POST/nba/_doc/1?versinotallow=2&version_type=external{"name":"Harden","team_name":"FireArrow","position":"ShootingGuard","play_year":"10","jerse_no":"13"}浅谈elasticsearch的分词原理Preface1我们创建一个文件PUTtest/_doc/1{"msg":"Jordanisthegodofbasketball"}我们通过关键字'Jordan'POST/test/_search{"query":{"match":{"msg":"Jordan"}}}我们发现可以匹配文档,那么整个过程的原理是什么呢?前言2让我们尝试使用中文分词器PUTtest/_mapping{"properties":{"msg_chinese":{"type":"text","analyzer":"ik_max_word"}}}POSTtest/_doc/1{"msg":"乔丹是篮球之神","msg_chinese":"乔丹是篮球之神"}POST/test/_search{"query":{"match":{"msg_chinese":"Joe"}}}POST/test/_search{"query":{"match":{"msg":"Joe"}}}为什么同样输入'Joe',为什么msg能匹配到文档,而msg_chinese却不能?在写分词的时候,我们用它来分析msg字段是如何分词的POSTtest/_analyze{"field":"msg","text":"Jordanisthegodofbasketball"}Joe,Dan,是的,basketball,ball,andGod,我们来分析一下msg_chinese字段是如何分词的POST测试/_analyze{"field":"msg_chinese","text":"Jordanisthegodofbasketball"}乔丹,没错,篮球之神,文档会根据field设置的tokenizer类型进行分词,如果not指定的是默认的标准分词器。写的时候需要在mapping中指定分词器,一旦指定就不能修改。如果你想修改它,你必须重建索引。阅读时分词由于默认阅读时分词与写作时分词相同,以上面的例子为例,如果搜索msg字段,则阅读时分词为Standard,而此时搜索msg_chinese的是ik_max_word。这个默认设置也很容易理解。只有使用一致的tokenizer进行读写,才能尽可能匹配分词的结果。一般允许设置POSTtest/_search{"query":{"match":{"msg_chinese":{"query":"Jordan","analyzer":"standard"}}}}不需要阅读时指定分词符。如果读取时没有单独设置分词器,则读取时分词器的校验方法与写入时相同。深度分析分析器(analyzer)分为三部分:charfilter:字符过滤器tokenizer:tokenizertokenfilter:tokenfiltercharfilter(字符过滤器)characterfilter接收字符流形式的原文,并可以添加、删除或更改字符以转换流。分析器可能有零个或多个字符过滤器。tokenizer(tokenizer)分词器获取字符流,将其拆分为单个标记(通常是单个单词),然后输出标记流。例如,使用whitespacetokenizer会在遇到空格时将文本拆分为标记。“吃苹果”>>[eating,and,apple]。一个分析器必须只有一个tokenizerPOST_analyze{"text":"eatinganapple","analyzer":"whitespace"}令牌过滤器(tokenfilter)令牌过滤器接收令牌流,并可能添加、删除或更改令牌。例如,小写标记过滤器可以将所有标记转换为小写。分析器可能有0个或多个令牌过滤器,它们按顺序应用。标准分析器tokenizerStanardtokenizertokenfifiltersStandardTokenFilterLowerCaseTokenFilter
