Redis是一个基于内存的 key-value 结构数据库。Redis是一款采用key-value数据存储格式的内存级NoSQL数据库
,重点关注数据存储格式,是key-value格式,也就是键值对的存储形式。与MySQL数据库不同,MySQL数据库有表、有字段、有记录,Redis没有这些东西,就是一个名称对应一个值,并且数据以存储在内存中使用为主。redis的基本使用
全文搜索的理解:
比如用户要在淘宝上买一本书(Java开发),那么他就可以以Java为关键字进行搜索,不管是书名中还是书的介绍中,甚至是书的作者名字,只要包含java就作为查询结果返回给用户查看。这就可以理解为全文搜索。
全文搜索的实现:
要实现全文搜索的效果,不可能使用数据库中like操作去进行比对,这种效率太低了。ES设计了一种全新的思想,来实现全文搜索。具体操作过程如下:
分词结果关键字 | 对应id |
---|---|
中华 | 1 |
人民 | 1,2 |
共和国 | 1 |
代表 | 2 |
大会 | 2 |
上述过程中分词结果关键字内容每一个都不相同,作用有点类似于数据库中的索引,是用来加速数据查询的。
windows版安装包下载地址:https://www.elastic.co/cn/downloads/elasticsearch
ES的运行:在bin目录下,双击elasticserach.bat文件。(默认端口号:9200)
然后访问:http://localhost:9200/,看到下面的json数据后,表示es已经启动成功。
创建索引:注意这里使用的请求方式是put而不是post
获取索引
{
"book": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "book",
"creation_date": "1704103713618",
"number_of_replicas": "1",
"uuid": "1mabgD9eR7WvHVZeCBfVqw",
"version": {
"created": "7160299"
}
}
}
}
}
参数数据如下:
{
"mappings":{ //mapping表示:定义mappings属性,替换创建索引时对应的mappings属性
"properties":{ // properties表示:定义索引中包含的属性设置(属性是自定义的)
"id":{ // 设置索引中包含id属性(相当于数据库表中创建一个id字段)
"type":"keyword" //设置当前属性为关键字,可以被直接搜索
},
"name":{ // 设置索引中包含name属性
"type":"text", //设置当前属性是文本信息,参与分词
"analyzer":"ik_max_word", //选择当前属性的分词策略,这里表示使用IK分词器进行分词
"copy_to":"all" // 表示把分词结果拷贝到all属性中,即all属性中也有name属性同样的作用
},
"type":{
"type":"keyword"
},
"description":{
"type":"text",
"analyzer":"ik_max_word",
"copy_to":"all"
},
"all":{ //all是一个定义属性(虚拟的属性,数据库中不存在的属性),用来描述多个字段的分词结果集合,当前属性可以参与查询
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
查询带分词器的索引
返回值:(与前面的查询不带分词器的相比,会发现mappings里面多了很多数据信息)
{
"books": {
"aliases": {},
"mappings": { //mappings属性已经被替换
"properties": {
"all": {
"type": "text",
"analyzer": "ik_max_word"
},
"description": {
"type": "text",
"copy_to": [
"all"
],
"analyzer": "ik_max_word"
},
"id": {
"type": "keyword"
},
"name": {
"type": "text",
"copy_to": [
"all"
],
"analyzer": "ik_max_word"
},
"type": {
"type": "keyword"
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "books",
"creation_date": "1704103876876",
"number_of_replicas": "1",
"uuid": "nQ2Jmml6QSOGwOI2cswwJw",
"version": {
"created": "7160299"
}
}
}
}
}
POST请求 http://localhost:9200/books/_doc #使用系统生成id(自动帮你创建)
POST请求 http://localhost:9200/books/_doc/1 #使用指定id,不存在创建,存在更新(版本递增)
POST请求 http://localhost:9200/books/_create/1 #使用指定id(必须指定id)
传参数据一般不使用id属性:因为指定了也不会生效,要么默认帮你创建,要么在请求路径上进行指定
参数的使用:
{
"id": 1, //一般不使用这一行
"name": "springboot1",
"type": "book",
"desctiption": "an book"
}
{
"_index": "books",
"_type": "_doc",
"_id": "MgeZxIwB35gR6M6IUssu",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
第二种请求方式:
{
"_index": "books",
"_type": "_doc",
"_id": "55",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
第三种请求方式:
返回结果:
{
"_index": "books",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
GET请求 http://localhost:9200/books/_search?q=name:springboot
# q=查询属性名:查询属性值
//文档通过请求参数传递,数据格式json
{
"name":"springboot",
"type":"springboot",
"description":"springboot"
}
//文档通过请求参数传递,数据格式json
{
"doc":{ //部分更新并不是对原始文档进行更新,而是对原始文档对象中的doc属性中的指定属性更新
"name":"springboot" //仅更新提供的属性值,未提供的属性值不参与更新操作
}
}
整合步骤(依旧是拿三板斧):
ES有两种级别的客户端,一种是Low Level Client,一种是High Level Client。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
步骤②:进行基础配置
spring:
elasticsearch:
rest:
uris: http://localhost:9200
配置ES服务器地址,端口9200(默认就是9200)
步骤③:使用springboot整合ES的专用客户端接口ElasticsearchRestTemplate来进行操作
@SpringBootTest
class Springboot18EsApplicationTests {
@Autowired
private ElasticsearchRestTemplate template;
}
@SpringBootTest
class Springbootests {
@BeforeEach //在测试类中每个操作运行前运行的方法
void setUp() {
//各种操作
}
@AfterEach //在测试类中每个操作运行后运行的方法
void tearDown() {
//各种操作
}
}
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
dependency>
这里的springboot版本为:2.5.4,es的版本为7.16.2,那时候的springboot没有整合高级别的ES,所以配置文件里不需要配置,只能写硬编码配置
步骤②:使用编程的形式设置连接的ES服务器,并获取客户端对象
步骤③:使用客户端对象操作ES,例如创建索引,为索引添加文档等等操作。
@SpringBootTest
class Springboot18EsApplicationTests {
@Autowired
private BookMapper bookMapper;
private RestHighLevelClient client;
@Test
void testCreateClient() throws IOException {
//先创建ES客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
client.close();
}
}
配置ES服务器地址与端口9200,记得客户端使用完毕需要手工关闭。由于当前客户端是手工维护的,因此不能通过自动装配的形式加载对象。
@SpringBootTest
class Springboot18EsApplicationTests {
@Autowired
private BookMapper bookMapper;
private RestHighLevelClient client;
@Test
void testCreateIndex() throws IOException {
//先创建ES客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
//在通过ES客户端创建索引
CreateIndexRequest request = new CreateIndexRequest("books");
client.indices().create(request, RequestOptions.DEFAULT);
client.close();
}
}
高级别客户端操作是通过发送请求的方式完成所有操作的,ES针对各种不同的操作,设定了各式各样的请求对象,上例中创建索引的对象是CreateIndexRequest,其他操作也会有自己专用的Request对象。
使用分词器IK:
//json的参数:
{
"mappings":{
"properties":{
"id":{
"type":"keyword"
},
"name":{
"type":"text",
"analyzer":"ik_max_word",
"copy_to":"all"
},
"type":{
"type":"keyword"
},
"description":{
"type":"text",
"analyzer":"ik_max_word",
"copy_to":"all"
},
"all":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
@Test
void testCreateClientIndexByIk() throws IOException {
// 创建客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
CreateIndexRequest request = new CreateIndexRequest("books");
String json = "{\n" +
" \"mappings\":{\n" +
" \"properties\":{\n" +
" \"id\":{\n" +
" \"type\":\"keyword\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"copy_to\":\"all\"\n" +
" },\n" +
" \"type\":{\n" +
" \"type\":\"keyword\"\n" +
" },\n" +
" \"description\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"copy_to\":\"all\"\n" +
" },\n" +
" \"all\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
//设置请求中的参数(添加分词器)
request.source(json, XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);
client.close();
}
IK分词器是通过请求参数的形式进行设置的,设置请求参数使用request对象中的source方法进行设置,至于参数是什么,取决于你的操作种类。当请求中需要参数时,均可使用当前形式进行参数设置。
// 添加文档:
@Test
void testCreateClientIndexByIkAddData() throws IOException {
// 创建客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
// 进行添加操作,因为前面已经创建好了books索引
Book book = bookMapper.selectById(1);
// 把book对象数据转换为json数据,
String json = JSON.toJSONString(book);
// 指定添加的文档的id为book.getId(),需要添加文档的索引为books
IndexRequest request = new IndexRequest("books").id(book.getId().toString());
// 传入数据
request.source(json,XContentType.JSON);
client.index(request,RequestOptions.DEFAULT);
client.close();
}
添加文档使用的请求对象是IndexRequest,与创建索引使用的请求对象不同。
// 批量添加
@Test
void testCreateClientIndexByIkAddBatchData() throws IOException {
// 创建客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
// 进行添加操作,因为前面已经创建好了books索引
List<Book> bookList= bookMapper.selectList(null);
// BulkRequest的对象,可以将该对象理解为是一个保存request对象的容器,
// 将所有的请求都初始化好后,添加到BulkRequest对象中,再使用BulkRequest对象的bulk方法,一次性执行完毕
BulkRequest bulk = new BulkRequest();
for (Book book : bookList) {
// 把book对象数据转换为json数据,
String json = JSON.toJSONString(book);
// 指定添加的文档的id为book.getId(),需要添加文档的索引为books
IndexRequest request = new IndexRequest("books").id(book.getId().toString());
// 传入数据
request.source(json,XContentType.JSON);
// 把数据放进BulkRequest对象里面
bulk.add(request);
}
// 批量执行
client.bulk(bulk,RequestOptions.DEFAULT);
// 关闭客户端
client.close();
}
批量做时,先创建一个BulkRequest的对象,可以将该对象理解为是一个保存request对象的容器,将所有的请求都初始化好后,添加到BulkRequest对象中,再使用BulkRequest对象的bulk方法,一次性执行完毕。
@Test
//按id查询
void testGetById() throws IOException {
// 创建客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
// 根据id查询
GetRequest request = new GetRequest("books","1");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 获取查询到的数据中的source属性的数据
String json = response.getSourceAsString();
System.out.println(json);
client.close();
}
@Test
//按条件查询
void testSearch() throws IOException {
// 创建客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
//
SearchRequest request = new SearchRequest("books");
//创建条件查询对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 设置查询条件
searchSourceBuilder.query(QueryBuilders.termQuery("all", "spring"));
// 把查询条件放进请求中
request.source(searchSourceBuilder);
// 根据请求获取返回数据
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 获取返回数据里面的hits属性(获取的具体属性,可以看上面的postman操作)
SearchHits hits = response.getHits();
for (SearchHit hit : hits) {
String source = hit.getSourceAsString();
//把json数据转换为对象
Book book = JSON.parseObject(source, Book.class);
System.out.println(book);
}
}
前三种代码要么有代码侵入,要么有延迟。
而基于Binlog与mysql实现同步:既能保证数据同步的实时性又没有代入、侵入性。
实施步骤
优缺点: