😀
倒排索引、Logstash
ES
ES就类似数据库,用于存储海量数据
问题:为什么ES能快速查找数据?
ES用来倒排索引来存储文档!
ES刚开始得到数据后会根据搜索的关键字创建出一个词条和文档
查询的时候会根据词条作为索引关键字,倒排索引主要有两个组件构成,词条和文档
词条列表记录了每个文档中包含哪些单词
文档列出了所有在文档集合中出现过的单词所对应的id
正常 MySQL是这样存储的:要是查找的时候会根据索引id一个一个找,假如有100w条数据,我只查询小米手机,如果只有索引1有小米手机的话,MySQL也会把所有的索引找一遍,非常浪费时间
id |
名字 |
价格 |
1 |
小米手机 |
3000 |
2 |
华为手机 |
4000 |
3 |
苹果 |
5000 |
ES会根据数据库以json格式另外存在一个文档里,查找的时候只需要根据词条查找,然后通过各自对应id的并集查到想要的数据
词条(term) |
文档(id) |
小米 |
1 |
手机 |
1,2 |
华为 |
2 |
苹果 |
3 |
比喻:
可以比作是去图书馆找一本书,如果你要找一本SpringBoot的书和MySQL的书,去了书店找了半天,最后发现没有,体验肯定很差。
所以我们可以把书都分类,以单词名字开头来找,比如s开头的书都放一起,然后你就可以快速找到有没有这本书!
问题:存在哪里?用什么存?
ElasticSearch就相当于MySQL数据库。我们也需要有个图形工具来操作,这个工具叫Kibana
下载好镜像后,我们放在容器中执行,首先要让es和kibana容器互联。这里先创建一个网络:
1
| docker network create es-net
|
运行docker命令:
1 2 3 4 5 6 7 8 9 10 11
| docker run -d \ --name es \ -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \ -e "discovery.type=single-node" \ -v es-data:/usr/share/elasticsearch/data \ -v es-plugins:/usr/share/elasticsearch/plugins \ --privileged \ --network es-net \ -p 9200:9200 \ -p 9300:9300 \ elasticsearch:7.12.1
|
之后在浏览器中输入:http://本地端口:9200 即可看到elasticsearch的响应结果:
下载好可视化界面Kibana后,加载镜像:
1 2 3 4 5 6
| docker run -d \ --name kibana \ -e ELASTICSEARCH_HOSTS=http://es:9200 \ --network=es-net \ -p 5601:5601 \ kibana:7.12.1
|
Kibana启动比较慢,我们稍等一会,可以查看运行日志:
问题:词条可以自己设定都有哪些词吗?
可以安装IK分词器,分词器有两种模式 同时如果我们要新创建词语的话,也可以在IKAnalyzer.cfg.xml配置文件内容中修改添加
ik_smart
:最少切分
ik_max_word
:最细切分
什么是索引库?什么是文档?
索引库就相当于mysql里面的一张表,文档就是存储的一行一行的数据,只不过是按照json格式存储的
添加索引库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| PUT /heima { "mappings": { "properties": { "info":{ "type": "text", "analyzer": "ik_smart" }, "email":{ "type": "keyword", "index": false }, "name":{ "properties": { "firstName":{ "type":"keyword" }, "lastName":{ "type":"keyword" } } } } } }
|
查看索引库: get /索引库名
修改索引库:
1 2 3 4 5 6 7 8
| PUT /索引库名/_mapping { "properties": { "新字段名":{ "type": "integer" } } }
|
删除索引库:DELETE /索引库名
新增文档:
1 2 3 4 5 6 7 8 9
| POST /索引库名/_doc/文档id { "字段1": "值1", "字段2": "值2", "字段3": { "子属性1": "值3", "子属性2": "值4" } }
|
查看文档: GET /索引库名称/_doc/文档id
修改文档: 修改文档就是把原来的覆盖掉,重新建一个,如果没有对应的id,就和新增一样了,会新建一个
1 2 3 4 5
| PUT /索引库名/_doc/文档id { "字段1": "值1", "字段2": "值2" }
|
修改部分字段:
1 2 3 4 5 6
| POST /索引库名/_update/文档id { "doc": { "字段名": "新的值" } }
|
删除文档: DELETE /索引库名/_doc/id值
问题:在java中怎么操作?
导入ES的依赖
1 2 3 4
| <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency>
|
因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本:
1 2 3 4
| <properties> <java.version>11</java.version> <elasticsearch.version>7.12.1</elasticsearch.version> </properties>
|
连接ES:
1 2 3 4 5 6 7 8 9
| RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(HttpHost.create("http://192.168.200.130:9200")) );
RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost("192.168.200.130", 9200, "http")) );
|
关闭ES:
问题: 每次创建文档都要来连接一次,有简单的方法吗?
我们可以把它抽取出来
1 2 3 4 5 6 7 8 9 10 11
| private RestHighLevelClient client;
@BeforeEach void initClient() { client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.200.130:9200"))); }
@AfterEach void closeClient() throws IOException { client.close(); }
|
问题:连接好了,接下来怎么创建索引库呢?
1 2 3 4 5 6
| CreateIndexRequest request = new CreateIndexRequest("你要创建的索引库的名称");
CreateIndexRequest source = request.source(HotelConstants.MAPPING_TEMPLATE, XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);
|
问题:怎么判断索引库是否存在?
1 2 3 4 5 6
| GetIndexRequest request = new GetIndexRequest("这里输入索引库的名称");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
|
问题:怎么删除索引库? indices是操作数据库怎么样怎么样
1 2 3
| DeleteIndexRequest hotel = new DeleteIndexRequest("hotel"); AcknowledgedResponse delete = client.indices().delete(hotel,RequestOptions.DEFAULT);
|
问题:索引库有了,怎么添加文档呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @Autowired private IHotelService hotelService;
private RestHighLevelClient client; @BeforeEach void initClient() { this.client = new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.136.130:9200") )); }
@AfterEach void closeClient() throws IOException { this.client.close(); }
public void test(){ Hotel byId = hotelService.getById(36934L); HotelDoc hotelDoc = new HotelDoc(byId); String json = JSON.toJSONString(hotelDoc); IndexRequest hostl = new IndexRequest("hostl").id(hotelDoc.getId().toString()); hostl.source(json, XContentType.JSON); client.index(hostl, RequestOptions.DEFAULT); }
|
问题:怎么查看文档?
1 2 3 4 5 6 7 8 9 10
| GetRequest hotel = new GetRequest("hotel", "369341");
GetResponse response = client.get(hotel, RequestOptions.DEFAULT);
String json = response.getSourceAsString(); System.out.println(json);
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); System.out.println(hotelDoc);
|
问题:怎么修改文档?
因为全部修改就是新增覆盖,所以和新增一样。
1 2 3 4 5 6
| UpdateRequest request = new UpdateRequest("hotel", "369341"); request.doc("age",100,"name","我的新名字"); client.update(request, RequestOptions.DEFAULT);
|
问题:怎么删除文档?
1 2 3 4
| DeleteRequest request = new DeleteRequest("hotel", "369341");
client.delete(request, RequestOptions.DEFAULT);
|
问题:上面都是一个一个添加文档,怎么批量处理文档呢?
这里拿批量添加来举例,我们可以用BulkRequest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| List<Hotel> list = hotelService.list(); BulkRequest request = new BulkRequest(); for (Hotel hotel : list) { HotelDoc hotelDoc = new HotelDoc(hotel); String jsonString = JSON.toJSONString(hotelDoc); request.add(new IndexRequest("hostl").id(hotelDoc.getId().toString()).source(jsonString, XContentType.JSON)); } client.bulk(request, RequestOptions.DEFAULT);
|
ES有了数据库了,怎么查询呢?
常见的查询类型分为4类:
第一个是查询所有数据用于测试:
1 2 3 4 5 6 7 8
| # 查看全部数据 GET /索引库名/_search { "query": { "match_all": { } } }
|
全文检索查询:
一般用于输入关键字查询,得到词条后会去倒排索引库匹配关键字,查询需要的文档,返回用户。
可以理解为你要去图书馆找一本书,要根据书的名字取对应的书架找需要的书。
match查询:根据字段名查找
1 2 3 4 5 6 7 8 9
| # 全文检索 GET /hotel/_search { "query": { "match": { "字段名": "对应的名称" } } }
|
multi_match查询:与match查询类似,只不过允许同时查询多个字段。
1 2 3 4 5 6 7 8 9 10
| # 全文检索多个 GET /hotel/_search { "query": { "multi_match": { "query": "如家", "fields": ["地址","城市","品牌"] } } }
|
两个查询效果一样,有什么区别呢?
match查询只是根据字段就能查出所有,而multi_match是根据多个字段查询,虽然比较精确,但查询的也多,对查询性能也大,所以建议使用单字段查询方式
Logstash
从多个数据源读数据,然后把数据存到ES中
- input:组件收集数据
- filter:过滤数据
- output:输出数据
// TODO