1.elasticsearch的认识与介绍
什么是elasticsearch?
- 一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能
什么是elastic stack(ELK)?
- 是以elasticsearch为核心的技术栈,包括beats、Logstash、kibana、elasticsearch被广泛应用在日志数据分析、实时监控等领域。
什么是Lucene?
- 是Apache的开源搜索引擎类库,提供了搜索引擎的核心API
正向索引
例如给下表(tb_goods)中的id创建索引:
基于title做模糊查询,只能是逐行扫描数据,流程如下:
1)用户搜索数据,条件是title符合"%手机%"
2)逐行获取数据,比如id为1的数据
3)判断数据中的title是否符合用户搜索条件
4)如果符合则放入结果集,不符合则丢弃。
5)查出所有结果并返回
这时只能是全表扫描,随着数据量增加,其查询效率也会越来越低。当数据量达到数百万时,就是一场灾难。
倒排索引
倒排索引中有两个非常重要的概念
- 文档(
Document
):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息 - 词条(
Term
):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:“我”、“是”、“中国人”、“中国”、"国人"这样的几个词条。
创建倒排索引是对正向索引的一种特殊处理,流程如下
- 将每一个文档的数据利用算法分词,得到一个个词条
- 创建表,每行数据包括词条、词条所在文档id、位置等信息
- 因为词条唯一性,可以给词条创建索引,例如hash表结构索引
倒排索引的搜索流程如下(以搜索"华为手机"为例):
1)用户输入条件"华为手机"进行搜索。
2)对用户输入内容分词,得到词条:华为
、手机
。
3)拿着词条在倒排索引中查找,可以得到包含词条的文档id:1 2 3
4)拿着文档id到正向索引中查找具体文档。
elasticsearch中有很多独有的概念,与mysql中略有差别,但也有相似之处。
我们统一的把mysql与elasticsearch的概念做一下对比:
- Mysql:擅长事务类型操作,可以确保数据的安全和一致性
- Elasticsearch:擅长海量数据的搜索、分析、计算
elasticsearch和kibana以及ik分词器的安装请参考:elasticsearch的安装
2.索引库操作
索引库就类似数据库表,mapping映射就类似表的结构。我们要向es中存储数据,必须先创建“库”和“表”。就需要编写DDL语句实现创建库和表。之前我们对比理解过 mapping等同于数据库中DDL。
2.1.mapping映射属性
mapping是对索引库中文档的约束,具体的映射针对的是字段。常见的mapping属性包括:
- type:字段数据类型,常见的简单类型有:
- 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
- 数值:long、integer、short、byte、double、float、
- 布尔:boolean
- 日期:date
- 对象:object
- geo_point gps坐标
- index:是否创建索引,默认为true
- analyzer:使用哪种分词器(前提是:要分词 才需要指定分词器)
- properties:该字段的子字段
对于下面的json文档(理解成数据库中的行):
{
"age": 28,
"weight": 59.1,
"isMarried": false,
"info": "腾讯公司大老板",
"email": "123456@qq.com",
"score": [99.1, 99.5, 98.9],
"name": {
"firstName": "云腾",
"lastName": "马"
}
}
对应的每个字段映射(mapping):
- age:类型为 integer;参与搜索,因此需要index为true;无需分词器
- weight:类型为float;参与搜索,因此需要index为true;无需分词器
- isMarried:类型为boolean;参与搜索,因此需要index为true;无需分词器
- info:类型为字符串,需要分词,因此是text;参与搜索,因此需要index为true;分词器可以用ik_smart
- email:类型为字符串,但是不需要分词,因此是keyword;不参与搜索,因此需要index为false;无需分词器
- score:虽然是数组,但是我们只看元素的类型,类型为float;参与搜索,因此需要index为true;无需分词器
- name:类型为object,需要定义多个子属性
- name.firstName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器
- name.lastName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器
2.2.索引库的CRUD
这里我们统一使用Kibana编写DSL的方式来演示。
2.2.1.创建索引库和映射【练习】
基本语法:
- 请求方式:PUT
- 请求路径:/索引库名,可以自定义
- 请求参数:mapping映射
格式:
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}
示例:
PUT /user_info
{
"mappings": {
"properties": {
"age" :{
"type": "integer"
},
"weight":{
"type": "double"
},
"isMarried":{
"type": "boolean"
},
"info":{
"type": "text",
"analyzer": "ik_smart"
},
"email":{
"type": "keyword",
"index": false
},
"score":{
"type": "float"
},
"name":{
"properties": {
"firstName":{
"type":"keyword"
},
"lastName":{
"type":"keyword"
}
}
}
}
}
}
2.2.2.查询索引库
基本语法:
- 请求方式:GET
- 请求路径:/索引库名
- 请求参数:无
格式:
GET /索引名
GET /user_info
2.2.3.修改索引库
倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改mapping。
虽然无法修改mapping中已有的字段,但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。
语法说明:
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "text"
}
}
}
2.2.4.删除索引库
语法:
- 请求方式:DELETE
- 请求路径:/索引库名
- 请求参数:无
格式:
DELETE /索引名称
3.文档操作
3.1.新增文档
语法:
POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
示例:
POST /user_info/_doc/1
{
"info": "这是测试信息",
"email": "zy111@163.cn",
"name": {
"firstName": "云",
"lastName": "赵"
}
}
响应:
注意,文档都会有一个唯一标识,我们把它叫做_id (文档ID) 类似于数据库的一行都会有一个主键来作为唯一标识一样。
3.2.根据ID查询文档
根据rest风格,新增是post,查询应该是get,不过查询一般都需要条件,这里我们把文档id带上。
语法:
GET /{索引库名称}/_doc/{id}
GET /user_info/_doc/1
查看结果:
3.3.根据ID删除文档
删除使用DELETE请求,同样,需要根据id进行删除:
语法:
DELETE /{索引库名}/_doc/id值
示例:
# 根据id删除数据
DELETE /user_info/_doc/1
3.4.根据ID修改文档
修改有两种方式:
- 修改全部:直接覆盖原来的文档,实际上当有存在的ID的时候,修改便是**(先删除再添加,版本号进行叠加)**
- 修改部分:修改文档中的部分字段
3.4.1.修改全部
全量修改是覆盖原来的文档,其本质是:
- 根据指定的id删除文档
- 新增一个相同id的文档
注意:如果根据id删除时,id不存在,第二步的新增也会执行,也就从修改变成了新增操作了。
语法:
PUT /{索引库名}/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
// ... 略
}
示例:
PUT /user_info/_doc/1
{
"info": "阿里巴巴大老板",
"email": "my@itcast.cn",
"name": {
"firstName": "云",
"lastName": "马"
}
}
3.4.2修改部分
增量修改是只修改指定id匹配的文档中的部分字段。
语法:
POST /{索引库名}/_update/文档id
{
"doc": {
"字段名": "新的值",
}
}
示例:
POST /user_info/_update/1
{
"doc": {
"email": "ZhaoYun@itcast.cn"
}
}
3.5.总结
文档操作
- 创建文档:POST /{索引库名}/_doc/文档id { json文档 }
- 查询文档:GET /{索引库名}/_doc/文档id
- 删除文档:DELETE /{索引库名}/_doc/文档id
- 修改文档:
- 修改全部:PUT /{索引库名}/_doc/文档id { json文档 } ,不管是多少数据,都会将之前的数据删除,并再次添加
- 修改部分:POST /{索引库名}/_update/文档id { “doc”: {字段}}
如果我的文章对您有帮助,还请您多多支持我。支付宝帮忙扫一下吧