Spring Boot 整合Redis实现消息队列

一、简介

  本篇文章主要来讲Spring Boot 整合Redis实现消息队列,实现redis用作消息队列有多种方式,比如:

  • 基于List rpush+lpop lpush+rpop
  • 基于List rpush+blpop lpush+brpop (阻塞式获取消息)
  • 基于Sorted Set 的优先级队列
  • Redis Stream (Redis5.0版本开始)
  • Pub/Sub 机制

  不过这里讲的是Pub/Sub 机制的,这种方式优缺点大致如下:

优点:

  • 一个消息可以发布到多个消费者
  • 消费者可以同时订阅多个信道,因此可以接收多种消息(处理时先根据信道判断)
  • 消息即时发送,消费者会自动接收到信道发布的消息

缺点:

  • 消息发布时,如果客户端不在线,则消息丢失
  • 消费者处理消息时出现了大量消息积压,则可能会断开通道,导致消息丢失
  • 消费者接收消息的时间不一定是一致的,可能会有差异(业务处理需要判重)

二、maven依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.alian</groupId>
    <artifactId>redis-message-queue</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis-message-queue</name>
    <description>redis-message-queue</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <project.package.directory>target</project.package.directory>
        <java.version>1.8</java.version>
        <!--com.fasterxml.jackson 版本-->
        <jackson.version>2.9.10</jackson.version>
        <!--lombok 版本-->
        <lombok.version>1.16.14</lombok.version>
        <!--阿里巴巴fastjson 版本-->
        <fastjson.version>1.2.68</fastjson.version>
        <!--junit 版本-->
        <junit.version>4.12</junit.version>
    </properties>

    <dependencies>

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

        <!--redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!--用于序列化-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!--java 8时间序列化-->
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!--JSON-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <!--日志输出-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

三、编码实现

3.1、配置文件

application.properties

# 端口
server.port=8090
# 上下文路径
server.servlet.context-path=/redisQueue

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
#spring.redis.host=192.168.0.193
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=20
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=10
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=20000
# 读时间(毫秒)
spring.redis.timeout=10000
# 连接超时时间(毫秒)
spring.redis.connect-timeout=10000

3.2、配置类

RedisConfiguration.java

package com.alian.queue.config;

import com.alian.queue.listener.RedisMessageListenerListener;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@Configuration
@EnableCaching
public class RedisConfiguration {

    /**
     * redis配置
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 实例化redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // key采用String的序列化
        redisTemplate.setKeySerializer(keySerializer());
        // value采用jackson序列化
        redisTemplate.setValueSerializer(valueSerializer());
        // Hash key采用String的序列化
        redisTemplate.setHashKeySerializer(keySerializer());
        // Hash value采用jackson序列化
        redisTemplate.setHashValueSerializer(valueSerializer());
        //执行函数,初始化RedisTemplate
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public ChannelTopic channelTopic() {
        return new ChannelTopic("TOPIC_USER");
    }

    @Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, RedisMessageListenerListener redisMessageListenerListener) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        redisMessageListenerContainer.addMessageListener(redisMessageListenerListener, channelTopic());
        return redisMessageListenerContainer;
    }

    /**
     * key类型采用String序列化
     *
     * @return
     */
    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    /**
     * value采用JSON序列化
     *
     * @return
     */
    private RedisSerializer<Object> valueSerializer() {
        //设置jackson序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        //设置序列化对象
        jackson2JsonRedisSerializer.setObjectMapper(getMapper());
        return jackson2JsonRedisSerializer;
    }


    /**
     * 使用com.fasterxml.jackson.databind.ObjectMapper
     * 对数据进行处理包括java8里的时间
     *
     * @return
     */
    private ObjectMapper getMapper() {
        ObjectMapper mapper = new ObjectMapper();
        //设置可见性
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //默认键入对象
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //设置Java 8 时间序列化
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        //禁用把时间转为时间戳
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.registerModule(timeModule);
        return mapper;
    }

}

  这里就是在整合redis的前提下(如果不懂可以参考:SpringBoot整合redis(redis支持单节点和集群)),然后新增了如下配置:

    @Bean
    public ChannelTopic channelTopic() {
        return new ChannelTopic("TOPIC_USER");
    }

    @Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, channelTopic());
        return redisMessageListenerContainer;
    }

  RedisMessageListenerContainer 是为Redis消息侦听器 MessageListener 提供异步行为的容器。处理侦听、转换和消息分派的低级别详细信息。它与低级别Redis(每个订阅一个连接)相反,容器只使用一个连接,该连接对所有注册的侦听器都是“多路复用”的,消息调度是通过任务执行器完成的。容器以惰性方式使用连接(仅当至少配置了一个侦听器时才使用连接),同时添加和删除侦听器具有未定义的结果,强烈建议对这些方法进行相应的同步/排序。

  • 创建一个Redis消息监听器容器
  • 设置 Redis 连接工厂
  • 将监听器和管道名想绑定
  • 配置管道名和推送消息时的管道名要一致,不然监听器监听不到消息

我这里使用的是主题订阅:ChannelTopic,你也可以使用模式匹配:PatternTopic,从而匹配多个信道。

3.3、监听器

package com.alian.queue.listener;

import com.alian.queue.service.ConsumeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class RedisMessageListenerListener implements MessageListener {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ConsumeService consumeService;

    /**
     * 消息处理
     *
     * @param message
     * @param pattern
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channel = new String(pattern);
        log.info("onMessage --> 消息通道是:{}", channel);
        try {
            RedisSerializer<?> valueSerializer = redisTemplate.getValueSerializer();
            Object deserialize = valueSerializer.deserialize(message.getBody());
            log.info("反序列化的结果:{}", deserialize);
            if (deserialize == null) {
                return;
            }
            String md5DigestAsHex = DigestUtils.md5DigestAsHex(deserialize.toString().getBytes(StandardCharsets.UTF_8));
            log.info("计算得到的key: {}", md5DigestAsHex);
            Boolean result = redisTemplate.opsForValue().setIfAbsent(md5DigestAsHex, "1", 20, TimeUnit.SECONDS);
            if (Boolean.TRUE.equals(result)) {
                // redis消息进行处理
                consumeService.processMessage(channel, deserialize.toString());
                log.info("处理redis消息完成");
            } else {
                log.info("其他服务处理中");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("处理redis消息异常:", e);
        }

    }

}

  我们实现MessageListener 接口,就可以通过onMessage()方法接收到消息了,该方法有两个参数:

  • 参数 message getBody() 方法以二进制形式获取消息体, getChannel() 以二进制形式获取消息通道
  • 参数 pattern 二进制形式的消息通道(实际和 message.getChannel() 返回值相同)

3.4、消费服务

ConsumeService.java

package com.alian.queue.service;

import com.alian.queue.dto.UserDto;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class ConsumeService {

    public void processMessage(String channel,String message) {
        // 可以根据channel再继续映射到不同的实现
        UserDto userDto = JSONObject.parseObject(message, UserDto.class);
        log.info("接收的结果:{}", userDto);
        // 做业务...
        // 还可以分布式锁幂等处理
    }
}

  这个就是消息处理了,可以根据channel再继续映射到不同的实现,然后业务也可以继续使用分布式锁进行逻辑判断处理,这里就不具体去操作了。

3.5、实体

UserDto.java

package com.alian.queue.dto;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public class UserDto implements Serializable {

    private String id;//员工ID

    private String name;//员工姓名

    private int age;//员工年龄

    private String department;//部门

    private double salary;//工资

    private LocalDateTime hireDate;//入职时间

    public UserDto() {

    }

    /*
     *  简单的构造方法用于测试
     */
    public UserDto(String id, String name, int age, String department, double salary, LocalDateTime hireDate) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.department = department;
        this.salary = salary;
        this.hireDate = hireDate;
    }
}

四、验证

  我们把上面的服务启动多个实例,这里就用端口区别,分别是 8090 8091,然后发送消息到 Redis,下面使我们的测试类:

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class RedisMessageQueueTest {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    public void sendMessage() {
        UserDto userDto = new UserDto("BAT002", "包雅馨", 25, "财务部", 8800.0, LocalDateTime.of(2016, 11, 10, 8, 30, 0));
        // 注意这里的通道名【TOPIC_USER】要和RedisMessageListenerContainer里面配置的一致
        redisTemplate.convertAndSend("TOPIC_USER", JSON.toJSONString(userDto));
        log.info("发送成功");
    }

}

  注意这里的通道名要和 RedisMessageListenerContainer 里配置的一致,不然消息发送不出去的。

8090的日志

onMessage --> 消息通道是:TOPIC_USER
反序列化的结果:{"age":25,"department":"财务部","hireDate":"2016-11-10T08:30:00","id":"BAT002","name":"包雅馨","salary":8800.0}
计算得到的key: c69dc1563cc892718bf3ee0c5b90320b
接收的结果:UserDto(id=BAT002,name=包雅馨,age=25,department=财务部,salary=8800.0,hireDate=2016-11-10T08:30)
处理redis消息完成

8091的日志

onMessage --> 消息通道是:TOPIC_USER
反序列化的结果:{"age":25,"salary":8800.0}
计算得到的key: c69dc1563cc892718bf3ee0c5b90320b
其他服务处理中

  从结果上可以看到,分布式服务都可以接收到消息,但是最终只有一台服务会真正进行业务的处理,因为我这里使用了最简单的分布式锁来控制了,实际上 redisTemplate.opsForValue().setIfAbsent() 并不是最优解,尤其是在集群模式,我这里只是为了演示要有这么一个操作,推荐还是使用 Redisson 去做分布式锁更可靠。如果有不懂的可以参考我之前的文章:SpringBoot基于Redisson实现分布式锁并分析其原理

五、优化

  其实我们还可以继续去优化我们的配置,消息的接收都是在频繁的创建线程,从而占用系统资源,我们可以通过线程池的方式去优化,RedisMessageListenerContainer 类中有一个方法setTaskExecutor(Executor taskExecutor)可以为监听容器配置线程池。配置线程池以后,所有的线程都会由该线程池产生,因此我们可以通过调节线程池来控制队列监听的速率。修改步骤大致如下:

5.1、注册任务执行器

  RedisConfiguration 新注册Bean:Executor

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置核心线程数
        executor.setCorePoolSize(5);
        //设置最大线程数
        executor.setMaxPoolSize(10);
        //设置任务队列容量
        executor.setQueueCapacity(10);
        //设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        //设置默认线程名称(线程前缀名称,有助于区分不同线程池之间的线程比如:taskExecutor-)
        executor.setThreadNamePrefix("taskExecutor-");
        //设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //设置允许核心线程超时
        executor.setAllowCoreThreadTimeOut(true);
        return executor;
    }

5.2、配置任务执行器

  redisMessageListenerContainer.setTaskExecutor(taskExecutor())

	@Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, RedisMessageListenerListener redisMessageListenerListener) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        // 设置连接工厂
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        // 绑定监听器和信道
        redisMessageListenerContainer.addMessageListener(redisMessageListenerListener, channelTopic());
        // 配置任务执行器
        redisMessageListenerContainer.setTaskExecutor(taskExecutor());
        return redisMessageListenerContainer;
    }

5.3、启用异步执行器

  RedisConfiguration 增加注解@EnableAsync,为了其他操作也能用到异步操作处理。

@Slf4j
@EnableAsync
@Configuration
@EnableCaching
public class RedisConfiguration {
   //...
}

原文地址:https://blog.csdn.net/Alian_1223/article/details/129188701

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读1.3k次。在 Redis 中,键(Keys)是非常重要的概念,它们代表了存储在数据库中的数据的标识符。对键的有效管理和操作是使用 Redis 数据库的关键一环,它直接影响到数据的存取效率、系统的稳定性和开发的便利性。本文将深入探讨 Redis 中键的管理和操作,包括键的命名规范、常用的键操作命令以及一些最佳实践。我们将详细介绍如何合理命名键、如何使用键的过期和持久化特性、如何批量删除键等技巧,旨在帮助读者更好地理解并灵活运用 Redis 中的键,从而提高数据管理和操作的效率和可靠性。
文章浏览阅读3.3k次,点赞44次,收藏88次。本篇是对单节点的应用,但从中我们也能推断出一些关于集群的应用,不过大多数公司能搞个主从就已经是不错了,所以你能学会这个已经算是很有用了,关于ES,博主前面也讲过一些基础应用,创建一个工具类利用ES的数据模型进行存储就可以达到一个canal同时对Redis和ES的同步,如果担心出问题,可以把Canal搞成集群的形式,这个后续有时间博主再给大家做讲解。今天就到这里了,觉得不错就支持一下吧。_canal redis
文章浏览阅读8.4k次,点赞8次,收藏18次。Spring Boot 整合Redis实现消息队列,RedisMessageListenerContainer的使用,Pub/Sub模式的优缺点_springboot redis 消息队列
文章浏览阅读978次,点赞25次,收藏21次。在Centos上安装Redis5.0保姆级教程!_centos7 安装redis5.0服务器
文章浏览阅读1.2k次,点赞21次,收藏22次。Docker-Compose部署Redis(v7.2)主从模式首先需要有一个redis主从集群,才能接着做redis哨兵模式。_warning: sentinel was not able to save the new configuration on disk!!!: dev
文章浏览阅读2.2k次,点赞59次,收藏38次。合理的JedisPool资源池参数设置能为业务使用Redis保驾护航,本文将对JedisPool的使用、资源池的参数进行详细说明,最后给出“最合理”配置。_jedispool资源池优化
文章浏览阅读1.9k次。批量删除指定前缀的Key有两中方法,一种是借助 redis-cli,另一种是通过 SCAN命令来遍历所有匹配前缀的 key,并使用 DEL命令逐个删除它们。_redis删除前缀的key
文章浏览阅读890次,点赞18次,收藏20次。1. Redis时一个key-cakye的数据库,key一般是String类型,不过value类型有很多。eg.String Hash List Set SortedSet (基本) | GEO BitMap HyperLog (特殊)2.Redis为了方便学习,将操作不同类型的命令做了分组,在官网可以进行查询。
文章浏览阅读1.1k次,点赞19次,收藏26次。若不使用Redisson,而是用synchronized(this),此时会造成对服务器的加锁,若开始大量查询ID为1的商品,每台机器都会先跑一遍加个锁,然后在查询ID为2的数据,此时需要等待ID为1的锁释放,所以需要将this对象调整为全局商品ID。若在执行bgsave命令时,还有其他redis命令被执行(主线程数据修改),此时会对数据做个副本,然后bgsave命令执行这个副本数据写入rdb文件,此时主线程还可以继续修改数据。在当前redis目录下会生成aof文件,对redis修改数据的命令进行备份。
文章浏览阅读1.5k次,点赞39次,收藏24次。本文全面剖析Redis集群在分布式环境下的数据一致性问题,从基础原理到高级特性,涵盖主从复制、哨兵模式、持久化策略等关键点,同时也分享了关于监控、故障模拟与自适应写一致性策略的实践经验。_redis集群一致性
文章浏览阅读1k次。RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是它有可能会丢数据,我们通常在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢数据的风险要小很多,在AOF文件中可以设置刷盘策略,我们当时设置的就是每秒批量写入一次命令。AOF的含义是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据。:在Redis中提供了两种数据持久化的方式:1、RDB 2、AOF。
文章浏览阅读1k次,点赞24次,收藏21次。NoSQL(No only SQL)数据库,泛指非关系型数据库,实现对于传统数据库而言的。NoSQL 不依赖业务逻辑方式进行存储,而以简单的 key-value 模式存储。因此大大增加了数据库的扩展能力。不遵循SQL标准不支持ACID远超于SQL的性能Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。
文章浏览阅读988次,点赞17次,收藏19次。在上面的步骤中,我们已经开启了 MySQL 的远程访问功能,但是,如果使用 MySQL 管理工具 navicat 连接 MySQL 服务端时,还是可能会出现连接失败的情况。在实际工作中,如果我们需要从其他地方访问和管理 MySQL 数据库,就需要开启 MySQL 的远程访问功能并设置相应的权限。这对于我们的工作效率和数据安全都有很大的帮助。通过查看 MySQL 用户表,我们可以看到’host’为’%’,说明 root 用户登录 MySQL 的时候,可以允许任意的 IP 地址访问 MySQL 服务端。
文章浏览阅读956次。Redis Desktop Manager(RDM)是一款用于管理和操作Redis数据库的图形化界面工具。提供了简单易用的界面,使用户能够方便地执行各种Redis数据库操作,并且支持多个Redis服务器的连接_redisdesktopmanager安装包
文章浏览阅读1.9k次,点赞52次,收藏27次。缓存击穿指的是数据库有数据,缓存本应该也有数据,但是缓存过期了,Redis 这层流量防护屏障被击穿了,请求直奔数据库。缓存穿透指的是数据库本就没有这个数据,请求直奔数据库,缓存系统形同虚设。缓存雪崩指的是大量的热点数据无法在 Redis 缓存中处理(大面积热点数据缓存失效、Redis 宕机),流量全部打到数据库,导致数据库极大压力。
文章浏览阅读1.2k次。一次命令时间(borrow|return resource + Jedis执行命令(含网络) )的平均耗时约为1ms,一个连接的QPS大约是1000,业务期望的QPS是50000,那么理论上需要的资源池大小是50000 / 1000 = 50个,实际maxTotal可以根据理论值合理进行微调。JedisPool默认的maxTotal=8,下面的代码从JedisPool中借了8次Jedis,但是没有归还,当第9次(jedisPool.getResource().ping())3、发生异常可能的情况。_redis.clients.jedis.exceptions.jedisconnectionexception: could not get a res
文章浏览阅读1k次,点赞27次,收藏18次。在这篇文章中,你将了解到如何在 CentOS 系统上安装 Redis 服务,并且掌握通过自定义域名来访问 Redis 服务的技巧。通过使用自定义域名,你可以方便地管理和访问你的 Redis 数据库,提高工作效率。无论你是开发者、系统管理员还是对 Redis 感兴趣的读者,这篇文章都会为你提供清晰的指导和实用的技巧。阅读本文,轻松搭建自己的 Redis 服务,并体验自定义域名带来的便捷!_redis怎么自定义域名
文章浏览阅读1.1k次,点赞15次,收藏18次。我们post请求,拦截器要预先读取HtppServletRequest里面的body的数据,是通过io的方式,都知道io读取完毕之后,之前的数据是变为null的,但是,当我么后面的接口来委派的时候,也是通过io读取body。我们要考虑一个事情,就是我们要验证数据的重复提交: 首先第一次提交的数据肯定是要被存储的,当而第二次往后,每次提交数据都会与之前的数据产生比对从而验证数据重复提交,我们要具体判断数据是否重复提交的子类。发现数据是成功存入的,剩余7s过期,在10s之内,也就是数据没过期之前,在发送一次。_json.parseobject(str, clazz, auto_type_filter);
文章浏览阅读3.9k次,点赞3次,收藏7次。PHP使用Redis实战实录系列:我们首先检查$redis->connect()方法的返回值来确定是否成功连接到Redis服务器。如果连接失败,我们可以输出相应的错误信息。如果连接成功,我们再执行一些操作,如$redis->set()、$redis->get()等,并检查每个操作的返回结果来判断是否发生了异常。_php redis
文章浏览阅读1.5w次,点赞23次,收藏51次。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis 是一个高性能的key-value数据库。redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。_redisdesktopmanager下载