一、开场白
1、哪些人需要学习ES
开发
测试
运维
利用数据来分析和查询的人
作为一个非关系型数据库,有着和mysql关系型数据库一样重要的地位。
2、学习ES需要具备什么基础
会写SQL语句
json格式
http请求
3、入门学习ES的内容
1)动手操作安装起来,最新的ES版本已经是7.4.2了。
官网https://www.elastic.co/cn/downloads/elasticsearch下载适合你OS版本的程序,解压缩并启动即可。
2)基于head插件或kibana的操作
3)简单理解为什么ES如此受欢迎,能够支持亿级数据。
4)ES是怎么支持关键词查询的
二、ES的几个知识点
1、和mysql的对比
数据库 | 表 | 行 | 字段 | DDL | |
---|---|---|---|---|---|
mysql | Database | Table | Row | Column | create table |
ES | Index 索引 | Type 类型 | Document 文档 | Field 字段 | Mapping |
客户端工具 | 复制方式 | |
---|---|---|
mysql | navicat, SQLyog, MySQL Workbench | 主从 |
ES | elasticsearch-head, Kibana, Elasticsearch-sql, Elasticsearch-HQ | 对等 |
es在5.x以后,没有了string类型,被拆分为关键词搜索keyword和全文搜索text。
keyword | text | |
---|---|---|
自动分词 | 否 | 是 |
支持聚合 | 是 | 否 |
ES支持多种数据类型,除了上面说的字符串类型,还有日期,布尔,二进制,还有geo,IP,Multi-fields等实用的类型。
2、集群、节点、分片、副本
1)集群与节点
集群一般要有3个节点,分开主分片和副本。
2)节点与分片
节点按角色可分为数据节点、主节点、协同节点。
分片数 = 节点数的1.5 ~ 3倍
3)分片与副本
副本是可以随时调整的,但分片指定后不可变。一般,一个分片有1~2个副本即可保证高可用。
请问下面的这个集群,有多少节点,多少分片,多少主分片,多少副本???
注意:集群下的分片数 = 索引的主分片数 * (1 + 副本数)
200GB的数据建议分片数为7~8个。(ES JVM heap 最大可以设置32G 。 30G heap 大概能处理的数据量 10 T。)
集群的状态 | 说明 |
---|---|
yellow | 所有主分片可用,但不是所有副本分片都可用。或者,硬盘空间超过85% |
green | 所有的主分片和副本分片都可用 |
red | 部分主分片不可用 |
在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据。开发环境单节点的情况下,由于es默认是有1个副本,主分片和副本不能在同一个节点上,所以副本就是未分配unassigned。新建索引像下面这样建立:
{
"settings":{
"number_of_shards":1,
"number_of_replicas":0
}
}
3、倒排索引
注意:不需要索引的字段,一定要明确定义出来,因为默认是自动创建索引的。
@Field(type = FieldType.text , index = FieldIndex.not_analyzed)
private String message;
比如有一个表User
ID | Name | Age | Sex |
---|---|---|---|
1 | Tom | 18 | Male |
2 | Kate | 18 | Female |
3 | Jack | 19 | Male |
ES分别为每个field都建立一个倒排索引
Term | Posting List ( 存储所有符合term的文档id) |
---|---|
18 | [1,2] |
19 | [3] |
现在的问题是如何快速查找到某个Term?
1)提高查询效率,mysql的B+树通过减少磁盘寻道次数,ES直接通过内存查找Term,不读磁盘。
但是如果Term太多,Term Dictionary也会很大,导致内存放不下。
2)于是就有了Term Index,就像字典里的索引页一样,A开头的有哪些term,分别在哪页,可以理解term index是一颗树。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。
注意:这棵树不会包含所有的term,它包含的是term的一些前缀。
3)把term index缓存到内存中。从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘随机读的次数。
(这里遗留一个问题,如果Term Index树还是很大怎么办?)
4、分词器
注意:对于String类型的字段,不需要analysis的也需要明确定义出来,因为默认是会analysis的。
@Field(type = FieldType.keyword)
private String message;
分词是将文本转换成一系列单词(Term or Token)的过程,也可以叫文本分析,在ES里面称为Analysis
@Field(type = FieldType.keyword ,analyzer = "ik_smart", searchAnalyzer = "ik_smart")
private String name;
分词器,推荐支持中文的ik-analyzer,详情参考Elasticsearch 默认分词器和中分分词器之间的比较及使用方法
5、压缩技巧
1)term index:用确定无环状态转换器(Deterministic acyclic finite state transducer, FST)压缩,以字节的方式存储所有的term。
(FST压缩率一般在3倍~20倍之间,相对于TreeMap/HashMap的膨胀3倍,内存节省就有9倍到60倍!)
我们对“october”,“november”, ”december”这三个词构建确定无环有限状态接收机(Deterministric acyclic finite state acceptor, FSA),如下:
(TRIE树只共享前缀,而FSA不仅共享前缀还共享后缀。)
我们对“mar”,“jul”,“jun”这三个词构建FST,如下:
(FST保证了不同的转移有唯一的值:mar的值为3,jul的值为7, jun的值为6。)
详细请参考 关于Lucene的词典FST深入剖析
2)posting list:增量编码压缩,将大数变小数,按字节存储。
Frame Of Reference压缩算法:
3)内存缓存数据:Roaring bitmaps,高效压缩解压和逻辑运算。
Bitmap是一种数据结构,假设有某个posting list:[1,3,4,7,10] 对应的bitmap就是:[1,0,1,1,0,0,1,0,0,1]
Bitmap的缺点是存储空间随着文档个数线性增长,
Roaring bitmaps需要打破这个魔咒就一定要用到某些指数特性:将posting list按照65535(=2^16-1,一个short的存储单位)为界限分块,比如第一块所包含的文档id范围在065535之间,第二块的id范围是65536131071,以此类推。再用<商,余数>的组合表示每一组id,这样每组里的id范围都在0~65535内了。
If a block has more than 4096 values, encode as a bit set, and otherwise as a simple array using 2 bytes per value
6、记得给索引取别名
巧妙利用这点,在你想修改某个字段的类型,或删除某个字段的时候,而不想修改代码的时候,作用就很明显了。当你的存储数据是基于时间序列规则的时候,你只要使用别名查询,ES会自动聚合。
好处是代码使用别名,只要别名一致,无论ES这么折腾,不会让你去修改代码。
三、文档的CRUD
ES提供了一套对外的restful api接口,让你操作变得规范和易懂。
详见官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/5.1/docs.html
操作类型 | 普通请求 | 批量操作 |
---|---|---|
查询 | _search + GET | _mget + GET |
新增 | _create + PUT | _reindex + POST |
删除 | DELETE | _delete_by_query + POST |
修改 | _update + POST | _update_by_query + POST |
1、创建索引
1)head插件:
开发环境地址: http://192.168.5.57:9100/
如果是单节点,非集群环境下,可以像下面这样设置作为测试:
新建成功后,可以在概览界面看到刚创建的索引user:
选择”动作“--》”删除“,在弹出框输入字符”删除"确认,便可以删除user索引。
看到如下弹出框,说明删除成功!!
2)postman
注意:请求方式是PUT,请求URL是http://192.168.5.57:9200/user , user是要创建的索引名。
删除索引,选择请求方式为DELETE
3)curl 等工具
curl -X PUT 'http://192.168.5.57:9200/user'
curl -X DELETE 'http://192.168.5.57:9200/user'
4)head自带的http请求,理解这一点很重要,后面数据的查询和增删改,通过这点都能实现。
创建索引
删除索引
2、查询数据
1)简单查询
它只能做查询搜索,不能创建、修改和删除数据,如果想要执行这些操作,那就只能用下一个功能块“复合查询”了。
索引reader有98个document,也就是我们说的98条记录,id字段是主键。
返回格式,你也可以选择json和csv格式:
下面是csv格式,支持导出。(亮点哦,建议我们所有的数据分析,打印的数据格式都是csv)
这里虽然说是基本查询,其实是支持多条件的查询了,类似navicat的查询。所以有必要再讲下使用。
must 返回的文档必须满足must子句的条件,类似于 == and
must not返回的文档必须不满足must not 子句的条件 类似于!= not
should 返回的文档只要满足should中的一个条件即可 类似于 || or
参数含义:
查询参数 | 含义 |
---|---|
term | 相当 |
wildcard | 通配符查询 例:* 商品 * |
prefix | 前缀 |
fuzzy | 区间,分词模糊查询 结合max_expansions 和min_similarity,数值则表示在此数值的增加,减小数量在多少范围之内的数据;字符则为在此自负基础上增加/减少多少字符范围内的数据 |
range | 区间查询,如果type是时间类型,可用内置now表示当前,-1d/h/m/s来进行时间操作 |
query_string | 可以对int, long, string查询,对int,long只能本身查询,对string进行分词和本身查询 |
text | 片段 |
missing | 返回没有字段或值为null的文档 |
另外:gt/lt/gte/lte就不解释了。
postman查询:地址http://192.168.5.57:9200/user/_search ,请求方式是GET,多条件查询建议使用上面的head。
如果你是指定id查询,末尾就不需要加_search了。否则会报错
No handler found for uri [/user/collect/3/_search] and method [GET]
2)复杂查询
相当于postman了,支持发送RESTful API到elasticsearch服务执行。
注意:勾选”易读“,对json文本进行格式化。复杂查询就不单是查询了,还支持增删改。
3、新增数据
1)指定index的type和id:
可以看到新增了一条记录。。。
上面是id指定,也可以让ES自动生成,但是不建议使用自增的方式插入数据:
这里大家可能会有疑问,user我知道是索引,那collect是啥子呢,是我们定义的type了,也就是表名。es是非关系型数据库,好处就是灵活。你可以自定义_type。
方便大家测试,我将user的属性json文本拷贝出来,仅供参考:
{
"name": "测试12",
"age": "18",
"gender": "male",
"ctime": "",
"utime": ""
}
ES索引的field是很灵活的,不用管总共有哪些field,只写你所需要的就好。
{
"name": "测试14",
"gender": "male"
}
当然也可以使用postman等工具哈,归功于ES对外很好的restful api接口。
4、修改数据
根据主键的唯一性,进行覆写操作。
请求地址为http://192.168.5.57:9200/reader/collect/47/ , 格式是http://192.168.5.57:9200/<_index>/<_type>/<_id> , 操作类型是_update(指明是修改操作),http请求方式是POST。
postman工具,请求地址是http://192.168.5.57:9200/user/collect/4/_update , 请求方式还是post
{
"doc":{
"name": "测试4"
}
}
支持条件修改:
请求地址是:http://192.168.5.57:9200/user/_update_by_query/ ,请求方式是POST。
{
"query": {
"match": {
"name": "zhangsan"
}
}
}
5、删除数据
请求地址为http://192.168.5.57:9200/user/collect/AW6g18C-QJexQIWLsAxW/ 格式是http://192.168.5.57:9200/<_index>/<_type>/<_id> ,http请求方式是DELETE。
支持条件删除:
请求地址是:http://192.168.5.57:9200/user/_delete_by_query/, 请求方式是POST
query如何来,建议从head的简单查询视图里拷贝。
{
"query": {
"match": {
"name": "zhangsan"
}
}
}
四、Mapping
个人理解这个类似于mysql的DDL语句,上面讲述的crud是属于DML语句。为什么说类似,而不完全是呢?mysql修改一个字段的类型轻轻松,ES会让你的工作绕一大圈子!!
1、查看mapping
在创建索引的时候,一起定义映射:
请求地址:http://192.168.5.57:9200/user ,请求方式PUT。
{
"settings":{
"index":{
"number_of_shards":1,
"number_of_replicas":0
}
},
"mappings":{
"collect":{
"properties":{
"name":{
"type":"text"
},
"age":{
"type":"long"
},
"gender":{
"type":"keyword"
},
"message":{
"type":"text",
"index":"not_analyzed"
},
"ctime":{
"type":"long"
},
"utime":{
"type":"long"
}
}
}
}
}
查看mapping
请求地址:http://192.168.5.57:9200/user/_mapping/, 请求方式GET
2、新增mapping:
请求地址是http://192.168.5.57:9200/user/collect/_mapping ,主要要指定index和type。
3、修改mapping
准确的说,是新增字段,而不是修改原有的字段类型。如果你需要修改,怎么办?要么删除索引重新导入,要么reindex。有时候请想下新增字段是不是能够解决你的需求~~请参考https://www.cnblogs.com/royfans/p/11436395.html
postman操作:
head操作: