一、引入
1、海量数据存储
mysql 表规模 最大千万级别 (还和列数有关系) ,超过千万,性能极差
2、全文检索
select * from book where name like ‘%jvm%’ or describe like ‘%jvm%’
现在 describe 描述非常长 ,mysql 检索 describe like ‘%jvm%’,1 先读取这一行 ,匹配describe 内容是否还有jvm , 效率很差 ,
MySQL可以做到 ,但是性能非常的差,需要全文检索引擎(类似百度搜索相关技术)
3、高亮显示
4、使用 搜索引擎 解决这些问题
常用搜索引擎 有 es ,Solr
es:最为常用,Solr逐步被边缘化(性能更为强大,比较难用,文档没有es 丰富)
5、搜索引擎作用
1.作为全文检索,搜索使用
2.作为数据库使用,存储海量数据(大部分公司使用es ,作为数据库使用,查询效率高)
3.查询结果高亮显示
4.作为日志搜索,监控工具。在代码运行过程中,总有一些异常,
将异常记录下来 使用log4j写入到日志文件中,同时将日志文件的新增内容导入到es,es 可以立即的查询到Exception 相关键字,可以针对结果进行监控告警 (比如 如果5s 发生 20个Exception 立即发送邮件,短信告警)
二、介绍
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析(类似mysql groupby 分组聚合)引擎,能够解决不断涌现出的各种用例。 作为 Elastic Stack 的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。
Elasticsearch 简称ES
Kibana
使用Es 的可视化工具,可以通过网页 ,查看 es,使用es,管理es
https://www.elastic.co/cn/elasticsearch/
Es 基于 Lucene 的一个 搜索引擎,使用java开发,具有分布式横向拓展能力。
倒排索引(重点****)**
索引:
英文词典,首页是不是都可以看到目录,目录中有所有的单词已经,单词所在的页面,
目录( 单词,页码)便于我们快速的查找。
倒排索引作用:
1.快速的查找对应的文章
2.通过倒排索引 记录词在文章中出现的 频次 和 位置,能够够好的衡量 当前单词对于文章的重要程度()
三、安装
1、前提条件
1.关闭防火墙 (注意关闭防火墙要重启docker)
2.将虚拟内存保持4g以上 如果小于4g 可能无法使用 ,尽量4g
3.设置 sysctl.conf参数为什么要配置sysctl.conf参数,因为es 会生成很多文件碎片,必须扩大文件句柄 已经 jvm 的性能
1、检查 vim /etc/sysctl.conf 是否有 vm.max_map_count=655360
2、没有 执行该命令
echo “vm.max_map_count=655360”>>/etc/sysctl.conf
3、再次 检查 vim /etc/sysctl.conf
4.sysctl -p #执行命令 让刚才的配置生效
2、linux安装
1、yml文件
version: "3.1"
services:
elasticsearch:
image: daocloud.io/library/elasticsearch:6.5.4
restart: always
container_name: elasticsearch
ports:
- 9200:9200
- 9300:9300
kibana:
image: daocloud.io/library/kibana:6.5.4
restart: always
container_name: kibana
ports:
- 5601:5601
environment:
- elasticsearch_url=http://192.168.12.130:9200 #这里是自己的ip
depends_on:
- elasticsearch
2、启动
[root@mastera docker-compose-es]# docker-compose up
3、配置kibana 配置
进入kibana容器中
[root@mastera ~]# docker exec -it kibana bash
bash-4.2$ vi config/kibana.yml
4、安装ik 分词器
ik 分词器 是一个中文 分词器,用来切分中文文章
[root@mastera ~]# docker exec -it elasticsearch bash
[root@44b91458f5b7 elasticsearch]# cd bin
[root@44b91458f5b7 bin]# ./elasticsearch-plugin install http://tomcat01.qfjava.cn:81/elasticsearch-analysis-ik-6.5.4.zip
5、重启 es 和 kibana
[root@mastera ~]# docker restart elasticsearch
elasticsearch
[root@mastera ~]# docker restart kibana
kibana
6、访问http://192.168.12.130:5601/
说明:
http://192.168.12.130:5601/ kibana 管理界面地址
9200 :是es 对外提供的http 请求端口
9300: 是es 对外提供的TCP 请求端口
7、测试 ik 分词器
PUT /book_v7 # 创建一个索引
POST /book_v7/_analyze
{
"analyzer": "ik_max_word",
"text": ["中华人民共和国"]
}
POST /book_v7/_analyze
{
"analyzer": "ik_max_word",
"text": ["如果我告诉您有一个 Redis 的分支版本,它的性能比原生的 Redis 快 5 倍,而且延迟却降低近 5 倍,你会不会想了解一下这个项目?而如果您不再需要哨兵节点并且您的副本可以接受读取和写入,这将有可能使分片数量减少 10 倍,这样对你的吸引力是不是更大了呢?"]
}
四、es 结构
索引:其实就是一个存储数据的位置(类似数据库)
分片:一个索引可以分为多个分片,类似redis中的slot,默认有5个分片
备份:在es的集群中,每一个索引都有多个分片,每一个分片在其他机器上都已经有一个副本分片
类型type
在es 5.x 版本中,索引下可以存储多种 类型(Type),在Es 7.x 版本中只允许存储一种 类型
es: 索引(index)-------->类型(Type)
mysql: 数据库(db)--------> 表 (table)
文档 Doc
文档 Doc,通俗的理解就是 一条数据,类似于 myslq 表中的一行数据,有很多字段(属性)组成
es: 索引(index)-------->类型(Type) -------> 文档 (doc)
mysql: 数据库(db)--------> 表 (table) -------> 行 (row)
属性Field
属性就是 文档 Doc 中的字段
es: 索引(index)-------->类型(Type) -------> 文档 (doc)-------> 属性(Field)
mysql: 数据库(db)--------> 表 (table) -------> 行 (row)-------> 列(column)
Filed属性的类型
字符串类型:
text : 文本类型 ,该类型的数据可以进行 单词切分
keyword: 关键词,关键字 ,当前类型的内容 无法被再次切分
数值类型:
- long:取值范围为-9223372036854774808~922337203685477480(-2的63次方到2的63次方-1),占用8个字节
- integer:取值范围为-2147483648~2147483647(-2的31次方到2的31次方-1),占用4个字节
- short:取值范围为-32768~32767(-2的15次方到2的15次方-1),占用2个字节
- byte:取值范围为-128~127(-2的7次方到2的7次方-1),占用1个字节
- double:1.797693e+308~ 4.9000000e-324 (e+308表示是乘以10的308次方,e-324表示乘以10的负324次方)占用8个字节
- float:3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,e-45表示乘以10的负45次方),占用4个字节
- half_float:精度比float小一半。
- scaled_float:根据一个long和scaled来表达一个浮点型,long-345,scaled-100 -> 3.45
时间类型:
- date类型,针对时间类型指定具体的格式
布尔类型:
- boolean类型,表达true和false
二进制类型: 几乎不用
- binary类型暂时支持Base64 encode string
五、完成 es 的增删改查
操作ES的RESTful语法
GET请求:
- http://ip:port/index:查询索引信息
- http://ip:port/index/type/doc_id:查询指定的文档信息
POST请求:
- http://ip:port/index/type/_search:查询文档,可以在请求体中添加json字符串来代表查询条件
- http://ip:port/index/type/doc_id/_update:修改文档,在请求体中指定json字符串代表修改的具体信息
PUT请求:
- http://ip:port/index:创建一个索引,需要在请求体中指定索引的信息,类型,结构
- http://ip:port/index/type/_mappings:代表创建索引时,指定索引文档存储的属性的信息
DELETE请求:
- http://ip:port/index:删除索引
- http://ip:port/index/type/doc_id:删除指定的文档
命令
创建索引
PUT /person
获取索引结构
GET /person
删除索引
DELETE /person
示例
PUT /book
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings": {
"novel":{
"properties":{
"name":{
"type":"text",
"analyzer":"ik_max_word", # 指定分词器
"index":true, #表明当前属性可以被创建索引,进行搜索
"store":false # 额外存储 当前列 为了提高查询效率可以额外为此列存储副本
},
"author": {
"type": "keyword"
},
"count": {
"type": "long"
},
"on_sale": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"descr": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
select name form student where id = 1 #1.先把一行读出来 2在一行中 找到对应的列 事项加入有200 列
select name form student # 性能损耗很大
es 如何解决此问题?
使用该列 “store”:true ,让es 单独为这个存一个副本,便于查询
插入数据
# put 增加数据必须制定 id
PUT /book/novel/1
{
"author":"曹雪芹",
"count" :1000,
"descr":"中国四大名著",
"name":"红楼梦",
"on_sale":"2021-06-28"
}
# post 请求 会自动 返回 "_id" : "LU7ZT3oB7oIJcj0lHhQX",
POST /book/novel/
{
"author":"吴承恩",
"count" :1000,
"descr":"中国四大名著 孩子最爱",
"name":"西游记",
"on_sale":"2021-06-28"
}
查看数据
GET /book/novel/1
GET /book/novel/LU7ZT3oB7oIJcj0lHhQX
修改数据
# post 提交数据制定id 会覆盖原来所有的数据,有课能造成数据丢失
POST /book/novel/LU7ZT3oB7oIJcj0lHhQX
{
"count" :6000,
"descr":"中国四大名著 孩子最爱",
"name":"西游记",
"on_sale":"2021-06-28"
}
# put 制定id 如果id 存在,则全部覆盖
PUT /book/novel/LU7ZT3oB7oIJcj0lHhQX
{
"count" :8000,
"descr":"中国四大名著 孩子最爱",
"name":"西游记"
}
# post + _update 可以对单个属性,修改,不会全部覆盖
POST /book/novel/LU7ZT3oB7oIJcj0lHhQX/_update
{
"doc": {
"count":10000,
"on_sale":"2021-06-28",
"author":"吴承恩"
}
}
删除数据
DELETE /book/novel/LU7ZT3oB7oIJcj0lHhQX
普通的属性 字符串有哪些类型
text
keyword
一般如果我们配置字符串的属性的类型,那么他只有一种类型但是es 支持,直接添加数据,不配置mappings,那么对应的类型可以即是 text 有是keyword
PUT /book2
#直接在 索引上添加 数据,不设置 mapping
POST /book2/novel/
{
"author":"吴承恩",
"count" :1000,
"descr":"中国四大名著 孩子最爱",
"name":"西游记",
"on_sale":"2021-06-28"
}
GET /book2
六、es 和 springboot 整合
1、创建 springboot 工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、配置文件
# ELASTICSEARCH (ElasticsearchProperties)
# Elasticsearch cluster name. 必须写正确 必须 和 docker 容器中的 es 配置一致
spring.data.elasticsearch.cluster-name=docker-cluster
# Comma-separated list of cluster node addresses. 必须是 9300
# springboot-es 和 es 交互 是通过 tcp(netty)
spring.data.elasticsearch.cluster-nodes=192.168.12.130:9300
# Whether to enable Elasticsearch repositories.
spring.data.elasticsearch.repositories.enabled=true
3、启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EsApplication {
public static void main(String[] args) {
SpringApplication.run(EsApplication.class,args);
}
}
4、实体类
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
*
*
* {
* "author":"吴承恩",
* "count" :1000,
* "descr":"中国四大名著 孩子最爱",
* "name":"西游记",
* "on_sale":"2021-06-28"
* }
*/
@Document(indexName="book3",type="novel",shards=5,replicas=1) //标记当前 类对应一个 文档
public class Book {
// 对应查询结果 _id
@Id
private String id;
@Field(type = FieldType.Keyword)
private String author;
@Field(type = FieldType.Text,analyzer="ik_max_word")
private String name;
@Field(type = FieldType.Long)
private long count;
@Field(type = FieldType.Text,analyzer="ik_max_word")
private String descr;
@Field(type = FieldType.Date,format = DateFormat.year_month_day)
private String on_sale;
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public String getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
public String getOn_sale() {
return on_sale;
}
public void setOn_sale(String on_sale) {
this.on_sale = on_sale;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
@Override
public String toString() {
return "Book{" +
"author='" + author + '\'' +
", name='" + name + '\'' +
", count=" + count +
", descr='" + descr + '\'' +
", on_sale='" + on_sale + '\'' +
'}';
}
}
5、测试类
import com.qfedu.entity.Book;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class EsTest {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Test
public void createIndex(){
// 创建索引
elasticsearchTemplate.createIndex(Book.class);
// 为索引设置 mapping
elasticsearchTemplate.putMapping(Book.class);
}
// 增删改查
@Test
public void addData(){
Book book = new Book();
book.setAuthor("张三");
book.setCount(1000555);
book.setDescr("一个是阆苑仙葩,一个是美玉无瑕");
book.setId("2");
book.setName("盘龙");
book.setOn_sale("2020-01-12");
IndexQuery indexQuery = new IndexQueryBuilder()
.withId(book.getId())
.withObject(book)
.build();
List<IndexQuery> queries = new ArrayList<>();
queries.add(indexQuery);
elasticsearchTemplate.bulkIndex(queries);
}
@Test
public void update(){
// 配置要更新的属性
Map<String, Object> params = new HashMap<>();
// 其中某一个属性
params.put("count", 999);
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.doc(params);
UpdateQueryBuilder updateQueryBuilder = new UpdateQueryBuilder();
*********配置 id
updateQueryBuilder.withId("2");
updateQueryBuilder.withUpdateRequest(updateRequest);
updateQueryBuilder.withClass(Book .class);
UpdateQuery build = updateQueryBuilder.build();
UpdateResponse update1 = elasticsearchTemplate.update(build);
// 获取更新结果
System.out.println(update1.getResult().getLowercase());
}
/**
* 根据id 查选
*/
@Test
public void queryById(){
GetQuery query = new GetQuery();
// 配置查询id
query.setId("2");
Book book = elasticsearchTemplate.queryForObject(query, Book.class);
System.out.println("book:"+book);
}
/**
* 模糊查询
*/
@Test
public void query(){
SearchQuery searchQuery = new NativeSearchQueryBuilder()
// 查询某一 列中的 词
.withQuery(QueryBuilders.queryStringQuery("descr:美玉"))
// 按照分页查询
.withPageable(new PageRequest(0, 20))
.build();
List<Book> list = elasticsearchTemplate.queryForList(searchQuery, Book.class);
System.out.println("list:"+list);
}
/**
* 删除
*/
@Test
public void delete(){
// 根据id删除 删除 某一条数据 一个文档
elasticsearchTemplate.delete(Book.class, "2");
// 删除索引
// elasticsearchTemplate.deleteIndex(Book.class);
}
}