摘要:Elasticsearch
《Elasticsearch搜索引擎构建入门与实战》第一章读书笔记
Elasticsearch特色优势
-
实时性好
:相比于Lucene,新增的数据数秒甚至1秒(缓存)内可以搜索到 -
分布式架构设计
:可以拓展到多台机器进行搜索 -
提供REST风格API
,相比于Lucene需要Java开发,ES支持HTTP请求来进行查询 -
提供了聚合查询
:ES可以对索引中的数据做聚合统计分析
Elasticsearch基本概念
-
索引
:要对进行数据存储和查询操作需要先建立索引,ES中的索引相当于传统数据库的库 -
文档
:相当于传统数据库的一条记录,就是ES中一个文档,一个文档可以包含一个或多个字段,每个字段可以有各种类型,文档的初始版本是1,每次写操作会自动版本号+1,查询时ES返回最大版本号的文档 -
字段
:常见的类型包括字符串,文本,数值,还有ES提供其他类型,比如数组,地理经纬,IP地址等,不同的数据类型ES支持不同的所有功能,比如文本可以基于分词搜索,地理经纬可以所有某点附近的文档 -
映射
:映射就是定义的数据结构schema,一旦设定之后不能更改,ES也提供了自动映射的功能,如果要写入的数据没有给定类型ES会自动推断 -
集群和节点
:多台机器一起协作作为集群,ES集群的节点数不受限制,一个节点就对应一台机器 -
分片
:ES会对数据进行切分存储到多台计算机中,均匀分摊单台机器的存储压力,ES默认一个索引5个分片,分片一定设置是不可以修改的,只能新建新索引解决,一个节点可以被分配多个主分片。每个分片可以设置副分片,当主分片故障离线时副分片会充当主分片继续提供服务,保证了高可用 -
副分片
:在一个索引中主分片的副分片个数没有限制,默认ES不会启用副分片。一个主分片的所有副分片都存储在不同的机器上,保证一台机器宕机的情况下,其他机器的副分片可以提供服务。因此如果只设置了一个ES节点,且启动了至少一个副分片,实际上ES只会分配一个主分片,不会分配副分片
如上图,三个主分片P0,P1,P2,各自的副分片R0,R1,R2都分别在另外的机器上,一个三个节点存储分别分配了一个主分片,主分片的备份数量是1
-
DSL
:领域特定语言,是用来定义查询的,HTML,CSS,SQL都属于DSL,ES中DSL使用JSON表达,查询的返回数据也是JSON格式
Elasticsearch和传统关系型数据库对比
-
索引方式不同
:关系型数据库大多是B-Tree索引,ES是倒排索引 -
事务支持
:ES不支持事务支持,ES使用乐观锁,不阻塞数据的更新操作,每次更新采用增加版本号的方式,导致某些更新操作可能失效,数据未更新成功。
悲观锁和乐观锁
- 悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,是一种串行的方式,只要开始执行就会锁表,一般数据库本身锁的机制都是基于悲观锁的机制实现的
- 乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现),乐观锁并不会真的加锁,而是通过一些状态的检验达到操作互斥的效果
例如有以下数据采用乐观锁的方式,用户A,B分别查询Name=zhangsan的这条记录,并进行修改再次写入数据库
因为采用乐观锁,因此A,B都可以并行地拿到数据,用户A拿到之后将Name修改为lisi,在提交操作时,数据库会把之前查询到的version与现在的数据的version进行比较,版本相同则可以提交,版本不同则视为数据过期,过期则提交失败,如以下SQL更新语句
update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version}
更新成功后数据库该记录变为
此时用户B将Name改为wangwu,再提交时会失败,因为自身verson=1,而数据库内的version=2,数据过期
-
查询语句不同
:关系型数据库采用SQL,原因是查询比较简单直接,ES采用DSL来完成比较复杂的查询所有需求 -
查询速度
:如果字段少,数据量不打,关系型数据库查询速度很快,但是单表有上百字段和几十亿条数据查询速度就会变慢,随着数据量的增长速度越来越慢,ES可以支持对全字段做索引,单个索引存储上百字段或几十亿条数据查询速度都不会变慢 -
数据的实时性
:关系型数据库是实时的,插入之后立马可以查到,ES内存中的数据先写入缓存,默认1s之后统一刷入磁盘,才能被ES查到,因此ES是准实时的
Elasticsearch架构原理
(1)节点的职责
节点按照职责可以分为master节点
,数据节点
,协调节点
,每个节点的角色可以单独配置,默认每台机器都可以担任这三种角色。
-
master节点
:维护ES集群工作,如创建删除索引,节点上线下线,健康状态监测等,master通过选举算法产生,在候选的机器中只能有一个master节点,可以配置node.master
为true将当前节点作为master节点的候选 -
数据节点
:负责数据保存,修改,删除,查询,数据节点的工作是调用Lucene库进行索引操作,对内存和I/O消耗比较大 -
协调节点
:负责对接客户端请求,默认情况下协调节点可以是集群中的任一节点,当一个请求开始时该节点作为协调节点存在,请求结束该协调节点生命结束。协调节点将请求转发给其他节点,最终汇总结果输出。为了降低集群负荷,可以设置某些节点作为单独的协调节点,将这些节点的node.master
,node.data
全部设置为false即可,所有的数据请求都会发到这些单独设置的协调节点,例如图中的Client Node就是协调节点
(2)主分片和副分片
ES中索引由一个或多个分片组成,一个分片可以拥有0个或多个副分片,保证了高可用。
例如当node1发生宕机,P0消失,集群感知到P0消失之后会找到node3的R0充当为新的P0,此时只有node2和node3对外提供服务。
当node1恢复时,node1上面的主分片和副分片会分别从node2,node3同步数据,node1本来的P0变为R0
(3)文档读写流程
写流程:
ES协调节点接受客户端请求,默认根据文档的_id值通过路由计算获得目标主分片,和主分片所在的节点,将客户端请求转发,写入该节点的主分片,然后对于该主分片的副分片,分别找到所在的节点再写入,等主副全部写好了,ES协调节点告诉客户端写入完成
路由计算:
路由计算的目的是对于客户端发来的文档,找到文档要读取或者写入的主分片和主分片所在的节点,实际上是计算的分片ID,公式如下
shard=hash(routing)%number_of_primary_shards
routing
:客户端提交的参数,默认是文档的_id
值
number_of_primary_shards
:索引中的主分片个数
相当于就是对文档hash取模计算出到底去哪个分片,计算出主分片ID之后,ES的协调节点上存储了一份节点-分片的对照表,根据这个表找到主分片和副分片所在的机器节点,找到节点进行写入操作
读流程:
当客户端收到请求文档的读取要求时,同样根据路由算法找到对应的主分片,协调节点根据对照表找到所有该主分片以及对应的副分片的节点,然后使用轮询算法从主/副中选取一个,数据传递给协调节点,再返回给客户端
轮询算法:
轮询算法是最简单的一种负载均衡算法。它的原理是把来自用户的请求轮流分配给内部的节点,从节点1开始,直到节点N,然后重新开始循环。
Elasticsearch安装
(1)单机安装
进入该网站下载对应的压缩包 https://www.elastic.co/cn/downloads/elasticsearch
开始解压
tar -zxvf elasticsearch-8.0.0-linux-x86_64.tar.gz
cd elasticsearch-8.0.0
ll
total 884
drwxr-xr-x 9 root root 4096 2月 4 00:55 ./
drwxr-xr-x 11 root root 4096 2月 26 19:37 ../
drwxr-xr-x 2 root root 4096 2月 4 00:55 bin/
drwxr-xr-x 3 root root 4096 2月 26 19:37 config/
drwxr-xr-x 9 root root 4096 2月 4 00:55 jdk/
drwxr-xr-x 3 root root 4096 2月 4 00:55 lib/
-rw-r--r-- 1 root root 3860 2月 4 00:47 LICENSE.txt
drwxr-xr-x 2 root root 4096 2月 4 00:52 logs/
drwxr-xr-x 65 root root 4096 2月 4 00:55 modules/
-rw-r--r-- 1 root root 858789 2月 4 00:52 NOTICE.txt
drwxr-xr-x 2 root root 4096 2月 4 00:52 plugins/
-rw-r--r-- 1 root root 2710 2月 4 00:47 README.asciidoc
ES不允许root用户启动,需要创建一个用户,并且将整个目录修改拥有者为该用户
root@ubuntu:/opt/elasticsearch-8.0.0# useradd -s /bin/bash /us^C
root@ubuntu:/opt/elasticsearch-8.0.0# useradd -s /bin/bash -m es
root@ubuntu:/opt/elasticsearch-8.0.0# passwd es
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@ubuntu:/opt/elasticsearch-8.0.0# su es
修改拥有者
root@ubuntu:/opt/elasticsearch-8.0.0# chown -R es /opt/elasticsearch-8.0.0
es默认配置进程占用内存1GB,如果机器内存不足可以修改配置
# 打开 confog/jvm.options
-Xms512m
-Xmx512m
关闭ssl认证和用户名密码认证
# config/elasticsearch.yml
xpack.security.http.ssl:
enabled: false
xpack.security.enabled: false
下面启动es
root@ubuntu:/opt/elasticsearch-8.0.0/bin# su - es
es@ubuntu:~$ cd /opt/elasticsearch-8.0.0/bin/
es@ubuntu:/opt/elasticsearch-8.0.0/bin$ ./elasticsearch
http访问9200端口
root@ubuntu:~# curl http://127.0.0.1:9200
{
"name" : "ubuntu",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "9T7jMwqUR_qSchEC6Hpn0w",
"version" : {
"number" : "8.0.0",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "1b6a7ece17463df5ff54a3e1302d825889aa1161",
"build_date" : "2022-02-03T16:47:57.507843096Z",
"build_snapshot" : false,
"lucene_version" : "9.0.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
目录下新增的data目录就是存储索引数据的文件
(2)安装Kibana
Kibana提供ES的查询可视化功能,同时提供DEV Tools,可以与ES进行交互请求。Kibana版本最好es版本一致
wget https://artifacts.elastic.co/downloads/kibana/kibana-8.0.0-linux-x86_64.tar.gz
解压,求改拥有者
tar -zxvf kibana-8.0.0-linux-x86_64.tar.gz
mv kibana-8.0.0 /opt/
chown -R es /opt/kibana-8.0.0
在kibana的配置文件中,默认elasticsearch.hosts为对应的ES HTTP服务地址
#elasticsearch.hosts: ["http://localhost:9200"]
如果允许其他计算机访问kibana,修改server.host为0.0.0.0
#server.host: "localhost"
然后先启动es,再启动kibana
es@ubuntu:/opt/kibana-8.0.0$ cd bin/
es@ubuntu:/opt/kibana-8.0.0/bin$./kibana
http端口号5601打开kibana界面
Elasticsearch快速开始
以下操作全部基于kibana的console
(1)创建索引
通过PUT请求,JSON中定义mappings,三个字段分别是text(文本),keyword(关键词),double(小数)类型
PUT /my_index
{
"mappings": {
"properties": {
"title":{
"type": "text"
},
"city":{
"type": "keyword"
},
"price": {
"type": "double"
}
}
}
}
(2)写入文档
使用POST请求,加入请求体JSON数据,指定文档的_id为001
POST /my_index/_doc/001
{
"title":"好再来酒店",
"city": "青岛",
"price": 578.23
}
(3)根据_id搜索文档
使用GET请求,在请求路由中指定_id
GET /my_index/_doc/001
查看返回结果,显示了该文档的_id,_version(版本号1),_source(返回的字段)
{
"_index" : "my_index",
"_id" : "001",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "好再来酒店",
"city" : "青岛",
"price" : 578.23
}
}
(4)根据一般字段搜索文档
进行复杂搜索时需要用到GET请求的_search
路由和query
请求体,本例查找price是578.23 的文档,使用term
搜索
GET /my_index/_search
{
"query": {
"term": {
"price": {
"value": 578.23
}
}
}
}
看一下返回
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "my_index",
"_id" : "001",
"_score" : 1.0,
"_source" : {
"title" : "好再来酒店",
"city" : "青岛",
"price" : 578.23
}
}
]
}
}
返回结果除了文档详情之外还显示了_shards
分片信息,一共一个分片,hits
信息一共一条符合要求的文档
(5)文本字段搜索
文本模糊搜索需要使用query中的match
关键字
GET /my_index/_search
{
"query": {
"match": {
"title": "好再"
}
}
}
查看返回结果
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.5753642,
"hits" : [
{
"_index" : "my_index",
"_id" : "001",
"_score" : 0.5753642,
"_source" : {
"title" : "好再来酒店",
"city" : "青岛",
"price" : 578.23
}
}
]
}
}
返回来对应的文档,耗费了5ms(took关键字)
网友评论