当前位置: 首页 > news >正文

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的使用案例&#xff0c;包括结合MybatisPlus使用ES&#xff0c;如何保证MySQL和es的数据一致性&#xff0c;另外使用了RabbitMQ进行解耦&#xff0c;自定义了发消息的方法。 其他相关的Elasticsearch的文章列表如下&#xff1a; Elasticsear…...

什么是CSGO大行动,2023年CSGO大行动时间预测

什么是CSGO大行动&#xff0c;2023年CSGO大行动时间预测 什么是CSGO大行动&#xff0c;2023年CSGO大行动时间预测 那天群里在提大行动&#xff0c;不明所以的新同学在问&#xff0c;什么是大行动&#xff0c;是不是官方红锁大行动要来了&#xff1f;当然不是&#xff0c;别自己…...

Pycharm中终端不显示虚拟环境名解决方法

文章目录 一、问题说明&#xff1a;二、解决方法&#xff1a;三、重启Pycharm 一、问题说明&#xff1a; Pycharm中打开项目配置完需要的虚拟环境后&#xff0c;在Terminal&#xff08;终端&#xff09;中无法切换及显示当前需要运行代码的虚拟环境。 比如以下一种情况&#…...

某翻译网站webpack 全扣js逆向法

持续创作文章&#xff0c;只是为了更好的思考 如下内容&#xff0c;如果有写的不清楚&#xff0c;不对的地方&#xff0c;也请大家提醒我一下&#xff0c;谢谢&#xff01; 本次的目标是某道翻译网站&#xff0c;相信各位爷应该明白&#xff0c;这次逆向的整体做法还是把webpac…...

【C++】C++11 ——— 可变参数模板

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C学习 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【C】STL…...

ros2 UR10仿真包运行

前言 一个月前安装了一下这个包&#xff0c;但是有报错。现在换了一个强劲的电脑&#xff0c;内存64G &#xff0c;显存39G &#xff0c;终于跑起来了&#xff0c;没有报错。网页控制器可以控制RVIZ中的机器人旋转。 vituralBOX中3D加速要勾选&#xff0c;这样才能发挥独立显…...

flutter开发实战-安卓apk安装、卸载、启动实现

flutter开发实战-安卓apk安装、卸载、启动实现 在之前的文章中&#xff0c;实现了应用更新apk下载等操作&#xff0c;具体文档看下 这里记录一下使用shell来操作apk的安装、卸载、启动的操作。用到了库shell&#xff0c;Shell用于在Dart中或在代表其他用户执行系统管理任务的…...

AI绘画使用Stable Diffusion(SDXL)绘制玉雕风格的龙

一、引言 灵感来源于在逛 LibLib 时&#xff0c;看到的 Lib 原创者「熊叁gaikan」发布的「翠玉白菜 sdxl&#xff5c;玉雕风格」 的 Lora 模型。简直太好看了&#xff0c;一下子就被吸引了&#xff01; 科普下「翠玉白菜」&#xff1a; 翠玉白菜是由翠玉所琢碾出白菜形状的清…...

上位机在自动化中有何作用和优势?

今日话题 上位机在自动化中有何作用和优势&#xff1f; 自动化控制编程领域包括单片机、PLC、机器视觉和运动控制等方向。输入“777”&#xff0c;即刻获取关于上位机开发和数据可视化的专业学习资料&#xff0c;近年来&#xff0c;上位机编程逐渐兴起&#xff0c;正在逐步替…...

centos7 部署oracle完整教程(命令行)

centos7 部署oracle完整教程&#xff08;命令行&#xff09; 一. centos7安装oracle1.查看Swap分区空间&#xff08;不能小于2G&#xff09;2.修改CentOS系统标识 (由于Oracle默认不支持CentOS)2.1.删除CentOS Linux release 7.9.2009 (Core)&#xff08;快捷键dd&#xff09;&…...

数据库常用的几大范式NF

1NF 列不可再分 数据表中每个列都是不可再分的数据项。 例子&#xff1a;数据表中有一个属性名为“价格”的属性列。假如进一步将价格属性列划分为“会员价”和“普通价”就违反了列不可再分的原则。也就不再满足1NF 2NF “取消了非主属性对主键的部分函数依赖” 或者说 所有…...

诈骗分子投递“大闸蟹礼品卡”,快递公司如何使用技术手段提前安全预警?

目录 快递公司能不能提前识别&#xff1f; 如何通过技术有效识别 为即将带来的双十一提供安全预警 金秋十月&#xff0c;正是品尝螃蟹的季节。中秋国庆长假也免不了走亲访友&#xff0c;大闸蟹更是成了热门礼品。10月7日&#xff0c;演员孙艺洲发布微博称&#xff0c;“收到…...

基于晶体结构优化的BP神经网络(分类应用) - 附代码

基于晶体结构优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于晶体结构优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.晶体结构优化BP神经网络3.1 BP神经网络参数设置3.2 晶体结构算法应用 4.测试结果…...

模型的选择与调优(网格搜索与交叉验证)

1、为什么需要交叉验证 交叉验证目的&#xff1a;为了让被评估的模型更加准确可信 2、什么是交叉验证(cross validation) 交叉验证&#xff1a;将拿到的训练数据&#xff0c;分为训练和验证集。以下图为例&#xff1a;将数据分成4份&#xff0c;其中一份作为验证集。然后经过…...

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…...

正向代理与反向代理

正向代理 客户端想要直接与目标服务器连接&#xff0c;但是无法直接进行连接&#xff0c;就需要先去访问中间的代理服务器&#xff0c;让代理服务器代替客户端去访问目标服务器 反向代理 屏蔽掉服务器的信息&#xff0c;经常用在多台服务器的分布式部署上&#xff0c;像一些大型…...

idea热加载,JRebel 插件是目前最好用的热加载插件,它支持 IDEA Ultimate 旗舰版、Community 社区版

1.如何安装 ① 点击 https://plugins.jetbrains.com/plugin/4441-jrebel-and-xrebel/versions 地址&#xff0c;下载 2022.4.1 版本。如下图所示&#xff1a; ② 打开 [Preference -> Plugins] 菜单&#xff0c;点击「Install Plugin from Disk…」按钮&#xff0c;选择刚下…...

0基础学习PyFlink——Map和Reduce函数处理单词统计

在很多讲解大数据的案例中&#xff0c;往往都会以一个单词统计例子来抛砖引玉。本文也不免俗&#xff0c;例子来源于PyFlink的《Table API Tutorial》&#xff0c;我们会通过几种方式统计不同的单词出现的个数&#xff0c;从而达到循序渐进的学习效果。 常规方法 # input.py …...

在 Ubuntu 22.04安装配置 Ansible

一、按官网指引安装 我使用的ubuntu22.04版本&#xff0c;使用apt安装。官网指引如下&#xff1a; $ sudo apt-get install software-properties-common $ sudo apt-add-repository ppa:ansible/ansible $ sudo apt-get update $ sudo apt-get install ansible 由于内部网络…...

【大数据 - Doris 实践】数据表的基本使用(三):数据模型

数据表的基本使用&#xff08;三&#xff09;&#xff1a;数据模型 1.Aggregate 模型1.1 例一&#xff1a;导入数据聚合1.2 例二&#xff1a;保留明细数据1.3 例三&#xff1a;导入数据与已有数据聚合 2.Uniq 模型3.Duplicate 模型4.数据模型的选择建议5.聚合模型的局限性 Dori…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...