GENGEN
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
  • java基础

    • JDK8 函数式编程
    • JDK8 新特性之Date-Time
    • Servlet 源码分析
    • ArrayList 源码
    • LinkedList 源码
    • HashMap 源码
    • String 源码
    • BigDecimal 源码
    • java 类的加载
    • Class 源码
    • Synchronized锁升级
    • 事务的传播机制
    • knowledge
  • JAVA WEB

    • Java Servlet
    • 权限设计
    • logback日志的链路追踪
  • DATABASE

    • MySQL EXPLAIN详解
    • MySQL 索引
    • MySQL 表锁、行锁
    • MySQL ACID与transcation
    • 分布式事务
    • MySQL MVCC机制
    • Mysql 乐观锁与悲观锁
    • 分布式锁1 数据库分布式锁
    • 分布式锁2 Redis分布式锁
    • 分布式锁3 ZK分布式锁
  • SpringCloud

    • SpringCloud服务注册中心之Eureka
    • SpringCloud服务注册中心之Zookeeper
    • SpringCloud服务调用之Ribbon
    • SpringCloud服务调用之OpenFeign
    • SpringCloud服务降级之Hystrix
    • SpringCloud服务网关之Gateway
    • SpringCloud Config分布式配置中心
    • SpringCloud服务总线之Bus
    • SpringCloud消息驱动之Stream
    • SpringCloud链路追踪之Sleuth
    • SpringCloud Alibaba Nacos
    • SpringCloud Alibaba Sentinel
  • Spring

    • SpringBoot
    • Spring-data-jpa入门
    • SpringCloud问题
    • dispatcherServlet 源码分析
    • @SpringBootApplication注解内部实现与原理
    • spring启动初始化初始化
  • 中间件

    • 分布式协调服务器Zookeeper
    • 服务治理Dubbo
    • 分布式配置管理平台Apollo
    • 消息中间件框架Kafka
    • 分布式调度平台ElasticJob
    • 可视化分析工具Kibana
    • ElacticSearch 基础
    • ElacticSearch进阶
    • ElacticSearch集成
  • 环境部署

    • 应用容器引擎Docker
    • DockerCompose服务编排
    • 负载均衡Nginx
    • Nginx的安装配置
    • K8S基础
  • 代码片段

    • listener 监听模式
    • spingboot 整合redis
    • XSS过滤
    • profile的使用
    • ConfigurationProperties注解
  • 设计模式

    • 工厂模式
    • 单例模式
    • 装饰者模式
    • 适配器模式
    • 模板方法模式
    • 观察者模式
  • 读书笔记

    • 《Spring in Action 4》 读书笔记
    • 《高性能mysql》 读书笔记
  • NoSQL

    • Redis基础
    • Redis高级
    • Redis集群
    • Redis应用
  • MQ

    • rabbitMQ基础
    • rabbitMQ高级
    • rabbitMQ集群
  • JVM

    • JVM体系架构概述
    • 堆参数调整
    • GC 分代收集算法
    • JVM 垃圾回收器
    • JVM 相关问题
  • JUC

    • JUC总览
    • volatile关键字
    • CAS
    • ABA问题
    • collections包下线程安全的集合类
    • Lock 锁
    • LockSupport
    • AQS
    • Fork/Join分支框架
    • JUC tools
    • BlockingQueue 阻塞队列
    • Executor 线程池
    • CompletableFuture
    • 死锁以及问题定位分析
  • Shell

    • shell命令
    • shell基础
  • Activiti

    • IDEA下的Activiti HelloWord
    • 流程定义的CRUD
    • 流程实例的执行
    • 流程变量
  • VUE

    • vue基础
    • vue router
    • Vuex
    • Axios 跨域
    • dialog 弹出框使用
    • vue 动态刷新页面
    • vue 封装分页组件
    • vue 动态菜单
    • vue 常用传值
  • Solidity 智能合约

    • Solidity 基础
    • Solidity ERC-20
    • Solidity 101
  • English

    • 时态

ElacticSearch进阶

集群部署

  • 集群提供可扩展的容量,且高可用,并发处理高,生产环境应用运行在集群中

Windows集群

Linux集群

ES核心

接近实时 NRT

  • ES是一个接近实时的搜索平台,这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(1s以内)

集群 cluster

  • 一个集群就是由一个或者多个节点组织一起的,它们共同持有整个数据,并提供索引和搜索功能。一个集群由一个唯一的名字来标识,这个名字默认‘elactisearch’,这个名字是重要的,因为节点的加入必须指定某个集群的名字。

节点 node

  • 集群包含多个节点,它参与集群的索引和搜索功能
  • 节点也是有名称的,默认情况下是一个随机的漫威角色名,这个名字会在节点启动时赋予。节点名称对于管理也是重要的,在管理过程中,需要确定网络中哪些服务器对于集群中的哪些节点。
  • 一个节点可以通过配置集群名称加入集群,如果你启动了若干节点,并假定它们彼此互相发现,那么它们就自动的形成并加入集群中。

分片 shards

  • 分片:
    • 分片就是,有一个大的索引,单节点没有这么大容量或者放单节点上处理搜索很慢,为了解决这个问题,es设置分片,允许一个索引放置在多个节点上。
    • 分片很重要,主要的原因有两个:1)允许你水平分割/拓展你的内容容量 2)允许你分片(潜在地位于多个节点上)之上进行分布式,并行的操作,提高吞吐量

副本 replicas

  • 在一个网络、云环境中,失败随时可能发生,在某个分片/节点挂掉时,有一个故障转移机制是非常有用的
  • 某个节点挂了,有一个故障转移很重要。为此目的,ES允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者复制
  • 复制很重要,主要的原因有两个:1)在节点失败的情况下,提供了高可用 2)拓展搜索量/吞吐量
  • 复制不与原分片/主要分片置于同一节点是重要的

分配 allocation

  • 将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由master节点完成的

系统架构

dock
# 假设我们设置一个users的索引,设置三个分片,每个分片对应一个副本
curl -XPUT "http://localhost:9200/users/_settings" -d'
{
  "settings":{
    "number_of_shards:": 3,
    "number_of_replicas": 1
  }
}
'

故障转移

  • 当集群只有一个节点时,意味着会有单点故障问题。
    • 当你在同一台集群启动另一个节点,只要第二个节点具有相同的cluster.name,它会自动发现加入。
    • 在不同机器上启动节点,为了加入集群,需要配置一个可连接到单播主机列表。之所以配置为使用单播发现,是防止其它节点无意中加入集群

水平扩容

  • 水平扩容,当启动三个节点,为了分散负载会对分片进行重新分片
    • 如果是一个或两个节点,三个分片每个节点都会有,当节点为三个,则三个分片会两两分配到三个节点上,保证每个节点上,任意一个分片异常,都有别的节点上副本提供备用

Tips

  • 假设三个分片0、1、2,三个节点A、B、C
    • A节点上分片 0、1
    • B节点上分片 0、2
    • C节点上分片 1、2
  • 分片是一个功能完整的搜索引擎,它拥有使用的一个节点上所有资源的能力。我们现在拥有6个节点(3主分片,3副本分片)的索引最大可扩展到6个节点,每个节点上一个分片,并且每个分片拥有所在节点的全部资源。

  • 如果我们想扩容超过6个节点怎么办?

    • 主分片数目在索引创建时已经确定了(不可变),只有3个。这个定义的数目已经确定了数据存储的最大量(取决数据大小、硬件大小、使用场景)。但是读操作,是可以同时从主分片和副本分片处理。意味着,你可以扩展大的副本节点,使吞吐量变大
#条件副本分片为2,即三个分片拥有6个副本
curl -XPUT "http://localhost:9200/users/_settings" -d'
{
  "number_of_replicas":2
}
'
# 这种情况下,极限节点数就是9台,3主+6副本

应对故障

  • 假设从Node挂掉,只会影响分片数,不会影响数据的CRUD。从节点恢复以后,继续加入主节点
  • 如果主Node挂掉,其它从Node会选举一个主Node,也不会影响数据的CRUD。挂掉的节点再加入就变成了从Node

ES的数据写入和读取

  • 写入

    • 客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node(协调节点)
    • coordinating node 对 document 进行路由,将请求转发给对应的 node(primary shard)。
    • 实际的 node 上的 primary shard 处理请求,然后将数据同步到 replica node。
    • coordinating node 如果发现 primary node 和所有 replica node 都搞定之后,就返回响应结果给客户端。
    • 写入数据,判断写入哪个主分片叫做路由计算 :hash(id)%主分片数
  • 读取

    • 可以通过 doc id 来查询,会根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询
    • 客户端发送请求到任意一个 node,成为 coordinate node。
    • coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
    • 接收请求的 node 返回 document 给 coordinate node。
    • coordinate node 返回 document 给客户端。

倒排索引结构

  • 前面提过,倒排索引就是根据value找key,为此我们举个例子
    • 假设有个users索引,它有四个字段,分别是name|gender|age|address。画出来大概是个这个样子,以关系型数据库一样

id | name | gender | age | address

  • | :-: | :-: | :-: | -: 1 | 张三 | 1 | 22 | 宝鸡市陈仓区 2 | 李四 | 2 | 21 | 西安市长安区
    3 | 王五 | 1 | 23 | 西安市雁塔区
  • Term单词:一段文本经过分析器分析后输出的一串单词,就是一个个的Term

  • Term Dictionary字典:顾名思义,就是保存Term的字典。

  • Posting List倒排列表:记录出现过某个Term的所有Documents文档列表以及该Term在文档位置,每条记录都称为一个倒排项Posting

    • 实际的Posting List不仅仅保存文档ID这么简单,还保存了其它信息,比如次频、偏移量等
  • Term Index单词索引:为了更好找到某个单词,我们为Term单词建立索引

  • 上面的例子,ES建立的索引大致如下:

  • name字段

Term | Posting List |

  • | :-: | :-: | :-: | -: 张三 | 1 | 李四 | 2 | 王五 | 3 |

    • age字段

Term | Posting List |

  • | :-: | :-: | :-: | -: 21 | 2 | 22 | 1 | 23 | 3 |

    • gender字段

Term | Posting List |

  • | :-: | :-: | :-: | -: 1 | [1,3] | 2 | 1 |

    • address字段

Term | Posting List |

  • | :-: | :-: | :-: | -: 宝鸡市 | 1 | 西安市 | [2,3] |
    陈仓区 | 1 |
    雁塔区 | 3 |
    长安区 | 2 |
  • ES会为每个字段都建立一个倒排索引,上面的张三、西安市、22都是Term,而[2,3]就是Posting List,存储了所有符合某个Term的文档ID
  • 如何根据关键词找到Term?这就需要给Term建立索引,类似MySQL的B+Tree结构
  • 倒排索引中,我们根据Term索引可以找到Term在Term Dictionary的位置,进而找到Posting List,然后找到 Documents的ID
    • 类比MySQL,Term Index可以理解为索引,Term Dictionary理解为数据

文档搜索

  • 早期全文检索会为整个文档Documents建立一个很大的倒排索引并写入磁盘,一旦新的索引就绪,旧的会被替换,这样保证最近的变化可以被检索到
  • 倒排索引写入磁盘后是不可改变的,目的如下:
    • 不需要锁,因为没有写入,就只有读
    • 索引被读入内核文件缓存,大部分的请求直接访问缓存,不会命中磁盘
    • 其它缓存(filter缓存),在索引生命周期内始终有效,不需要每次数据改变被重建
    • 写入单个大的倒排索引允许数据压缩,减少磁盘I/O和需要被缓存到内存的使用量
  • 当然不变的倒排索引也有缺点,即创建后,如果要修改,就需要重建整个倒排索引,如果更新频率高,则性能有很大影响
  • 如何保证不变性前提下实现倒排索引更新?
    • 答案是:建立更多的倒排索引,即通过创建新的倒排索引来补充修改的内容,而不是直接全部重写。每个倒排索引都会被查询,合并后返回
  • 逻辑删除:这样的话,一个倒排索引就可能包含多段,既然段不可改变,删除的数据也是不能真正在磁盘中删除的。所以我们会对删除的数据进行标记(逻辑删除)。这个缺点就会导致无效数据量可能累计过大。
  • 合并:解决一个倒排索引中多个段无效数据过多的问题,对数据进行整理合并。

文档刷新&文档刷写&文档合并

  • 近实时搜索:
    • 随着按段(per-segment)搜索的发展,提交Commiting一个新的段到磁盘需要一个fsync来确保数据写入磁盘。保证数据不丢失。但是这个操作代价很大,如果每个索引都执行一次开销很大。
    • 我们有更轻量化方式使文档可被搜索。即修改的内容先在内存缓存区OS Cache中被写入一个新的段中。然后再被刷新到磁盘。到磁盘这一步代价较高,不过文件以及在缓存区OS Cache中了,就已经可以被读取。保证了近实时。
    • 内存中数据写入到OS Cache过程叫refresh,效率很高。进入以后就可以提供给用户查询了,而写入磁盘的flush较慢。flush中包含了文档的合并

Tips

  • 数据写入磁盘,协调节点找到主分片,并发写入副本分片
    • 延时:主分片的延时+并行写入副本的最大延时

文档分析

  • 文档分析包含以下过程

    • 将一个词条分成合适的倒排索引独立的Term
    • 将这些词条统一化成标准格式提高可搜索性
  • 以上操作由分析器来操作,分析器实际是将三个操作封装到包里:

    • 字符过滤器:字符串按顺序通过每个字符过滤器。它们任务是在分词前整理字符串。一个字符过滤器可以用来去掉HTML、或者将&转化成and
    • 分词器:字符串会被分词器分为单个的Term,一个简单的分词器遇到空格或者标点时,可能会对文本拆分
    • Token过滤器:词条按顺序通过Token过滤器,这个过程可能会改变词条(例如,Like变小写),删除词条(例如a、the、and无效词条),或者增加词条(例如,jump和leap这种同义词)
  • 内置分词器

    • ES提供了一些预装的分词器,主要的如下:
      • 标准分词器:这是ES的默认分词器。它分析各种语言最常用文本选择,删除绝大部分标点,将词条小写。
      • 简单分词器:在任何不是字母的地方进行分隔,词条小写
      • 空格分词器:在空格的地方分词
      • 语言分析器:特定语言分析器可用于多种语言,它可以考虑语言特点,例如分析英语中无效的a\the\and等
  • 分析器使用场景

    • 当我们索引一个文档,它的全文域会被分析成Term来创建倒排索引。但当我们用全文域搜索时。需要将查询字符串通过相同的分析过程,以保证我们搜索词条格式和索引中Term一致
# 检测分词器分词效果
curl -XGET "http://localhost:9200/_analyze" -d'
{
	"analyzer":"standard",
	"text":"Text to analyze"
}
'
# 返回结果中token是实际存储的Term,position记录Term在原始文本的位置,start_offset\end_offset记录偏移量
  • 指定分析器IK
    • 中文分词,采用IK分词器,下载地址 https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v7.8.0
    • 解压文件放入ES根目录下plugins目录下,重启ES
  • 测试效果
# ik_max_word将文本拆成最细粒度的
curl -XGET "http://localhost:9200/_analyze" -d'
{
	"analyzer":"ik_max_word",
	"text":"中国人"
}
'
# ik_max_word中Term会分为`中国人` `中国` `国人`

#ik_smart 将文本最粗粒度拆分
curl -XGET "http://localhost:9200/_analyze" -d'
{
	"analyzer":"ik_smart",
	"text":"中国人"
}
'
#ik_smart中Term只有一个就是`中国人` 
  • ES也可以自己对分词进行扩展
    • 进入ES的plugins目录的ik文件夹,进入config目录,创建custome.dic文件,写入自定义分词
    • 打开IKAnalyzer.cfg.xml文件,将新建custom.dic配置其中,然后重启ES
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">custom.dic</entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

文档控制

  • 文档冲突:当我们使用index API更新文档,可以一次性读取原始文档,做修改。然后重新索引整个文档。最后的索引请求将被执行。如果同时他人也在更改整个文档,他们的更改会丢失。
  • 如果是全量更新,最终执行的是一个人的修改数据。如果是局部更新,可能A成员改了a部分数据,B成员改了b部分数据,这就有问题了。
  • 悲观并发控制:这种方式在关系型数据库被广泛使用,简单来说就是操作数据时,对资源加锁
  • 乐观并发控制:ES假设这种冲突不太可能发生,并且不会阻塞数据操作。如果源数据读写过程中被修改,更新将会失败,应用程序将决定如何处理失败。例如尝试更新、使用新的数据,或者将问题报告给用户 。
    • put文档时,会返回_seq_no和_primary_term字段,用来做乐观锁(老版本用version)
curl -XPOST "http://localhost:9200/shopping/_doc/1001?if_seq_no=0&if_primary_term=1" -d'
{
	"name":"华为",
	"price":"4999"
}
'

#还有一种用version的方式,即你的version需要大于文档的version
curl -XPOST "http://localhost:9200/shopping/_doc/1001?version=3&version_type=external" -d'
{
	"name":"华为",
	"price":"4999"
}
'

文档展示-kibana

  • 下载安装kibana
  • 修改配置config/kibana.yml文件
#默认端口
server.port:5601
#配置ES服务器地址
elasticsearch.hosts:["http://localhost:9200"]
#索引名
kibana.index:".kibana"
#支持中文
i18n.locale:"zh-CN"
Last Updated:
Contributors: wal365@126.com
Prev
ElacticSearch 基础
Next
ElacticSearch集成