ElasticSearch
Es概念介绍
ElasticSearch:简称为ES,基于Lucene全文检索引擎服务,支持分布式集群(数据横向扩展、分布式计算)
应用场景:全文检索或者搜索服务;NOSQL数据库(ES中的数据单元为JSON);ELK数据分析平台
NRT (near real time) 接近实时 ES中的一条数据写入后大概会有1s的延迟才能被检索到
9300端口:Es节点之间通讯使用 9200:Es节点和外部通讯使用
名词介绍
名词 | 说明 | 额外补充 |
---|---|---|
索引(index) | 类似于数据库中的database | |
类型(type) | 相当于sql中的table | Es6.0以后删除了类型的概念,6.0还可以设置类型,但只能设置一个 |
文档(Document) | 相当于sql中的一行记录 | |
分片(Shard) | 每个索引都有1到多个分片, 每个分片都是一个luncene索引 | 片的好处: 分摊索引的搜索压力, 分片还支持水平的拓展和拆分以及分布式的操作, 可以提高搜索和其他处理的效率 |
备份/复制(replicas) | 拷贝一个分片就完成了分片的备份 | 备份的好处: 当主分片失败或者挂掉, 备份就可以代替分片进行操作, 进而提高了es的可用性, 备份的分片还可以进行搜索操作, 以分摊搜索的压力. |
映射(Mapping) | 类似于Table的Schema(模式) | 例如:create table person_info(name varchar(20),age tinyint)创建一张表,person后的括号是定义表中的字段,即为Schema |
analyzer(分析器)介绍
analyzer(分析器)是一个包,这个包由三部分组成,分别是:character filters (字符过滤器)、tokenizer(分词器)、token filters(token过滤器)。
一个analyzer可以有0个或多个character filters
一个analyzer有且只能有一个tokenizer
一个analyzer可以有0个或多个token filters
character filter 是做字符转换的,它接收的是文本字符流,输出也是字符流
tokenizer 是做分词的,它接收字符流,输出token流(文本拆分后变成一个一个单词,这些单词叫token)
token filter 是做token过滤的,它接收token流,输出也是token流
由此可见,整个analyzer要做的事情就是将文本拆分成单个单词,文本 ----> 字符 ----> token
分析器的任务是分析(Analyze)文本数据,分析是分词,规范化文本的意思
ES在创建索引时, 默认创建5个分片(shard), 每个分片有一份备份/复制分片(replica shard), 可以修改, 分片的数量只能在创建索引的时候指定, 索引创建后就不能修改分片的数量了, 而备份是可以动态修改的
反向索引又叫倒排索引,是根据文章内容中的关键字建立索引
Keyword 类型是不会分词的,直接根据字符串内容建立反向索引,Text 类型在存入 Elasticsearch 的时候,会先分词(指定分词器会按指定分词器分词,未指定按默认分词器分词)),然后根据分词后的内容建立反向索引
java REST api是通过http访问,走9200端口(java api是9300端口)。
虽然es带有java api,但是会引起版本兼容性的问题,以及微弱到可以忽略的性能提升,并且java api在未来的es版本会放弃,官方推荐使用java REST api
每个elasticsaerch分片都是一个Lucene 索引。在单个索引中你最多可以存储2147483519 (= Integer.MAX_VALUE - 128) 个文档。你可以使用 _cat/shards api去监控分片的的大小。
Es配置文件
vim elasticsearch.yml
#集群名字,es启动后会将具有相同集群名字的节点放到一个集群下。
cluster.name: elasticsearch
#节点名字
node.name: "es-node1"
#指定集群中的节点中有几个有master资格的节点。
#对于大集群可以写3个以上。
discovery.zen.minimum_master_nodes: 2
#设置集群中自动发现其它节点时ping连接超时时间,默认是3s,
#为避免因为网络差而导致启动报错,设成了40s。
discovery.zen.ping.timeout: 40s
#设置是否打开多播发现节点,默认是true
discovery.zen.ping.multicast.enabled: false
#ip地址
network.host: 192.168.137.100
#指明集群中其它可能为master的节点ip,以防es启动后发现不了集群中的其他节点。
discovery.zen.ping.unicast.hosts:["节点1的ip","节点2的ip","节点3的ip"]
一般测试时,只需要改一下cluster.name、node.name、network.host即可,使用默认也可以
Es启动:
启动:# bin/elasticsearch
发生以下错误:Caused by: java.lang.RuntimeException: can not run elasticsearch as root(不能用root用户启动) 原因:root用户权限过大
解决方案: useradd es (添加一个es用户)
passwd es (设置用户es密码)
chown -R es:es * (给es用户权限)
su -es (切换用户)
切换到上面新添加的es用户再次执行启动命令:此时可能会出现3个错误
ERROR: [3] bootstrap checks failed
[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at
least [65536]
解决(切换到root用户):
vim /etc/security/limits.conf
# 添加以下内容(*也是,代表所有用户):增大内存和硬盘
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
[2]:max number of threads [3802] for user [es] is too low, increase to at least [4096]
解决:
vim /etc/security/limits.d/90-nproc.conf(用户最大线程数)
修改以下内容
* soft nproc 4096(原先是1024)
[3]:max virtual memory areas vm.max_map_count [65530] is too low, increase to at least
[262144]
解决:
vim /etc/sysctl.conf(配置最大线程数)
# 添加以下内容
vm.max_map_count=655360
再次切换到es用户执行启动命令成功启动
Kibana工具
当es启动后,我们在命令行直接敲es命令是比较麻烦的,因此此时用到kibana插件,kibana是es的一个可视化视图工具,当然es也有其他的插件,比如elasticsearch-head,ElasticHD等等
kibana的安装
kibana下载地址:https://www.elastic.co/cn/downloads/kibana,注意:下载的版本要和es使用的版本要对应
kibana的配置文件
vim kibana.yml
#对外服务监听端口
server.port: 5601
#绑定可以访问5601端口服务的IP地址,0.0.0.0表示任何地址在没有防火墙限制的情况下都可以访问,生产环境别这样设置,不安全。
server.host: "0.0.0.0"
#默认值为主机名称,表示kibana实例绑定的主机,可以是IP地址或者主机名称.
server.name: "192.168.1.11"
#用来处理ES请求的服务URL
elasticsearch.hosts: ["http://192.168.1.11:9200","http://192.168.1.12:9200"]
#用来控制证书的认证,可选的值为full,none,certificate。此处由于没有证书,所以设置为null,否则启动会提示错误.
elasticsearch.ssl.verificationMode: none
#kibana搜索数据请求超时时间
elasticsearch.requestTimeout: 90000
更多配置信息查看:https://www.elastic.co/guide/en/kibana/5.6/settings.html
启动命令:kibana文件夹bin/kibana
Es集群相关操作
查看集群健康信息 GET /_cat/health?
查看集群中节点信息 GET /_cat/nodes?v
查看集群中的索引信息 GET /_cat/indices?v
可以看到我们集群叫“elasticsearch”,运行状态是green。每当我们查询集群健康情况时,接口可能会返回green,yellow或red状态。green意味着一切良好(集群所有的功能都正常)。
yellow意味着所有的数据都是可用的,但是一些复制分片可能没有正确分发(集群的所有功能还是正常的)。red意味着因为某些原因导致有些数据不能使用。
注意,即使集群状态是red,它仍然可以运行一部分的功能。(例如,它依然可以从一些可用的分片处理搜索请求)但你应该尽快去修复它,因为这样会使搜索结果丢失一些数据
索引相关操作
简单操作: 创建: put/索引名 删除:delete/索引名
put、get、post、delete操作
添加数据
put 索引名/类型名/文档(id) {json数据}
不指定类型(id)时,会随机生成id值,但只能用post post 索引名/类型名 {json数据}
获取数据 get 索引名/类型名/文档(id)
测试文档是否存在
HEAD 索引名/类型名/文档(id)
200 - OK 200:该文档存在 404 - Not Found 404:该文档不存在
批量获取 主要是Get (索引名/类型名)_mget {json数据} 具体分类看保存的图片
批处理操作
POST /索引名/类型/_bulk # 批量插入多个document
{"index":{}}
{"name":"ww","title":"王五","age":18,"created":"2018-12-27"}
{"index":{}}
{"name":"zl","title":"赵六","age":25,"created":"2018-12-27"}
POST /索引名/类型名/_bulk # 批量操作(包含修改和删除)
{"update":{"_id":"KrOP6WcBVEuCC3JS8V9K"}} # 修改
{"doc":{"title":"王小五"}}
{"delete":{"_id":"K7OP6WcBVEuCC3JS8V9K"}} # 删除
es更新文档的原理为:先找到这个文档,删除旧的文档内容执行更新,更新完后再索引最新的文档
过滤器 注意: 过滤查询运行时先执行过滤语句,后执行普通查询
过滤器的类型
1. term 、 terms Filter
term、terms的含义与查询时一致。term用于精确匹配、terms用于多词条匹配
2. ranage filter 3. exists filter 4. ids filter
Query和Filter更详细的对比可参考:https://blog.csdn.net/laoyang360/article/details/80468757
Mapping Type:
1. 简单类型: text , keyword , date , long , double , boolean or ip
2. 其它类型: object , geo_point , geo_shape 等
查看类型mapping GET /索引名/_mapping/类型名 GET 索引名/_mapping?pretty
查看某个字段的分词结果
GET your_index/your_type/your_id/_termvectors?fields=your_fieldsName
IK分词器
elasticSearch默认的分词器对中文不是很友好,会将中文单个字的建立索引(标准分词器对于英文单词分词
,对于中文单字分词
),因此可以使用ik分词器
ik分词器下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
同样,版本尽量和Es相对应。
下载完以后在Es的plugins文件夹中创建一个名为IK的文件夹,将下载的ik分词器解压到该文件夹下,
重新启动Es,出现plugin [analysis-ik]说明ik分词器被加载。
Ik分词器的两种方式:
智能模式和细粒度模式(智能:对应es的IK插件的ik_smart,细粒度:对应es的IK插件的ik_max_word)
细粒度分词,包含每一种切分可能(更全);而智能模式,只包含各种切分路径中最可能的一种。
Es中高级检索
6.0以后删除类型了,下面的查询条件可以把索引类型去掉,因为这是6.0之前的语句,可以根据版本决定是否去掉
1.查询所有(match_all)
GET /索引名/索引类型/_search
{
"query": { "match_all": {} }
}
2.查询结果中返回指定条数(size)
size 关键字: 指定查询结果中返回指定条数。 默认返回值10条
GET /索引名/索引类型/_search
{
"query": { "match_all": {} },
"size": 1
}
3. 分页查询(from、scroll)
浅分页:from+size
from/size的原理:因为es是基于分片的,假设有5个分片,from=100,size=10。则会根据排序规则从5个分片中各取回100条数据数据,然后汇总成500条数据后选择最后面的10条数据。
GET /索引名/索引类型/_search
{
"query": {"match_all": {}},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"size": 2,
"from": 1
}
优点:from+size在数据量不大的情况下,效率比较高
缺点:在数据量非常大的情况下,from+size分页会把全部记录加载到内存中,这样做不但运行速递特别慢,而且容易让es出现内存不足而挂掉
为了解决上面的问题,elasticsearch提出了一个scroll滚动的方式。
scroll 类似于sql中的cursor(游标),使用scroll,每次只能获取一页的内容,然后会返回一个scroll_id。根据返回的这个scroll_id可以不断地获取下一页的内容,所以scroll并不适用于有跳页的情景。
深分页:scroll
scroll分为初始化和遍历两步
初始化:
GET /索引名/索引类型/_search?scroll=3m
{
"query": {"match_all": {}},
"size": 3,
"from": 0
}
- scroll=3m代表当前查询的数据缓存3分钟
- size:3 代表当前查询3条数据
- from:起始位置,使用scroll必须要将from设置为0,可以不写使用默认值。
遍历: 初始化时会返回一个scoll_id,用于下面的遍历查询
GET _search/scroll
{
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAJZ9Fnk1d......",
"scroll": "5m"
}
注意:请求的接口不再使用索引名了,而是 _search/scroll,其中GET和POST方法都可以使用。
scroll删除
根据官方文档的说法,scroll的搜索上下文会在scroll的保留时间截止后自动清除,但是我们知道scroll是非常消耗资源的,所以一个建议就是当不需要了scroll数据的时候,尽可能快的把scroll_id显式删除掉。
清除指定的scroll_id:
DELETE _search/scroll/DnF1ZXJ5VGhlbkZldGNo.....
清除所有的scroll:
DELETE _search/scroll/_all
深分页:search_after
scroll 的方式,官方的建议不用于实时的请求(一般用于数据导出),因为每一个 scroll_id 不仅会占用大量的资源,而且会生成历史快照,对于数据的变更不会反映到快照上。
search_after 分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。但是需要注意,因为每一页的数据依赖于上一页最后一条数据,所以无法跳页请求。
为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。
GET 索引名/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"age": 28
}
}
]
}
},
"size": 20,
"from": 0,
"sort": [
{
"timestamp": {
"order": "desc"
},
"_id": {
"order": "desc"
}
}
]
}
- 使用search_after必须要设置from=0。
- 这里我使用timestamp和_id作为唯一值排序。
- 我们在返回的最后一条数据里拿到sort属性的值传入到search_after。
使用sort返回的值搜索下一页:
GET 索引名/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"age": 28
}
}
]
}
},
"size": 10,
"from": 0,
"search_after": [
1541495312521,
"d0xH6GYBBtbwbQSP0j1A"
],
"sort": [
{
"timestamp": {
"order": "desc"
},
"_id": {
"order": "desc"
}
}
]
}
4.关键词查询(term)
term 关键字: 用来使用关键词查询
GET /索引名/索引类型/_search
{
"query": {
"term": {
"address": {
"value": "北京"
}
}
}
}
在ES使用标准分词器(StandardAnalyzer)时,Mapping Type 中 keyword , date ,integer, long , double , boolean or ip 这些类型不分词,只有text类型分词。
5.范围查询(range)
range 关键字: 用来指定查询指定范围内的文档
gt:> ;lt: <;gte: >=;lte: <=
GET /索引名/索引类型/_search
{
"query": {
"range": {
"age": {
"gte": 8,
"lte": 30
}
}
}
}
6. 前缀查询(prefix)
prefix 关键字: 用来检索含有指定前缀的关键词的相关文档
GET /索引名/索引类型/_search
{
"query": {
"prefix": {
"content": {
"value": "redis"
}
}
}
}
7.通配符查询(wildcard)
wildcard 关键字: 通配符查询 ? 用来匹配一个任意字符 * 用来匹配多个任意字符
GET /索引名/索引类型/_search
{
"query": {
"wildcard": {
"content": {
"value": "re*"
}
}
}
}
8.多id查询(ids)
ids 关键字 : 值为数组类型,用来根据一组id获取多个对应的文档
GET /索引名/索引类型/_search
{
"query": {
"ids": {
"values": ["lg5HwWkBxH7z6xax7W3_","lQ5HwWkBxH7z6xax7W3_"]
}
}
}
9. 模糊查询(fuzzy)
fuzzy 关键字: 用来模糊查询含有指定关键字的文档
GET /索引名/索引类型/_search
{
"query": {
"fuzzy": {
"content":"spring"
}
}
}
10. 布尔查询(bool)
bool 关键字: 用来组合多个条件实现复杂查询
must: 相当于&& 同时成立
should: 相当于|| 成立一个就行
must_not: 相当于! 不能满足任何一个
GET /索引名/索引类型/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"age": {
"gte": 0,
"lte": 30
}
}
}
],
"must_not": [
{"wildcard": {
"content": {
"value": "redi?"
}
}}
]
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
11. 高亮查询(highlight)
highlight 关键字: 可以让符合条件的文档中的关键词高亮
GET /索引名/索引类型/_search
{
"query": {
"term": {
"content": {
"value": "redis"
}
}
},
"highlight": {
"fields": {
"*": {}
}
}
}
自定义高亮html标签: 可以在highlight中使用pre_tags
和post_tags
GET /索引名/索引类型/_search
{
"query":{
"term":{
"content":"框架"
}
},
"highlight": {
"pre_tags": ["<span style='color:red'>"],
"post_tags": ["</span>"],
"fields": {
"*":{}
}
}
}
多字段高亮: 使用require_field_match
开启多个字段高亮
GET /索引名/索引类型/_search
{
"query":{
"term":{
"content":"框架"
}
},
"highlight": {
"pre_tags": ["<span style='color:red'>"],
"post_tags": ["</span>"],
"require_field_match":false,
"fields": {
"*":{}
}
}
}
12. 多字段查询(multi_match)
GET /索引名/索引类型/_search
{
"query": {
"multi_match": {
"query": "中国",
"fields": ["name","content"] #这里写要检索的指定字段
}
}
}
13. 多字段分词查询(query_String)
GET /索引名/索引类型/_search
{
"query": {
"query_string": {
"query": "中国声音",
"analyzer": "ik_max_word",
"fields": ["name","content"]
}
}
}
14. 过滤查询 (filter query)
其实准确来说,ES中的查询操作分为2种:
查询(query)
和过滤(filter)
。
查询即是之前提到的query查询,它 (查询)默认会计算每个返回文档的得分,然后根据得分排序。而过滤(filter)只会筛选出符合的文档,并不计算得分,且它可以缓存文档 。所以,单从性能考虑,过滤比查询更快。
换句话说,过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时, 应先使用过滤操作过滤数据, 然后使用查询匹配数据。
过滤语法
GET /索引名/索引类型/_search
{
"query": {
"bool": {
"must": [
{"match_all": {}}
],
"filter": {
"range": {
"age": {
"gte": 10
}
}
}
}
}
}
- 在执行filter和query时,先执行filter在执行query
- Elasticsearch会自动缓存经常使用的过滤器,以加快性能。
常见的过滤器类型
term 、 terms Filter
GET /索引名/索引类型/_search # 使用term过滤
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "小黑"
}
}}
],
"filter": {
"term": {
"content":"框架"
}
}
}
}
}
GET /索引名/索引类型/_search #使用terms过滤
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "中国"
}
}}
],
"filter": {
"terms": {
"content":[
"科技",
"声音"
]
}
}
}
}
}
ranage filter
GET /索引名/索引类型/_search
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "中国"
}
}}
],
"filter": {
"range": {
"age": {
"gte": 7,
"lte": 20
}
}
}
}
}
}
exists filter:过滤存在指定字段,获取字段不为空的索引记录使用
GET /索引名/索引类型/_search
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "中国"
}
}}
],
"filter": {
"exists": {
"field":"aaa"
}
}
}
}
}
ids filter:过滤含有指定字段的索引记录
GET /索引名/索引类型/_search
{
"query": {
"bool": {
"must": [
{"term": {
"name": {
"value": "中国"
}
}}
],
"filter": {
"ids": {
"values": ["1","2","3"]
}
}
}
}
}
向es的mapping中添加新字段
PUT /索引名/_mapping/
{
"properties": {
"新增字段": {
"type": "text",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above" : 256
}
}
}
}
}
--给新字段赋值(原先的数据)
POST 索引名/_update_by_query
{
"script": {
"lang": "painless",
"inline": "if (ctx._source.新增字段== null) {ctx._source.新增字段 = '值'}"
}
}