搜索引擎需要有一个数据源,这里就需要一个数据采集和数据同步的过程(来自数据库、爬虫等)。针对这些数据进行处理后存放到相应的搜索引擎节点上,从而为用户提供检索服务。它存在的目的就是优化用户体验,精准地提供有效的搜索服务、对用户搜索行为进行分析。
- Lucene:是 Java 类库,实现集群复杂
- Solr:基于 Lucene
- Elasticsearch:ES 基于 Lucene,提供 Restful 风格的接口进行 *** 作,所以可对接其它开发语言
- 索引
index
:一组集合,可以看作为关系型数据库中的表 - 文档
document
:以 JSON 的形式存在,一个文档就是一条记录,可以看作为关系型数据库中的数据行 - 字段
fields
:文档的属性,可以看作为关系型数据库中的数据列即字段名 - 映射
mapping
:字段的表结构定义(是否为主键,数据类型,是否可为null
等) - 近实时
NRT
:Near real time
指的是新的文档、新的索引新增后,此时用户搜索会有一定的延迟,延迟时间大概一秒左右,可以忽略不计,从而看做接近实时 - 节点
node
:集群中的每一个搜索引擎服务器 shard
和replica
:ES 集群架构原理shard
数据分片:把索引库拆分为多份,分别放在不同的节点上。即这些不同的节点的索引库的数据内容加在一起才是一个完整的索引库,目的为了水平扩展和提高吞吐量replica
备份:由于节点的数据结合起来才是所有数据,所以就要做好节点宕机的准备,这里就需要使用replica
,即每个shard
的备份,所以此时就分为:主分片primary shard
和 备份节点replica shard
- 倒排索引:在实际应用中需要根据属性(字段)的值来查找数据,返回的索引表中的每一项都包含一个属性值和该属性值的各个记录地址(下图蓝色圈)。这种不是根据查询到的数据来确定属性,而是根据属性来确定数据的位置,就称为倒排索引。
到 官网 下载对应的安装包,解压缩后在解压缩目录下创建一个名为 data
的文件夹,此时当前目录下有以下文件目录:
bin
:包含可执行文件config
:配置文件目录JDK
:java 环境lib
:依赖的 jar,类库logs
:日志文件目录modules
:ES 相关的模块plugins
:可以自开发的插件存放的地方data
:存放数据(索引表)的地方
修改存放在 config
目录下核心配置文件 elasticsearch.yml
# 集群名称
cluster.name: seiei-elasticsearch
# 当前 ES 节点的名称
noed.name: es-node0
# 修改存放索引表的目录
path.data: /usr/local/elasticsearch/data
# 修改日志文件目录
path.logs: /usr/local/elasticsearch/logs
# 同 Redis,设置 0.0.0.0 代表可以让远程连接,不受 ip 限制
network.host: 0.0.0.0
# 设置跨域
http.cors.enabled: true
http.cors.allow-origin: "*"
# 集群节点列表
cluster.initial_master_nodes: ["es-node0"]
由于我这边是在虚拟机运行所以还需要修改一下 JVM 参数,打开 config
目录下的 jvm.options
文件,作以下修改:
# 运行内存设置,默认1G,可使用虚拟机就修改为 128M
-Xms128m
-Xmx128m
由于 ES 不允许使用 root
用户 *** 作,所以需要切换用户,将 ES 的用户权限属性进行修改:
chown -R seiei:seiei /usr/local/elasticsearch
这时启动 ES 可能还会发生以下错误:
那么还需要修改一下 Linux 配置
vim /etc/security/limits.conf
添加以下配置
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
打开以下文件
vim /etc/sysctl.conf
添加以下配置:
vm.max_map_count=262145
使用以下命令刷新一下配置
sysctl -p
最后输入一下命令,在后台启动一下 ES
./elasticsearch -d
关闭 ES,可以使用 jps
命令能显示当前所有 java 进程的 pid
,从而杀掉 ES 进程。如果没有 jps
命令,需要 yum install java-1.8.0-openjdk-devel.x86_64
安装。
使用谷歌插件 elasticsearch head
可提供可视化 *** 作 ES。
使用以下 API 可获取集群节点的运行情况:green
表示所有分片都可以正常运行;yellow
表示所有主分片都可以正常运行,但部分备用分片不能正常运行;red
表示部分主分片不能正常运行
GET /_cluster/health
3.1 *** 作索引表
- 查看所有索引表
GET /_cat/indices?v
- 查询某个索引表
GET /indexName
- 构建索引表,其中
PUT /{indexName}
{
"settings": {
"index": {
"number_of_shards": "3", // 主分片个数
"number_of_replicas": "2" // 备用分片个数
}
},
// 设置 Mapping
"mappings": {
"properties": {
"realname": {
"type": "text",
"index": true // 如果设置为 false 的话,该字段就不会被索引
},
"nickname": {
"type": "keyword",
"index": false
}
}
}
}
- 删除索引表
DELETE /{indexName}
3.2 *** 作 Mappings
- 为已存在的索引表创建 Mappings,索引表的某个属性一旦被建立,就不能修改,只可以新增额外的属性,
type
的属性有:text
,keyword
,long
,integer
,float
,double
,byte
,short
,text
,boolean
,date
,object
POST /{indexName}/_mapping
{
"properties": {
{fieldName}: {
"type": {typeName}
}
}
}
text
和 keyword
的区别:如果 Mappings 的 type
设置为 text
,使用分词分析命令时,就可以看到会返回 analyzeTxt
拆分词列表,即会进行分词、倒排索引;如果设置为 keyword
,则会直接返回 analyzeTxt
不作拆分词 *** 作,精准匹配:
POST /{indexName}/_analyze
{
"field": {fieldName}
"text": {analyzeTxt}
}
3.3 *** 作文档
- 添加文档,如果索引表没有手动建立对应 Mappings,那么当插入文档数据的时候,会根据文档类型自动设置属性类型。这个就是 ES 的动态映射,帮我们在索引库中建立对应的数据结构相关配置信息。
POST /{indexName}/_doc/{id}
{
{fieldName1}: {fieldValue1}
{fieldName2}: {fieldValue2}
...
}
要注意的是上面的 id
指的是 ES 中的 id
,如果不指定,ES 会自动生成一个字符串,建议这里的 id
和设置数据的 id
保持一致。
看下图是由 ES 自动映射生成的索引表,name
字段属性下还有有一个 fields
属性,里头声明了 "type": "keyword"
,这是代表对一个字段设置多种索引模式,即使用 text
类型做全文分词检索,也可以使用 keyword
类型做聚合和排序;而 "ignore_above": 256
表示字段索引和存储的长度最大值,超过则被忽略。
- 删除文档,文档删除是逻辑删除,文档还是保存在磁盘上,随着数据越来越多,才会把那些曾经标识过删除的,从磁盘清理出去
DELETE /{indexName}/_doc/{id}
- 修改文档
使用以下接口进行 全量替换,如果修改前有设置过字段 A 的值,而修改时没有指定字段 A 的值,那么最后的结果会导致该数据的字段 A 的值为空。
PUT /{indexName}/_doc/{id}
{
{fieldName1}: {fieldValue1},
{fieldName2}: {fieldValue2},
...
}
使用以下接口进行 局部替换:
POST /{indexName}/_doc/{id}/_update
{
"doc": {
{fieldName1}: {fieldValue1},
{fieldName2}: {fieldValue2},
...
}
}
- 查看某个
id
的文档是否存在,存在返回200
,不存在返回404
,这样的查询会更节省流量
HEAD /{indexName}/_doc/{id}
- 查询文档,根据
id
查询
// 查询全部字段
GET /{indexName}/_doc/{id}
// 查询部分字段
GET /{indexName}/_doc/{id}?_source=fieldName1,fieldName2...
查询全部数据:
// 查询全部字段
GET /{indexName}/_doc/_search
// 查询部分字段
GET /{indexName}/_doc/_search?_source=fieldName1,fieldName2...
其中获取到的数据以下划线开头的字段数据是 元数据
_index
:文档数据所属的索引表_type
:文档数据属于哪个类型,7.x
版本的 ES 一概为_doc
_id
:文档数据的唯一标识,主键_score
:查询相关度,是否契合用户匹配,分数越高用户的搜索体验越高_source
:文档查询到的数据_seq_no
:文档版本号(针对某个主分片内分配的编号)_primary_term
:文档所在位置(针对的是哪个主分片)_version
:版本号,旧版本使用它来作为上乐观锁依据,新版本使用_seq_no
和_primary_term
联合作为上锁依据,通俗的说,可以把_version
看做学校对每个学生都进行了编号,而_seq_no
和_primary_term
则是先对学生做一个班级分类(_primary_term
),再在班级分类里对学生进行编号,这样的效率会更加高效,管理也会更方便
指定 _seq_no
和 _primary_term
,模拟两个客户端同时 *** 作同一个文档数据, _seq_no
和 _primary_term
发生了冲突
POST /{indexName}/_doc/{_id}/_update?if_seq_no={数值}&if_primary_term={数值}
4 分词器
4.1 分词和内置分词器
把文本转换为一个个的单词,分词又称为 analysis
,ES 默认只对英文语句做分词,而不支持中文,每个中都会被拆分为独立的个体。
- 全局分词分析
POST /_analyze
{
"analyzer": {analyzerType},
"text": {analyzeText}
}
- 对于某个索引表的数据属性进行分词分析
POST /{indexName}/_analyze
{
"analyzer": {analyzerType},
"field": {fieldName},
"text": {analyzeText}
}
其中 analyzer
是指分词器,ES 的内置分词器有:
standard
:默认分词,单词会被拆分,大写会转化为小写simple
:按照非字母分词,即don't
会分词为:don
和t
,大写会转为小写whitespace
:根据按照空格分词,忽略大小写stop
:去除无意义的单词,如the
,a
,is
等keyword
:不作分词处理,整个文本作为一个单独的关键词
在 github 上下载对应的 IK 压缩包(版本号要一致,用户权限),然后解压到 ES 安装目录下的 plugins/ik
目录下,然后重启动 ES 即可:
unzip elasticsearch-analysis-ik-7.4.2.zip -d /usr/local/elasticsearch/plugins/ik
重启后就可以使用以下两个分词器来分析中文:
ik_max_word
:会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合ik_smart
:会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”
自定义词典:
- 修改配置文件
vim /usr/local/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml
- 添加自定义词典
DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置comment>
<entry key="ext_dict">seieiCustom.dicentry>
<entry key="ext_stopwords">entry>
properties>
- 在同级目录下创建自定义词典文件
seieiCustom.dic
如:
骚年
蚌埠住
- 重启 ES
注:
在设置 Mappings 时,可以在字段设置 analyzer
为 ik_max_word
,用户对文档进行分词检索时,首先用户的搜索词汇进行一次分词 *** 作,而文档对应的字段如果设置了分词器属性,它自身就会做一个分词 *** 作(初始化的文档的时候),然后对二者进行一个比较。
如下设置分词器属性:
POST /{indexName}/_mapping
{
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
在更新、添加了自定义词典后,先前创建了的文档(设置了对应分词器)不会自动更新对应的词典,此时需要手动更新:
POST /{indexName}/_update_by_query?conflicts=proceed
// 多个索引表更新
POST /{indexName1},{indexName2}../_update_by_query?conflicts=proceed
// 更新 index 为前缀的索引表
POST /index*/_update_by_query?conflicts=proceed
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)