Elasticsearch使用——结合MybatisPlus使用ES es和MySQL数据一致性 结合RabbitMQ实现解耦

前言
本篇博客是一篇elasticsearch的使用案例,包括结合MybatisPlus使用ES,如何保证MySQL和es的数据一致性,另外使用了RabbitMQ进行解耦,自定义了发消息的方法。
其他相关的Elasticsearch的文章列表如下:
-
Elasticsearch的Docker版本的安装和参数设置 & 端口开放和浏览器访问
-
Elasticsearch的可视化Kibana工具安装 & IK分词器的安装和使用
-
Elasticsearch的springboot整合 & Kibana进行全查询和模糊查询
目录
- 前言
- 引出
- 结合MybatisPlus使用ES
- 1.引入依赖
- 2.进行配置
- 3.实体类上加入注解
- 4.创建操作的 Repository
- 5.初始化es中的数据
- 6.进行全查询以及分页
- 带条件分页查询
- es和mysql的数据一致性
- 延迟双删
- 加锁的方式
- 用rabbitmq进行解耦
- 配置yml文件
- rabbitmq的配置类
- callback回调方法
- 自定义发消息工具类
- 进行消息的发送
- 接收到消息,更新es
- 总结
引出
1.elasticsearch的使用案例,包括结合MybatisPlus使用ES;
2.如何保证MySQL和es的数据一致性;
3.使用了RabbitMQ进行解耦,自定义了发消息的方法。
结合MybatisPlus使用ES
1.引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- springboot 整合mybaits plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
2.进行配置
package com.tianju.es.config;import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;/*** 你也可以不继承 AbstractElasticsearchConfiguration 类,而将 ESConfig 写成一般的配置类的型式。* 不过继承 AbstractElasticsearchConfiguration 好处在于,它已经帮我们配置好了elasticsearchTemplate 直接使用。*/
@Configuration
public class ESConfig extends AbstractElasticsearchConfiguration {@Overridepublic RestHighLevelClient elasticsearchClient() {ClientConfiguration clientConfiguration =ClientConfiguration.builder().connectedTo("192.168.111.130:9200").build();return RestClients.create(clientConfiguration).rest();}
}
3.实体类上加入注解

package com.tianju.es.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.math.BigDecimal;/*** 产品,包括库存,价格信息*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("finance_sku")
@Document(indexName = "finance_sku")
public class FinanceSkuES {@TableId(value = "ID",type = IdType.AUTO)private Long id;@TableField("finance_sku_describe")@Field(index = true,analyzer = "ik_smart",searchAnalyzer = "ik_smart",type = FieldType.Text)private String detail; // 详情@TableField("finance_sku_price")private BigDecimal price;@TableField("finance_sku_stock")private Long stock;@TableField("finance_state")private Integer status;
}
参数解释
@Document(indexName = "books", shards = 1, replicas = 0)
@Data
public class Book {@Id@Field(type = FieldType.Integer)private Integer id;@Field(type = FieldType.Keyword)private String title;@Field(type = FieldType.Text)private String press;@Field(type = FieldType.Keyword)private String author;@Field(type = FieldType.Keyword,index=false)private BigDecimal price;@Field(type = FieldType.Text)private String description;
}
- @Document :注解会对实体中的所有属性建立索引;
indexName = “books” :表示创建一个名称为 “books” 的索引;
shards = 1 : 表示只使用一个分片;
replicas = 0 : 表示不使用复制备份;
index = false: 不能索引查询 - @Field(type = FieldType.Keyword) : 用以指定字段的数据类型。
4.创建操作的 Repository

从它的祖先们那里继承了大量的现成的方法,除此之外,它还可以按 spring data 的规则定义特定的方法。

package com.tianju.es.mapper;import com.tianju.es.entity.FinanceSkuES;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;/*** 操作es,类似于之前的mapper*/
@Repository
public interface SkuESMapper extends ElasticsearchRepository<FinanceSkuES, Long> {/*** 根据关键字进行 分词 分页查询 sku数据* @param detail 查询条件* @param pageable 分页* @return*/Page<FinanceSkuES> findFinanceSkuESByDetail(String detail, Pageable pageable);/*** 根据id进行删除* @param id*/void removeFinanceSkuESById(Long id);}
5.初始化es中的数据

运行的后台信息

查看es页面的信息,index management

6.进行全查询以及分页

进行全查询

{"content": [{"id": 1,"detail": "HUAWEI MateBook X Pro 2023 微绒典藏版 13代酷睿i7 32GB 2TB 14.2英寸3.1K原色全面屏 墨蓝","price": 13999.0,"stock": 50,"status": 1},{"id": 2,"detail": "HUAWEI Mate 60 Pro+ 16GB+1TB 宣白","price": 9999.0,"stock": 60,"status": 1},{"id": 3,"detail": "iPhone 15 Pro Max 超视网膜 XDR 显示屏","price": 9299.0,"stock": 46,"status": 1},{"id": 4,"detail": "MacBook Air Apple M2 芯片 8 核中央处理器 8 核图形处理器 8GB 统一内存 256GB 固态硬盘","price": 8999.0,"stock": 60,"status": 1}],"pageable": {"sort": {"empty": true,"sorted": false,"unsorted": true},"offset": 0,"pageSize": 4,"pageNumber": 0,"paged": true,"unpaged": false},"totalElements": 4,"last": true,"totalPages": 1,"number": 0,"size": 4,"sort": {"empty": true,"sorted": false,"unsorted": true},"numberOfElements": 4,"first": true,"empty": false
}
带条件分页查询

注意分页查询的page从0开始,尝试发现需要输入分词器分词后最小单元,比如hu不是最小单元,而HUAWEI是

分词器进行分词的结果

es和mysql的数据一致性
延迟双删

@Overridepublic FinanceSkuES updateByIddDoubleDelete(FinanceSkuES financeSkuES) {// 把es看做是缓存,如何保证es 和 mysql的 数据一致性?// 延迟双删的模式// 1.先删除缓存 esskuESMapper.deleteAll();// 2.更新数据库 mysqlupdateById(financeSkuES);// 3.延时操作try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}// 4.再次删除缓存 esskuESMapper.deleteAll();// 5.最后更新缓存 esskuESMapper.saveAll(list());Optional<FinanceSkuES> byId = skuESMapper.findById(financeSkuES.getId());log.debug("byId: "+byId);return byId.get();}
上面代码有不妥的地方,我这里是修改,结果一开始直接从es中全部删除,应该是根据id把修改的数据删除,然后把修改好的数据set进es里面
加锁的方式
感觉好像没什么用的样子,就是用了一下加锁

用rabbitmq进行解耦

配置yml文件

spring:main:allow-circular-references: truedatasource:driver-class-name: com.mysql.cj.jdbc.Driver### 本地的数据库url: jdbc:mysql://127.0.0.1:3306/consumer_finance_product?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=trueusername: rootpassword: 123# redis的相关配置redis:host: 119.3.162.127port: 6379database: 0password: Pet3927# rabbitmq相关rabbitmq:host: 192.168.111.130port: 5672username: adminpassword: 123virtual-host: /test# 生产者保证消息可靠性publisher-returns: truepublisher-confirm-type: correlated# 设置手动确认listener:simple:acknowledge-mode: manual
rabbitmq的配置类

将Java对象转换成json字符串传输

package com.tianju.es.rabbit;import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitConfig {public static final String ES_EXCHANGE = "es_exchange";public static final String ES_QUEUE = "es_queue";public static final String ES_KEY = "es_key";@Beanpublic DirectExchange directExchange(){return new DirectExchange(ES_EXCHANGE);}@Beanpublic Queue esQueue(){return new Queue(ES_QUEUE);}@Beanpublic Binding esQueueToDirectExchange(){return BindingBuilder.bind(esQueue()).to(directExchange()).with(ES_KEY);}/*** 将对象转换为json字符串* @return*/@Beanpublic MessageConverter messageConverter(){return new Jackson2JsonMessageConverter();}@Beanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setMessageConverter(messageConverter());// 修改转换器return rabbitTemplate;}}

callback回调方法
package com.tianju.es.rabbit;import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;/*** 生产者消息可靠性*/
// RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback
@Configuration
@Slf4j
public class CallbackConfigimplements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {@Autowiredprivate RabbitTemplate rabbitTemplate;// 初始化@PostConstructpublic void init(){rabbitTemplate.setConfirmCallback(this);rabbitTemplate.setReturnCallback(this);rabbitTemplate.setMandatory(true);}/*** 不管成功或者失败都会执行* @param correlationData correlation对象需要在 发送消息时候 给* @param ack true表示成功,false表示发送失败* @param cause 如果失败的话,会写失败原因;如果成功,返回为null*/@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {log.debug("ack是否成功:"+ack);log.debug("cause信息:"+cause);if (correlationData!=null){JSONObject jsonObject = JSON.parseObject(correlationData.getReturnedMessage().getBody());String exchange = correlationData.getReturnedMessage().getMessageProperties().getReceivedExchange();String routingKey = correlationData.getReturnedMessage().getMessageProperties().getReceivedRoutingKey();log.debug("消息体:"+jsonObject);log.debug("交换机:"+exchange);log.debug("路由key:"+routingKey);}if (ack){return;}// 失败了// 1、重试重试上限次数(默认值5)每重试一次时间间隔会增加// 2、把消息、交换机名称、路由键等相关的消息保存到数据库,有一个程序定时扫描相关的消息,然后重新发送消息。// 重发上限次数(默认值5)超过阈值会转人工处理// 2、把消息体、交换机名称、路由键等相关的消息保存到数据库,有一个程序定时扫描相关的消息,然后重新发送消息。// 重发上限次数(默认值5)超过阈值会转人工处理// 2.1需要把相关的信息存放到数据中,表字段:消息体、交换机名称、路由键、状态、次数// 2.2定时任务(单体:spring定时任务 分布式:XxL-job),发送消息}/*** 只有失败了才会执行* @param message* @param replyCode* @param replyText* @param exchange* @param routingKey*/@Overridepublic void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {// 2、把消息、交换机名称、路由键等相关的消息保存到数据库,有一个程序定时扫描相关的消息,然后重新发送消息。}
}
自定义发消息工具类

package com.tianju.common.util;import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;@Slf4j
public class RabbitUtil {/*** 延迟队列,发送消息,到达时间后进入死信队列中* @param rabbitTemplate 调用的rabbitTemplate* @param redisTemplate 用来在redis里面存token* @param msg 发送的消息* @param token 发送的token,用于保证幂等性* @param ttl 如果是延迟消费,则消息的过期时间,到达改时间后进入死信交换机,到死信队列中* @param exchange 交换机名字* @param routingKey 路由键名字* @param <T> 发送消息的实体类*/public static <T> void sendMsg(RabbitTemplate rabbitTemplate,StringRedisTemplate redisTemplate,T msg,String token,Integer ttl,String exchange,String routingKey) {log.debug("给交换机[{}]通过路由键[{}]发送消息 {},token为{}",exchange,routingKey,msg,token);MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {redisTemplate.opsForValue().set(token, token,5*60000);message.getMessageProperties().setMessageId(token);if (ttl!=null){message.getMessageProperties().setExpiration(ttl.toString());}return message;}};CorrelationData correlationData = new CorrelationData();// 消息体Message message = new Message(JSON.toJSONBytes(msg));// 交换机名称message.getMessageProperties().setReceivedExchange(exchange);// 路由键message.getMessageProperties().setReceivedRoutingKey(routingKey);correlationData.setReturnedMessage(message);// 发送MQ消息rabbitTemplate.convertAndSend(exchange, // 发给交换机routingKey, // 根据这个routingKey就会给到TTL队列,到时间成死信,发给死信交换机,到死信队列msg,messagePostProcessor,correlationData);}
}
进行消息的发送

接口
package com.tianju.es.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.tianju.es.entity.FinanceSkuES;public interface SkuService extends IService<FinanceSkuES> {/*** 延迟双删的方式,保证es 缓存 和 mysql数据库的数据一致性* @param financeSkuES 修改的数据* @return*/FinanceSkuES updateByIddDoubleDelete(FinanceSkuES financeSkuES);/*** 加锁的方式,不过感觉没啥用的样子* @param financeSkuES* @return*/FinanceSkuES updateByIdRedisLock(FinanceSkuES financeSkuES);/*** 通过rabbitmq进行解耦* @param financeSkuES* @return*/String updateByIdRabbitMQ(FinanceSkuES financeSkuES);
}
实现类
package com.tianju.es.service.impl;import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianju.common.util.RabbitUtil;
import com.tianju.es.entity.FinanceSkuES;
import com.tianju.es.mapper.SkuESMapper;
import com.tianju.es.mapper.SkuMybatisPlusMapper;
import com.tianju.es.rabbit.RabbitConfig;
import com.tianju.es.service.SkuService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.util.Collection;
import java.util.Optional;
import java.util.UUID;@Service
public class SkuServiceImpl extends ServiceImpl<SkuMybatisPlusMapper,FinanceSkuES>implements SkuService {@Autowiredprivate SkuESMapper skuESMapper;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic FinanceSkuES updateByIddDoubleDelete(FinanceSkuES financeSkuES) {// 把es看做是缓存,如何保证es 和 mysql的 数据一致性?// 延迟双删的模式// 1.先删除缓存 esskuESMapper.deleteAll();// 2.更新数据库 mysqlupdateById(financeSkuES);// 3.延时操作try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}// 4.再次删除缓存 esskuESMapper.deleteAll();// 5.最后更新缓存 esskuESMapper.saveAll(list());Optional<FinanceSkuES> byId = skuESMapper.findById(financeSkuES.getId());log.debug("byId: "+byId);return byId.get();}@Overridepublic FinanceSkuES updateByIdRedisLock(FinanceSkuES financeSkuES) {// 第二种方式加锁String uuid = UUID.randomUUID().toString();// 相当于setnx指令Boolean skuLock = stringRedisTemplate.opsForValue().setIfAbsent("skuLock", uuid);try {if (skuLock){ // 抢到了锁skuESMapper.deleteAll();updateById(financeSkuES);}}finally {if (uuid.equals(stringRedisTemplate.opsForValue().get("skuLock"))){stringRedisTemplate.delete("skuLock");}}skuESMapper.saveAll(list());Optional<FinanceSkuES> byId = skuESMapper.findById(financeSkuES.getId());log.debug("byId: "+byId);return byId.get();}@Overridepublic String updateByIdRabbitMQ(FinanceSkuES financeSkuES) {// 采用rabbitmq进行解耦updateById(financeSkuES);FinanceSkuES skuES = getById(financeSkuES.getId());String uuid = IdUtil.fastUUID();RabbitUtil.sendMsg(rabbitTemplate,stringRedisTemplate,skuES,uuid,null,RabbitConfig.ES_EXCHANGE,RabbitConfig.ES_KEY);return "已经发送消息:"+skuES;}
}

接收到消息,更新es
接收到消息进行es的更新,把原来的删除,把最新的set进去

package com.tianju.es.rabbit;import com.rabbitmq.client.Channel;
import com.tianju.es.entity.FinanceSkuES;
import com.tianju.es.mapper.SkuESMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.io.IOException;@Slf4j
@Component
public class ESListener {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate SkuESMapper skuESMapper;@RabbitListener(queues = RabbitConfig.ES_QUEUE)public void esUpdate(FinanceSkuES financeSkuES, Message message, Channel channel) {String messageId = message.getMessageProperties().getMessageId();log.debug("进行业务----> 监听到队列{}的消息,messageId为{}",financeSkuES,messageId);try {// 幂等性if (redisTemplate.delete(messageId)){// 根据id删除原有的 es 数据// 然后把新的数据set进来log.debug("处理es的业务,删除原有的,替换最新的");skuESMapper.removeFinanceSkuESById(financeSkuES.getId());skuESMapper.save(financeSkuES);}// 手动签收消息channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);}catch (Exception e){// 幂等性redisTemplate.opsForValue().set(messageId,messageId,5*60000);// 1、重试重试上限次数(默认值5) 每重试一次时间间隔会增加// 2、把消息、交换机名称、路由键等相关的消息保存到数据库,有一个程序定时扫描相关的消息,然后重新发送消息。// 重发上限次数(默认值5)超过阈值会转人工处理// 已知的消息,交换机,路由器,消息 message.getBody() 消息发送给的是监听的队列try {channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);} catch (IOException ex) {throw new RuntimeException(ex);}}}
}

后台打印的日志

总结
1.elasticsearch的使用案例,包括结合MybatisPlus使用ES;
2.如何保证MySQL和es的数据一致性;
3.使用了RabbitMQ进行解耦,自定义了发消息的方法。
相关文章:
Elasticsearch使用——结合MybatisPlus使用ES es和MySQL数据一致性 结合RabbitMQ实现解耦
前言 本篇博客是一篇elasticsearch的使用案例,包括结合MybatisPlus使用ES,如何保证MySQL和es的数据一致性,另外使用了RabbitMQ进行解耦,自定义了发消息的方法。 其他相关的Elasticsearch的文章列表如下: Elasticsear…...
什么是CSGO大行动,2023年CSGO大行动时间预测
什么是CSGO大行动,2023年CSGO大行动时间预测 什么是CSGO大行动,2023年CSGO大行动时间预测 那天群里在提大行动,不明所以的新同学在问,什么是大行动,是不是官方红锁大行动要来了?当然不是,别自己…...
Pycharm中终端不显示虚拟环境名解决方法
文章目录 一、问题说明:二、解决方法:三、重启Pycharm 一、问题说明: Pycharm中打开项目配置完需要的虚拟环境后,在Terminal(终端)中无法切换及显示当前需要运行代码的虚拟环境。 比如以下一种情况&#…...
某翻译网站webpack 全扣js逆向法
持续创作文章,只是为了更好的思考 如下内容,如果有写的不清楚,不对的地方,也请大家提醒我一下,谢谢! 本次的目标是某道翻译网站,相信各位爷应该明白,这次逆向的整体做法还是把webpac…...
【C++】C++11 ——— 可变参数模板
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C学习 🎯长路漫漫浩浩,万事皆有期待 上一篇博客:【C】STL…...
ros2 UR10仿真包运行
前言 一个月前安装了一下这个包,但是有报错。现在换了一个强劲的电脑,内存64G ,显存39G ,终于跑起来了,没有报错。网页控制器可以控制RVIZ中的机器人旋转。 vituralBOX中3D加速要勾选,这样才能发挥独立显…...
flutter开发实战-安卓apk安装、卸载、启动实现
flutter开发实战-安卓apk安装、卸载、启动实现 在之前的文章中,实现了应用更新apk下载等操作,具体文档看下 这里记录一下使用shell来操作apk的安装、卸载、启动的操作。用到了库shell,Shell用于在Dart中或在代表其他用户执行系统管理任务的…...
AI绘画使用Stable Diffusion(SDXL)绘制玉雕风格的龙
一、引言 灵感来源于在逛 LibLib 时,看到的 Lib 原创者「熊叁gaikan」发布的「翠玉白菜 sdxl|玉雕风格」 的 Lora 模型。简直太好看了,一下子就被吸引了! 科普下「翠玉白菜」: 翠玉白菜是由翠玉所琢碾出白菜形状的清…...
上位机在自动化中有何作用和优势?
今日话题 上位机在自动化中有何作用和优势? 自动化控制编程领域包括单片机、PLC、机器视觉和运动控制等方向。输入“777”,即刻获取关于上位机开发和数据可视化的专业学习资料,近年来,上位机编程逐渐兴起,正在逐步替…...
centos7 部署oracle完整教程(命令行)
centos7 部署oracle完整教程(命令行) 一. centos7安装oracle1.查看Swap分区空间(不能小于2G)2.修改CentOS系统标识 (由于Oracle默认不支持CentOS)2.1.删除CentOS Linux release 7.9.2009 (Core)(快捷键dd)&…...
数据库常用的几大范式NF
1NF 列不可再分 数据表中每个列都是不可再分的数据项。 例子:数据表中有一个属性名为“价格”的属性列。假如进一步将价格属性列划分为“会员价”和“普通价”就违反了列不可再分的原则。也就不再满足1NF 2NF “取消了非主属性对主键的部分函数依赖” 或者说 所有…...
诈骗分子投递“大闸蟹礼品卡”,快递公司如何使用技术手段提前安全预警?
目录 快递公司能不能提前识别? 如何通过技术有效识别 为即将带来的双十一提供安全预警 金秋十月,正是品尝螃蟹的季节。中秋国庆长假也免不了走亲访友,大闸蟹更是成了热门礼品。10月7日,演员孙艺洲发布微博称,“收到…...
基于晶体结构优化的BP神经网络(分类应用) - 附代码
基于晶体结构优化的BP神经网络(分类应用) - 附代码 文章目录 基于晶体结构优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.晶体结构优化BP神经网络3.1 BP神经网络参数设置3.2 晶体结构算法应用 4.测试结果…...
模型的选择与调优(网格搜索与交叉验证)
1、为什么需要交叉验证 交叉验证目的:为了让被评估的模型更加准确可信 2、什么是交叉验证(cross validation) 交叉验证:将拿到的训练数据,分为训练和验证集。以下图为例:将数据分成4份,其中一份作为验证集。然后经过…...
2023-10-17 mysql-配置主从-记录
摘要: 2023-10-17 mysql-配置主从-记录 参考: mysql配置主从_mysql主从配置_Tyler唐的博客-CSDN博客 master: 环境: 192.168.74.128mysql8/etc/my.cnf.d/mysql-server.cnf # # This group are read by MySQL server. # Use it for options that only the server (but not cli…...
正向代理与反向代理
正向代理 客户端想要直接与目标服务器连接,但是无法直接进行连接,就需要先去访问中间的代理服务器,让代理服务器代替客户端去访问目标服务器 反向代理 屏蔽掉服务器的信息,经常用在多台服务器的分布式部署上,像一些大型…...
idea热加载,JRebel 插件是目前最好用的热加载插件,它支持 IDEA Ultimate 旗舰版、Community 社区版
1.如何安装 ① 点击 https://plugins.jetbrains.com/plugin/4441-jrebel-and-xrebel/versions 地址,下载 2022.4.1 版本。如下图所示: ② 打开 [Preference -> Plugins] 菜单,点击「Install Plugin from Disk…」按钮,选择刚下…...
0基础学习PyFlink——Map和Reduce函数处理单词统计
在很多讲解大数据的案例中,往往都会以一个单词统计例子来抛砖引玉。本文也不免俗,例子来源于PyFlink的《Table API Tutorial》,我们会通过几种方式统计不同的单词出现的个数,从而达到循序渐进的学习效果。 常规方法 # input.py …...
在 Ubuntu 22.04安装配置 Ansible
一、按官网指引安装 我使用的ubuntu22.04版本,使用apt安装。官网指引如下: $ sudo apt-get install software-properties-common $ sudo apt-add-repository ppa:ansible/ansible $ sudo apt-get update $ sudo apt-get install ansible 由于内部网络…...
【大数据 - Doris 实践】数据表的基本使用(三):数据模型
数据表的基本使用(三):数据模型 1.Aggregate 模型1.1 例一:导入数据聚合1.2 例二:保留明细数据1.3 例三:导入数据与已有数据聚合 2.Uniq 模型3.Duplicate 模型4.数据模型的选择建议5.聚合模型的局限性 Dori…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
