Elasticsearch简介
一个采用RestFulAPI标准的高扩展性和高可用性的实时数据分析的全文搜索工具
- Node(节点):单个装有Elasticsearch服务并且提供故障装和扩展的服务器。集群的每个节点都是对等的关系,这样做的优点就是去中心化,而Master节点只不过是多了一个维护集群状态的功能。每个节点上面的状态和数据都是实时同步的,如果master节点出了故障,其一台node会马上顶上去的,然后成为一个新的master
- Cluster(集群):一个集群就是由一个或者多个node组织在一起,共同工作,共同分享整个数据具有负载均衡功能的集群;
- Document(文档):一个文档是一个可以被索引的基础信息单元;
- Index(索引):索引就是一个拥有几分相似特征的文档的集合,索引名称是英文单词的小写
- Type(类型):一个索引中,你可以定义一种或者多种类型
- Field(列):Field是es的最小单元,相当于数据的某一列
- Shards(分片):es将索引分成若干份,每个部分就是一个shard,一个索引的大小可能会超出单个节点这个硬盘限制的大小(也就是说这个节点硬盘没有那么大的容量),在创建索引的时候就可以指定,默认是5份,但是索引创建完之后,这个值就没法更改了
- Replicas(复制):Replicas是索引一份或多份的拷贝
es相当于一个数据库
关系型数据库 | 非关系型数据库 |
---|---|
数据库 | 索引 |
表 | 类型 |
数据行 | 文档 |
数据列 | Field(列) |
Solr和Elasticsearch
solr在实时搜索方面的效率,没有Es强,但是在支持文本格式方面,要比es强,例如:html,pdf,word,excel,cvs等
Restful
XML慢慢被人丢弃的原因:
- XML文件格式比较庞大,也比较复杂,传输占用带宽;
- 服务端和客户端都需要花费大量的代码去解析
- 不同浏览器解析XML的方式不太一致,需要重复并写好多代码,代码不容易维护
Json格式是被压缩的,占用带宽比较小,易于解析,支持很多语言,直接能为服务端代码使用
Http协议是一种无状态协议,无状态就是指服务端不会去记客户端的所有信息和操作,数据的状态只保存在服务端
CURL以命令的方式执行http协议请求的协议
- 访问一个网页
curl www.baidu.com - 显示http response的头信息
curl -i www.baidu.com - 显示一次http请求的通信过程
curl --trace output.txt www.baidu.com - curl执行GET/POST/PUT/DELETE 操作
curl -X GET/POST/PUT/DELETE www.example.com
MAC下docker安装elasticsearch和kibana
安装elasticsearch
docker pull docker.elastic.co/elasticsearch/elasticsearch:6.3.2
docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2
安装kibana
docker pull docker.elastic.co/kibana/kibana:6.3.2
docker run --name kb -p5601:5601 -d docker.elastic.co/kibana/kibana:6.3.2
安装插件
docker pull mobz/elasticsearch-head:5
docker run -d --name es-head -p 9100:9100 docker.io/mobz/elasticsearch-head:5
MAC下安装elasticsearch和kibana
安装elasticsearch
brew install elasticsearch
安装完即可显示一些信息,比如一些文件的位置
或者也可以运行命令:
brew info elasticsearch
查看安装信息:
StevendeMacBook-Pro:folders steven$ brew info elasticsearch
elasticsearch: stable 6.8.0, HEAD
Distributed search & analytics engine
https://www.elastic.co/products/elasticsearch
/usr/local/Cellar/elasticsearch/6.8.0 (133 files, 103.1MB) *
Built from source on 2019-06-03 at 20:01:37
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/elasticsearch.rb
==> Requirements
Required: java = 1.8 ✔
==> Options
--HEAD
Install HEAD version
==> Caveats
Data: /usr/local/var/lib/elasticsearch/
Logs: /usr/local/var/log/elasticsearch/elasticsearch_steven.log
Plugins: /usr/local/var/elasticsearch/plugins/
Config: /usr/local/etc/elasticsearch/
To have launchd start elasticsearch now and restart at login:
brew services start elasticsearch
Or, if you don't want/need a background service you can just run:
elasticsearch
==> Analytics
install: 12,164 (30 days), 33,037 (90 days), 119,903 (365 days)
install_on_request: 11,411 (30 days), 30,956 (90 days), 110,635 (365 days)
build_error: 0 (30 days)
安装kibana
brew install kibana
elasticsearch索引
倒排索引
也被成为反向索引,是一种索引方法,被用来存储在全文检索下某个单词在一个文档或者一组文档中的存储位置的映射。他是文档检索系统中最常用的数据结构
常规的索引建立方式:文档-->关键词的映射过程(正向索引) ==缺点,费时,需要把这个文档全部遍历一遍==
倒排反向建立索引:把正向索引的结果重新构造成倒排索引(反向索引)
elasticsearch API CURD 操作
Marvel也是elasticsearch的一个管理监控工具,集head和bigdesk优点于一身。但是是付费的
索引初始化操作
curl -XPUT "http://localhost:9200/http://127.0.0.1:9200/libray/" -H 'Content-Type: application/json' -d'
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}'
当然在类似于kibana这样的命令行中可以直接输入:
PUT libray/
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
后面同理.
"blocks.read_only": false
设置为true,则当前索引只允许读,不允许写或者更新
"blocks.read": true, //禁止读操作
"blocks.write": true //禁止写操作
"blocks.metadata": true //禁止对metadata操作
可以通过GET带上参数_settings可以获得该索引详细的配置信息
GET /libray/_settings
同时获取两个索引的信息
GET /libray1,libray1/_settings
获取所有的索引的信息
GET /_all/_settings
创建一个索引
# ----索引名称
# |
# | Type 名称
# | |
# | | 文档ID
# | | |
# v v |
PUT /libray/book/1
{
"title": "",
"name": {
"first": "",
"last": ""
},
"price": "96.35"
}
#ID可以不设置,可以自动生成
PUT /libray/book/
{
"title": "",
"name": {
"first": "",
"last": ""
},
"price": "96.35"
}
#通过ID获得文档信息
GET /libray/book/1
#通过_source获取指定的字段
GET /libray/book/1?_source=title,price
修改一个索引
#通过_update API的方式单独更新你想要更新的字段
POST /libray/book/1/_update
{
"doc": {
"price":"10.25"
}
}
删除一个索引
#删除一个文档的方法
DELETE /libray/book/1
Elasticsearch的内置字段以及类型
描述 | 字段 |
---|---|
内置字段 | _uid,_id,_type,_source,_all,_analyzer,_boost,_parent_rooting,_index,_size,_timestamp,_ttl |
字段类型 | String,Integer/long,Float/Double,Boolean,Null,Date |
Mget获取多个文档
同时检索多个文档
mget API参数是一个doc数组,数组的每个节点定义一个文档的_index、_type、_id元数据
GET /_mget
{
"docs": [
{
"_index": "",
"_type": "",
"id": ""
},
{
"_index": "",
"_type": "",
"id": ""
}
]
}
#获取相同index相同type下不同ID的文档
GET /libray/book/_mget
{
"ids":["",""]
}
批量操作bulk
实现多个文档的create、index、update、delete
例如:
{"delete":{"_index":"library","_type":"books","_id":"1"}}
命令
POST /libray/books/_bulk
{"index":{"_id":1}}
{"title":"hello elasticsearch","price":"45"}
{"index":{"_id":"2"}}
{"title":"hello elasticsearch2","price":"45"}
bulk处理文档大小的最佳值
- 数据家在在每个节点的RAM里
- 请求的数据超过一定大小,那bulk的处理性能就会下降
- 文档数据大小跟硬件配置,文档复杂度,以及当前集群的负载有关
Elasticsearch版本控制
为什么要进行版本控制
为了保证数据在多线程操作下的准确性
悲观锁和乐观锁
- 悲观锁:假定会发生并冲突,屏蔽一切可能违反数据完整性的操作 并发量小的时候使用
- 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据的完整性 不会让线程给数据加锁,假定多个线程修改一个数据的情况比较少,如果多个线程使用了乐观锁,多个线程修改一条数据的时候,那么后面的线程会修改失败,只有再次获取最新的状态,再做修改,才能修改成功,因为使用乐观锁,会增加大量线程读取数据对象的次数,即查询次数较多,并且有查询数据不实时的可能,即看到的数据是OK的,但是去校验是失败的。
内部版本控制和外部版本控制
elasticseach使用的是乐观锁
- 内部版本控制:_version自增长,修改数据后,_version会自动+1
- 外部版本控制:为了保持_version和外部版本控制的数值一致,使用version_type=external检查数据当前的version值是否小于请求中的version
例如:
PUT /libray/book/1
{
"title": "Java编程思想",
"name": {
"first": "Bruce",
"last": "Eckel"
},
"price": "96.35"
}
新增一条记录,可以看到返回结果中的version是1
更新这条记录,再次查询发现这个版本变成了2
我们再做一次更新:
POST /libray/book/1/_update?version=3
{
"doc": {
"price":"423.3"
}
}
返回:
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[book][1]: version conflict, current version [2] is different than the one provided [3]",
"index_uuid": "bq6RJKjJTgKi-4D4IMCBVw",
"shard": "3",
"index": "libray"
}
],
"type": "version_conflict_engine_exception",
"reason": "[book][1]: version conflict, current version [2] is different than the one provided [3]",
"index_uuid": "bq6RJKjJTgKi-4D4IMCBVw",
"shard": "3",
"index": "libray"
},
"status": 409
}
以上就是内部版本控制
外部版本控制示例:
PUT /libray/book/1?version=5&version_type=external
{
"title": "Java编程思想",
"name": {
"first": "Bruce",
"last": "Eckel"
},
"price": "200.35"
}
# 我们将version版本自己定义成了5,这个时候,再去更新version=5的时候就会提示版本冲突
# 会检查当前数据中的version值是否小于请求中的version值(例如通过时间戳控制(整数))
Elasticsearch Mapping映射
所谓的映射,就是创建索引的时候,可以预先定义字段的类型和相关属性(不设置的话会自动设置),同时还可以定义分析器等
作用:这样会让索引建立的更加细致和完善
分类:静态映射和动态映射
数据类型对照:
type | ES Type |
---|---|
String,VarChar,Text | string |
Integer | integer |
Long | long |
Float | float |
Double | double |
Boolean | boolean |
Date/Datetime | date |
Bytes/Binary | binary |
映射的属性方法
出了定义字段的类型,还可以给字段添加相关的属性
属性 | 描述 | 适用类型 |
---|---|---|
store | 值为yes就是存储,no就是不存储 默认是no | all |
index | 值为:analyzed(索引且分析)(默认),not_analyzed(索引但不分析) 或者no(不索引这个字段,这样就搜不到) | String 其他类型只能设置为no或者not_analyzed |
null_value | 如果字段是空值。通过它可以设置一个默认值,比如:"null_value":"NA" | all |
boost | 设置字段的权值 默认值是1.0(即重要程度) | all |
index_analyzed | 设置一个索引时引用的分析器 | all |
search_analyzed | 设置一个搜索时用的分析器 | all |
analyzed | 可以设置索引和搜索时用的分析器,默认下es使用的是standard分析器,除此之外,还可以使用whitespace、simple、english这三种内置分析器 | all |
include_in_all | 默认下es会为每个文档定义一个特殊的域_all,它的作用就是每个字段都会被搜索到,如果你不想让它被搜索到,那么就在这个字段里定义一个include_in_all=false;默认值是true | all |
index_name | 定义字段的名称;默认是字段本身的名字 | all |
norms(比较费资源) | 作用是根据各种规范化因素去计算权值,这样方便查询;如果index=analyzed,默认值是true,not_analyzed是false | all |
动态映射
文档中碰到一个以前没见过的字段时,动态映射可以自动决定该字段的类型,并且对该字段添加映射
如何配置动态映射
通过dynamic属性进行控制
true:默认值,动态添加字段;false:忽略新字段 ;strict:碰到陌生字段,抛出异常
适用范围:适用在根对象上或者object类型的任意字段上
建立映射
#建立映射
POST /libray
{
"setting":{
"number_of_shards":5,
"number_of_replicas":1
},
"mappings":{
"books":{
"properties":{
"title":{"type":"string","boost":"2"},
"name":{"type":"string","index":"not_analyzed"},
"publish_date":{"type":"date","index":"not_analyzed"},
"price":{"type":"double"},
"number":{"type":"integer"}
}
}
}
}
#动态映射
PUT /libray
{
"mappings": {
"books": {
"dynamic": "strict",
"properties": {
"title": {
"type": "string"
},
"name": {
"type": "string",
"index": "not_analyzed"
},
"publish_date": {
"type": "date",
"index": "not_analyzed"
},
"price": {
"type": "double"
},
"number": {
"type": "object",
"dynamic": true
}
}
}
}
}
管理映射
获取某个索引的映射信息
GET /library/_mapping
获取某个索引下某个type的映射信息
GET /library/_mapping/book
获取这个集群内所有的映射信息
GET /_all/_mapping
获取这个集群内某两个或多个type的映射信息
GET /_all/_mapping/books,bank_account
更新修改MApping映射
很遗憾,mapping一旦建立,就不能修改现有的字段映射,如果要推到现有的映射,就需要重新建立一个索引,然后重新定义映射嘛,然后将之前索引中的数据导入到新建的索引中
具体办法:
- 给现有的索引定义一个别名,并且吧现有的索引指向这个别名
命令:PUT /现有索引/_alias/别名A
- 新创建一个索引,定义好最新的映射
- 将别名指向新的索引,并且删除之前索引的指向,
POST /_aliases
{
"actions": [
{
"add": {
"index": "新建索引名",
"alias": "别名A"
},
"remove": {
"index": "现有的索引名",
"alias": "别名A"
}
}
]
}
通过以上步骤,就可以实现索引的平滑过度,并且是零停机的
删除映射
DELETE /libray/book
DELETE /libray/book/_mapping
DELETE /libray/_mapping/book,bank
Elasticsearch 的查询操作
- 基本查询:利用ES内置查询条件进行查询
- 组合查询:把多个基本查询组合到一起的复合性查询
- 过滤:查询同时,通过filter条件在不影响打分的情况下筛选出想要的数据
基本查询
- term,terms查询
- from,size
- 返回版本号_version
- match查询
- 升序降序
- prefix前缀匹配查询
- range范围查询
- wildcard通配符查询
- fuzzy模糊查询
- more_like_this&more_like_field查询
#指定index名以及yupe名的搜索
GET /user/students/_search?q=age:18
#指定index但是没有type的搜索
GET /user/_search?q=age:18
#既没有index,也没有type
GET /_search?q=age:18
minimum_match:最小匹配集;1:说明关键词里最少有一个,2:说明文档中两个关键词都得存在
GET /user/students/_search
{
"query": {
"bool": {
"minimum_should_match": 2,
"should": [
{
"term": {
"name": "张"
}
},
{
"terms": {
"name": ["貂","四"]
}
}
]
}
},
"highlight": {
"fields": {
"title": {}
}
}
}
GET /user/students/_search
{
"query": {
"terms": {
"name": ["张","貂"]
}
}
}
#控制查询返回的数量
#from和size 相当于mysql里面的limit
#from:从哪个接口开始返回
#size:定义返回最大的结果数
GET /user/students/_search
{
"from": 0,
"size": 20,
"query": {
"term": {
"name": {
"value": "四"
}
}
}
}
#返回版本号_version
GET /user/students/_search
{
"version": true,
"query": {
"term": {
"name": {
"value": "张"
}
}
}
}
#match查询
#match查询可接受文字,数字日期等数据类型
#match跟term的区别是,match查询的时候,es会根据给定的字段提供核实的分析器,而term查询不会有分析器分析的过程
GET /user/students/_search
{
"query": {
"match": {
"name": "张"
}
}
}
#match_all查询
#查询指定索引下所有的文档
GET /user/students/_search
{
"query": {
"match_all": {}
}
}
#通过match_phrase查询
#短语查询,slop定义的是关键词之间隔多少位置单词
GET /user/students/_search
{
"query": {
"match_phrase": {
"name": {
"query": "王,四",
"slop": 1
}
}
}
}
#multi_match查询
#可以指定多个字段
#比如查询多个字段中都包含某个关键词的文档
GET /user/students/_search
{
"query": {
"multi_match": {
"query": "张",
"fields": ["name","sex"]
}
}
}
以下两个因为版本问题已经不用了,自行招替代方案
#指定返回的字段
#注意只能返回store为yes的字段
GET /user/students/_search
{
"fields":["age"],
"query": {
"match": {
"name": "张三"
}
}
}
#通过partial_fields控制加载的字段
GET /user/students/_search
{
"partial_fields": {
"partical": {
"include": ["name"],
"exclude": ["age","sex"]
}
},
"query": {"match_all": {}}
}
#还可以加通配符
GET /user/students/_search
{
"query": {
"multi_match": {
"query": "张",
"fields": ["na*","sex"]
}
}
}
排序
通过sort将结果排序 desc:降序 ;asc:升序;
GET /user/students/_search
{
"query": {
"match_all": {}
},"sort": [
{
"age": {
"order": "desc"
}
}
]
}
控制范围
#控制范围
#range查询,范围查询
#有from,to,include_lower,include_upper,boost这些参数
#include_lower:是否包含范围的左边界,默认是true
#include_upper:是否包含范围的有右边届,默认是true
GET /user/students/_search
{
"query": {
"range": {
"age": {
"from": 10,
"to": 30
}
}
}
}
GET /user/students/_search
{
"query": {
"range": {
"age": {
"from": 10,
"to": 30,
"include_lower":true,
"include_upper":true
}
}
}
}
wildcard查询 允许你使用通配符*和?来进行查询
#wildcard查询 允许你使用通配符*和?来进行查询
#*就代表一个或者多个字符
#?仅代表一个字符
#注意:这个查询很影响性能
GET /user/students/_search
{
"query": {
"wildcard": {
"name": {
"value": "王?四"
}
}
}
}
fuzzy 模糊查询
#fuzzy 模糊查询
#value:查询的关键词
#boost:设置查询的权值,默认是1.0
#min_similarity:设置匹配的最小相似度,默认值为0.5,对于字符串,取值为0-1(包括0,1);对于数值,取值可能大于1;对于日期型,取值为1d,2d,1m这样,1d就代表一天
#prefix_length:指明区间分词项共同前缀长度,默认是0
#max_expansions:指明查询中的词项可扩展的数目,默认可以无限大
GET /user/students/_search
{
"query": {
"fuzzy": {
"name": "张"
}
}
}
GET /user/students/_search
{
"query": {
"fuzzy": {
"name": {
"value": "王",
"min_similarity":0.5
}
}
}
}
这里min_similarity似乎又不能使用了,可能是版本问题
fuzzy_like_this、fuzzy_like_this_field查询
==这两个好像也是因为版本问题,不能使用了,自行查看解决方案==
#fuzzy_like_this
#查询得到与给定内容相似的所有文档
#fields:字段组,默认是_all
#like_text:设置关键词
#ignore_tf:设置忽略词项的频次,默认是false
#max_query_terns:指明在生成的查询中查询词项的最大数目,默认是25
#min_similarity:设置匹配的最小相似度,默认值为
#prefix_length:指明区间分词项共同前缀长度,默认是0
#boost :设置权值,默认是1.0
#analyze:指明分析给定内容的分析器
GET /user/students/_search
{
"query": {
"fuzzy_like_this":{
"fields":["name"],
"like_text":"张",
"prefix_length":0.2
}
}
}
#fuzzy_like_this_field查询
#只作用在一个字段里
#其他与fuzzy_like_this功能一样
more_like_this查询
fields:定义字段组,默认_all
like_text:定义要查询的关键字
percent_terms_to_match:该参数指明一个文档必须匹配多大的词项才会被视为相似,默认值0.3,意思就是30%的比例
min_terms_freq:指在生成的查询中查询词项的最大数目,默认25
stop_word:该参数指明将被忽略的单词集合
min_doc_freq:词项应该至少在多少个文档中出现才不会被忽略,默认是5
max_doc_freq:出现词项的最大数据,以避免词项被忽略,默认是无限大
min_word_len:该参数指明单个单词的最小长度,低于该值的单子将被忽略,默认是无限大
max_mord_len:指明单个单词的最大长度,高于该值的单词将会被忽略,默认是无限大
boost_terms: 该参数指提升每个单词的权重时使用的权值,默认是1
boost:指明提升一个查询的权值,默认是1.0
analyze:指定用于分析的分析器
GET /user/students/_search
{
"query": {
"more_like_this": {
"fields": [
"name"
],
"like": "张三",
"min_term_freq": 1,
"min_doc_freq": 1
}
}
}
过滤
filter查询
#最简单的filter查询
#select document from product where price=20
#filtered 查询价格是20的商品
GET /store/products/_search
{
"query":{
"filtered":{
"query":{
"match_all":{}
},
"filter":{
"term":{
"price":20
}
}
}
}
}
#filtered 版本遗弃
#bool过滤查询,可以做组合查询
{
"bool":{
"must":[],
"should":[],
"must_not":[]
}
}
#must:条件必须满足
#should条件可以满足也可以不满足
#must_not:条件不需要满足
GET /store/products/_search
{
"query": {
"bool": {
"should": [
{"term": {"price": {"value": "20"}}},
{"term": {"productID": {"value": "SD1002137"}}}
],
"must_not": [
{"term": {"price": {"value": "30"}}}
]
}
}
}
嵌套查询
GET /store/products/_search
{
"query": {
"bool": {
"should": [
{"term": {"price": {"value": "20"}}},
{"term": {"productID": {"value": "SD1002137"}}}
],
"must_not": [
{"term": {"price": {"value": "30"}}},
{"bool": {
"must": [
{"term": {"price": {"value": "20"}}},
{"term": {"productID": {"value": "SD1002137"}}}
]
}}
]
}
}
}
range范围过滤
lt:小于
gte:大于等于
lte:小于等于
GET /store/products/_search
{
"query": {
"bool": {
"must": [
{"range": {
"price": {
"gte": 10,
"lte": 30
}
}},{
"term": {
"productID": {
"value": "sd1002137"
}
}
}
]
}
}
}
++这里有个问题需要注意,我们发现上面的productID的值如果我写成大写的SD1002137,就查不到结果++
#我们使用分析工具
GET /_analyze?
{"analyzer" : "standard", "text" : "SD1002137"}
#返回值:
{
"tokens" : [
{
"token" : "sd1002137",
"start_offset" : 0,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 0
}
]
}
#即这个字段默认是被分析的,即把大写转化成了小写
#那如果不想被分析,就需要删除这个索引,重新建立并指定mapping
这是因为创建的时候为建立mapping,因为系统使用了默认的mapping
#删除索引
DELETE /store
#指定mapping
PUT /store
{
"mappings": {
"products": {
"properties": {
"productID": {
"type": "keyword"
}
}
}
}
}
#导入数据
POST /store/products/_bulk
{"index":{"_id":1}}
{"price":10,"productID":"SD1002136"}
{"index":{"_id":2}}
{"price":20,"productID":"SD1002137"}
{"index":{"_id":3}}
{"price":30,"productID":"SD1002132"}
{"index":{"_id":4}}
{"price":40,"productID":"SD1002133"}
#查询结果
GET /store/products/_search
网友评论