003 Springboot操作RabbitMQ

news2024/10/11 6:27:34

Springboot整合RabbitMQ

文章目录

    • Springboot整合RabbitMQ
      • 1.pom依赖
      • 2.yml配置
      • 3.配置队列、交换机
        • 方式一:直接通过配置类配置bean
        • 方式二:消息监听通过注解配置
      • 4.编写消息监听发送测试
      • 5.其他类型交换机配置
        • 1.FanoutExchange
        • 2.TopicExchange
        • 3.HeadersExchange
      • 6.延迟消息处理(TTL)
        • 方式一:ttl配置
        • 方式二:消息发送设置
      • 7.死信队列

1.pom依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>

    </dependencies>

2.yml配置

#配置使用的配置文件
spring:
  #配置rabbitmq
  rabbitmq:
    host: 47.122.26.28 #主机地址
    port: 5672  #端口号
    username: xxx #用户名
    password: xxx #密码
    virtual-host: my_vhost  #虚拟主机地址
    #开启消息送达提示
    publisher-returns: true
    # springboot.rabbitmq.publisher-confirm 新版本已被弃用,现在使用 spring.rabbitmq.publisher-confirm-type = correlated 实现相同效果
    publisher-confirm-type: correlated
    listener:  #消息监听配置
      type: simple
      simple:
        acknowledge-mode: manual #manual手动确认消息  auto没有异常时 进行自动确认 (异常类型 消息重新入队)
        prefetch: 1 #限制每次发送一条数据。
        concurrency: 3 #同一个队列启动几个消费者
        max-concurrency: 3 #启动消费者最大数量
        #重试策略相关配置
        retry:
          # 开启消费者(程序出现异常)重试机制,默认开启并一直重试
          enabled: true
          # 最大重试次数
          max-attempts: 5
          # 重试间隔时间(毫秒)
          initial-interval: 3000


server:
  port: 18082
  address: 127.0.0.1
  servlet:
    context-path: /

3.配置队列、交换机

方式一:直接通过配置类配置bean

推送消息时不存在创建队列和交换机

/**
 * direct模式声明配置
 */
@Configuration
public class RabbitDirectConfig {

    public static final String EXCHANGE_NAME="direct-exchange";
    public static final String QUEUE_NAME="direct-queue";
    public static final String BINDING_KEY="change:direct";

    /**
     * 声明直连交换机
     * name:交换机的名称
     * durable 队列是否持久化
     * autoDelete:是否自动删除,(当该交换机上绑定的最后一个队列解除绑定后,该交换机自动删除)
     * argument:其他一些参数
     */
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(EXCHANGE_NAME,false,false,null);
    }

    /**
     * 声明队列
     *  queue 队列的名称
     *  durable 队列是否持久化
     *  exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
     *  autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
     *  arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
     */
    @Bean
    public Queue directQueue() {
        return new Queue(QUEUE_NAME,false,false,false,null);
    }

    /**
     * 交换机队列绑定
     */
    @Bean
    public Binding springExchangeBindSpringQueue() {
        return BindingBuilder.bind(directQueue()).to(directExchange()).with(BINDING_KEY);
    }

}

方式二:消息监听通过注解配置

启动时创建队列和交换机

    @RabbitListener(
            bindings = @QueueBinding(
                value = @Queue(value = "direct1-queue",durable = "true"),
                exchange = @Exchange(value = "direct1-exchange",type = ExchangeTypes.DIRECT,durable = "true"),
                key = "change1:direct"))

注意:rabbitmq同名的队列只能创建一个,创建多个会报错,推送消息时需确保队列和交换机已存在,

方式一队列和交换机在第一次推送消息时才会自动创建队列和交换机,方式二注解在启动时就会创建

4.编写消息监听发送测试

监听

@Slf4j
@Component
public class RabbitMQListener {


    @RabbitListener(queues = "direct-queue")
    @RabbitHandler
    public void bootMsg(Channel channel, Message message){
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" direct 消费者:'" + message1 + "'");
        //手动确认该消息
        try {
            //消息确认,根据消息序号(false只确认当前一个消息收到,true确认所有比当前序号小的消息(成功消费,消息从队列中删除 ))
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (IOException e) {
            log.error("执行异常",e);
             // 拒绝消息并重新入队
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); 
        }
    }

    
    @RabbitListener(
            bindings = @QueueBinding(
                value = @Queue(value = "direct1-queue",durable = "true"),
                exchange = @Exchange(value = "direct1-exchange",type = ExchangeTypes.DIRECT,durable = "true"),
                key = "change1:direct"))
    @RabbitHandler
    public void bootMsg1(Channel channel, Message message){
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" direct 消费者:'" + message1 + "'");
        //手动确认该消息
        try {
            //消息确认,根据消息序号(false只确认当前一个消息收到,true确认所有比当前序号小的消息(成功消费,消息从队列中删除 ))
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (IOException e) {
            log.error("执行异常",e);
             // 拒绝消息并重新入队
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }
    }

}

测试

@Slf4j
@SpringBootTest(classes = RabbitProviderApplication.class)
public class RabbitTest {

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Test
    public void directProvider(){
        String message = "direct模式消息推送。。。。。";
        /**
         * 参数分别为,交换机,路由key,消息体
         */
        amqpTemplate.convertAndSend("direct-exchange","change:direct",message);
        System.out.println(" 消息发送 :'" +message + "'");
    }


    @Test
    public void directProvider1(){
        String message = "direct模式消息推送1。。。。。";
        /**
         * 参数分别为,交换机,路由key,消息体
         */
        amqpTemplate.convertAndSend("direct1-exchange","change1:direct",message);
        System.out.println(" 消息发送1 :'" +message + "'");
    }

}

在这里插入图片描述

5.其他类型交换机配置

1.FanoutExchange
/**
 * fanout模式声明配置
 */
@Configuration
public class RabbitFanoutConfig {

    public static final String EXCHANGE_NAME="fanout-exchange";
    public static final String QUEUE_NAME1="fanout-queue1";
    public static final String QUEUE_NAME2="fanout-queue2";

    /**
     * 声明交换机
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(EXCHANGE_NAME,false,false,null);
    }

    /**
     * 声明队列
     */
    @Bean
    public Queue fanoutQueue1() {
        return new Queue(QUEUE_NAME1,false,false,false,null);
    }
    @Bean
    public Queue fanoutQueue2() {
        return new Queue(QUEUE_NAME2,false,false,false,null);
    }

    /**
     * 交换机队列绑定
     */
    @Bean
    public Binding springExchangeBindQueue1() {
        return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
    }

    /**
     * 交换机队列绑定
     */
    @Bean
    public Binding springExchangeBindQueue2() {
        return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
    }

}

监听

    @RabbitListener(queues = "fanout-queue1")
    public void fanoutMsg1(Channel channel, Message message) {
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" fanout-queue1 消费者:'" + message1 + "'");
    }

    @RabbitListener(queues = "fanout-queue2")
    public void fanoutMsg2(Channel channel, Message message) {
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" fanout-queue2 消费者:'" + message1 + "'");
    }

测试

    @Test
    public void fanoutProvider(){
        String message = "fanout模式消息推送。。。。。";
        amqpTemplate.convertAndSend("fanout-exchange", "",message);
        System.out.println(" 消息发送 :'" +message + "'");
    }

在这里插入图片描述

2.TopicExchange
/**
 * topic模式声明配置
 */
@Configuration
public class RabbitTopicConfig {

    public static final String EXCHANGE_NAME="topic-exchange";
    public static final String QUEUE_NAME="topic-queue";

    public static final String BINDING_KEY="*.orange.#";

    /**
     * 声明交换机
     */
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(EXCHANGE_NAME,false,false,null);
    }

    /**
     * 声明队列
     */
    @Bean
    public Queue topicQueue() {
        return new Queue(QUEUE_NAME,false,false,false,null);
    }


    /**
     * 交换机队列绑定
     */
    @Bean
    public Binding topicExchangeBindQueue() {
        return BindingBuilder.bind(topicQueue()).to(topicExchange()).with(BINDING_KEY);
    }

}

    @RabbitListener(queues = "topic-queue")
    public void topicMsg2(Channel channel, Message message) {
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" topic-queue2 消费者:'" + message1 + "'");
    }

测试

    @Test
    public void topicProvider(){
        String message1 = "topic test模式消息推送。。。。。";
        String message2 = "topic test.aaa模式消息推送。。。。。";
        amqpTemplate.convertAndSend("topic-exchange", "com.orange.test",message1);
        amqpTemplate.convertAndSend("topic-exchange", "com.orange.test.aaa",message2);
        System.out.println(" 消息发送");
    }

在这里插入图片描述

3.HeadersExchange
/**
 * headers模式声明配置
 * 与路由key无关,只需要消息的头参数匹配即可
 * x-match参数代表是全部匹配还是部分匹配
 */
@Configuration
public class RabbitHeadersConfig {

    public static final String EXCHANGE_NAME="headers-exchange";
    public static final String QUEUE_NAME="headers-queue";
    public static final String QUEUE_NAME1="headers-queue1";

    /**
     * 声明交换机
     */
    @Bean
    public HeadersExchange headersExchange() {
        return new HeadersExchange(EXCHANGE_NAME,false,false,null);
    }

    /**
     * 声明队列
     */
    @Bean
    public Queue headersQueue() {
        return new Queue(QUEUE_NAME,false,false,false,null);
    }
    @Bean
    public Queue headersQueue2() {
        return new Queue(QUEUE_NAME1,false,false,false,null);
    }

    /**
     * 交换机队列绑定(任意匹配)
     * whereAny 等同于x-match = any
     */
    @Bean
    public Binding headersExchangeBindSpringQueue() {
        HashMap<String, Object> header = new HashMap<>();
        header.put("test", "111");
        header.put("test1", "222");
        return BindingBuilder.bind(headersQueue()).to(headersExchange()).whereAny(header).match();
    }

    /**
     * 交换机队列绑定(全部匹配)
     * whereAny 等同于x-match = all
     */
    @Bean
    public Binding headersExchangeBindSpringQueue1() {
        HashMap<String, Object> header = new HashMap<>();
        header.put("test", "111");
        header.put("test1", "222");
        return BindingBuilder.bind(headersQueue2()).to(headersExchange()).whereAll(header).match();
    }

}

发送测试

  @Test
    public void headerProvider(){
        String param = "headers 模式消息推送。。。。。";
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType("text/plain");
        messageProperties.setContentEncoding("utf-8");
        messageProperties.setHeader("test","111");
        Message message = new Message(param.getBytes(), messageProperties);
        amqpTemplate.convertAndSend("headers-exchange", null,message);
        System.out.println(" 消息发送");
    }

在这里插入图片描述

队列queue任意匹配有数据,queue1全部匹配无数据

headers-queue

在这里插入图片描述

headers-queue1

在这里插入图片描述

消息监听

    @RabbitListener(queues = "headers-queue")
    public void headersMsg2(Channel channel, Message message) {
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" headers-queue 消费者:'" + message1 + "'");
    }


    @RabbitListener(queues = "headers-queue1")
    public void headers1Msg2(Channel channel, Message message) {
        String message1 = new String(message.getBody(), StandardCharsets.UTF_8);
        System.out.println(" headers-queue1 消费者:'" + message1 + "'");
    }

在这里插入图片描述

6.延迟消息处理(TTL)

  • 第一种是使用普通队列和死信队列来模拟实现延迟的效果。将消息放入一个没有被监听的队列上,设置TTL(一条消息的最大存活时间)为延迟的时间,时间到了没有被消费,直接成为死信,进入死信队列。后监听私信队列来消息消费

  • 第二种是使用rabbitmq官方提供的delayed插件来真正实现延迟队列。

方式一:ttl配置

超时自动删除

/**
 * rabbitmq的ttl延迟过期时间配置
 */
@Configuration
public class RabbitMQTTLConfig {

    /**
     * 声明交换机
     * @return
     */
    @Bean
    public DirectExchange ttlDirectExchange(){
        return new DirectExchange("ttl-direct-exchange");
    }

    /**
     * 声明队列
     * @return
     */
    @Bean
    public Queue ttlQueue(){
        //设置参数
        Map<String,Object> args = new HashMap<>();
        //设置ttl过期时间,需设置int值
        args.put("x-message-ttl",5000);
        return new Queue("ttl-direct-queue",true,false,false,args);
    }

    /**
     * 绑定队列
     * @return
     */
    @Bean
    public Binding ttlBingQueue(){
        return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("direct:ttl:key");
    }

}

测试

    @Test
    public void ttlSendMessageTest(){
        String exchange = "ttl-direct-exchange";
        String routingKey = "direct:ttl:key";
        String msg = UUID.randomUUID().toString();
        //发送并设置
        amqpTemplate.convertAndSend(exchange,routingKey,msg);
        System.out.println("消息发送成功====="+msg);
    }

在这里插入图片描述

方式二:消息发送设置

注释掉x-message-ttl参数,使用普通队列,发送消息时设置过期时间

    @Test
    public void ttlSendMessageTest(){
        String exchange = "ttl-direct-exchange";
        String routingKey = "direct:ttl:key";
        String msg = UUID.randomUUID().toString();
        //设置过期时间
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("5000");
                message.getMessageProperties().setContentEncoding("UTF-8");
                return message;
            }
        };
        //发送并设置
        amqpTemplate.convertAndSend(exchange,routingKey,msg,messagePostProcessor);
        System.out.println("消息发送成功====="+msg);
    }

在这里插入图片描述

注意:如果项目中即使用了ttl配置过期时间,有设置了消息过期时间,则执行时以最小的时间为准,ttl过期队列的消息过期会写到死信,而设置方式的普通队列则不会自动写到死信队列

7.死信队列

死信的情况:消息被拒绝,消息过期,队列达到最大长度

死信队列声明

@Configuration
public class RabbitMQDLXConfig {

    /**
     * 声明死信交换机
     * @return
     */
    @Bean
    public DirectExchange dlxDirectExchange(){
        return new DirectExchange("dlx-direct-exchange");
    }

    /**
     * 声明死信队列
     * @return
     */
    @Bean
    public Queue dlxQueue(){ ;
        return new Queue("dlx-direct-queue",true);
    }

    /**
     * 绑定队列
     * @return
     */
    @Bean
    public Binding dlxBingQueue(){
        return BindingBuilder.bind(dlxQueue()).to(dlxDirectExchange()).with("direct:dlx:key");
    }

}

过期推送到死信设置

   /**
     * 声明ttl队列
     * @return
     */
    @Bean
    public Queue ttlQueue(){
        //设置参数
        Map<String,Object> args = new HashMap<>();
        //设置ttl过期时间,需设置int值
        args.put("x-message-ttl",5000);
        args.put("x-max-length",5);//最大长度
        //消息过期死信队列入队配置
        args.put("x-dead-letter-exchange","dlx-direct-exchange");//设置死信交换机
        args.put("x-dead-letter-routing-key","direct:dlx:key");//死信路由key,fanout模式不需要设置路由key
        return new Queue("ttl-direct-queue",true,false,false,args);
    }

注意:队列参数修改后,不会重新创建覆盖而是会报错,需要手动删除重新创建,生产环境中则可以通过重新创建一个队列,进行转移

测试

在这里插入图片描述

消息过期进死信队列
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2204425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6. 继承、重写、super、final

文章目录 一、重新定义需求二、继承1. 继续分析2. 概念3. 代码① 父类② 子类③ 测试结果 4. 饿狼传说之多层继承① 概念② 代码 5. 多继承 三、方法的重写1. 情境2. 代码① 吃什么② 怎么叫(Override重写) 3. 小结 四、super1. 啃老2. 啃老啃到底 五、final1. 用途及特征2. 举…

【Python爬虫系列】_025.关于互斥锁(Lock)

课 程 推 荐我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)教程合集 👈👈

Vue使用@别名替换后端ip地址

1. 安装 types/node types/node 包允许您在TypeScript项目中使用Node.js的核心模块和API&#xff0c;并提供了对它们的类型检查和智能提示的支持。 npm install types/node --save-dev 比如安装之后&#xff0c;就可以导入nodejs的 path模块&#xff0c;在下面代码 import path…

【SEO】什么是SEO?

什么是SEO&#xff08;搜索引擎优化&#xff09;&#xff1f;为什么SEO对于⼀个⽹站⾄关重要&#xff1f; SEO 全称是搜索引擎优化&#xff08;Search Engine Optimization&#xff09; 因为我们目前开发的网址&#xff0c;需要人看到&#xff0c;除了通过宣传营销的方式展现…

C++ | Leetcode C++题解之第468题验证IP地址

题目&#xff1a; 题解&#xff1a; class Solution { public:string validIPAddress(string queryIP) {if (queryIP.find(.) ! string::npos) {// IPv4int last -1;for (int i 0; i < 4; i) {int cur (i 3 ? queryIP.size() : queryIP.find(., last 1));if (cur st…

ctf.bugku-eval

题目来源&#xff1a;eval - Bugku CTF 访问页面&#xff0c; 代码解释 <?phpinclude "flag.php"; //包含"flag.php"文件$a $_REQUEST[hello]; //从请求参数hello中获取值并赋给变量$a。 eval( "var_dump($a);"); //…

SQLAlchemy模型定义:映射数据库表到Python类

SQLAlchemy是一个流行的Python SQL工具包和对象关系映射&#xff08;ORM&#xff09;框架&#xff0c;它提供了一个高层的ORM以及底层的SQL表达式语言。使用SQLAlchemy&#xff0c;开发者可以以面向对象的方式来操作数据库&#xff0c;而不必编写复杂的SQL语句。本文将详细介绍…

【spring ai】java 实现RAG检索增强,超快速入门

rag 需求产生的背景介绍&#xff1a; 在使用大模型时&#xff0c;一个常见的问题是模型会产生幻觉&#xff08;即生成的内容与事实不符&#xff09;&#xff0c;同时由于缺乏企业内部数据的支持&#xff0c;导致其回答往往不够精准和具体&#xff0c;偏向于泛泛而谈。这些问题…

如何利用wsl-Ubuntu里conda用来给Windows的PyCharm开发

前提&#xff1a;咱们在wsl-Ubuntu上&#xff0c;有conda的虚拟环境 咱们直接打开PyCharm,打开Settings 更换Python Interpreter即可 当然一开始可能没有下面的选项&#xff0c;需要我们点击右边的Add Interpreter 这里选择wsl 点击next 将这两步进行修改 可以看出来&#xff0…

计算机视觉之OpenCV vs YOLO

好多开发者希望搞明白OpenCV 和YOLO区别&#xff0c;实际上&#xff0c;二者在计算机视觉领域都有广泛应用&#xff0c;但它们有很大的不同。 一、OpenCV 概述 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它…

继承--C++

文章目录 一、继承的概念及定义1、继承的概念 二、继承定义1、定义格式2、继承基类成员访问方式的变化3、继承类模板 三、基类和派生类间的转换1、继承中的作用域2、隐藏规则&#xff1a; 四、派生类的默认成员函数1、4个常见默认成员函数2、实现⼀个不能被继承的类 五、继承与…

(八)Proteus仿真STM32单片机GPIO驱动数码管

1&#xff0c;参考上篇&#xff0c;将LED点阵屏更换成数码管如下图 2&#xff0c;修改驱动函数&#xff0c;数组seg[14]前10个是0-9数字的编码&#xff0c;后四个是空格&#xff0c;点&#xff0c;横线&#xff0c;下划线 char seg_decode(char num)//数字解码 {const char se…

【华为欧拉】国产OpenEuler服务器系统安装以及图形界面

openEuler下载 | openEuler ISO镜像 | openEuler社区官网 下载安装iso 本次选择4G的社区版本 安装&#xff0c;复制到光盘&#xff0c;光盘引导安装。虚拟机安装&#xff0c;准备好iso文件引用&#xff0c;指定好安装源&#xff0c;安装界面和centOS基本一样。选择最小安装就…

JVM系列(二) -类的加载过程介绍

一、背景介绍 我们知道 Java 是先通过编译器将.java类文件转成.class字节码文件&#xff0c;然后再通过虚拟机将.class字节码文件加载到内存中来实现应用程序的运行。 那么虚拟机是什么时候加载class文件&#xff1f;如何加载class文件&#xff1f;class文件进入到虚拟机后发…

彻底理解TypeScript函数语法

目录 参数类型基本声明默认参数剩余参数可选只读匿名函数回调函数 返回值类型函数类型表达式调用签名构造签名 函数的重载this可推导的编译选项this类型内置工具 函数是JavaScript非常重要的组成部分&#xff0c;TypeScript中也是如此&#xff0c;TypeScript 提供了强大的类型系…

网关在不同行业自动化生产线的应用

网关在不同行业自动化生产线的应用&#xff0c;展示了其作为信息与物理世界交汇点的广泛影响力&#xff0c;尤其在推动行业智能化、自动化方面发挥了不可估量的作用。以下是网关技术在污水处理、智慧农业、智慧工厂、电力改造及自动化控制等领域的深入应用剖析。 1. 污水处理 …

盒子模型的简单运用

1.块内元素与行内元素 HTML_code <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</titl…

Scala面试题大全~基础题(15题)

1&#xff1a;Scala是什么? Scala是一种多范式的编程语言&#xff0c;它结合了面向对象编程和函数式编程的特性&#xff0c;它支持面向对象、函数式和命令式编程方法。Scala运行在Java虚拟机&#xff08;JVM&#xff09;上&#xff0c;这意味着它可以与Java代码无缝集成。它还…

【多版本并发控制(MVCC)】

并发事务问题&#xff1a; MySQL隔离级别-未提交读&#xff0c;提交读&#xff0c;可重复读&#xff0c;序列化 隔离级别对于并发事务的解决情况 隔离级别脏读不可重复读幻读未提交读不可不可不可读已提交可不可不可可重复读 &#xff08;默认&#xff09;可可不可串行化&…

现货黄金价格走势图策略分析 先看“势”

在现货黄金投资市场&#xff0c;对金价走势图的趋势进行分析&#xff0c;是投资者做出明智决策的关键步骤。通过有效的趋势分析&#xff0c;投资者可以更好地预测市场的走向&#xff0c;从而制定相应的交易策略。本文将详细介绍如何分析金价的趋势&#xff0c;并探讨这种分析方…