概述

Redis(Remote Dictionary Server)是一个开源的内存数据库,也可以作为缓存、消息队列和会话管理工具等使用。Redis 支持多种数据结构,包括字符串、列表、集合、有序集合、哈希表等,提供了丰富的数据操作功能,如增删改查、事务、发布订阅等。

redis的特性

高性能: Redis 数据存储在内存中,读写速度非常快,适合高并发的场景。
丰富的数据结构: Redis 支持多种数据结构,可以满足各种数据存储和处理需求。
持久化: Redis 支持持久化功能,可以将内存中的数据保存到磁盘上,以防止数据丢失。
高可用性: Redis 支持主从复制和哨兵机制,可以实现高可用性和自动故障转移。
丰富的功能: Redis 提供了丰富的功能和命令,如事务、发布订阅、Lua 脚本、过期键等。
灵活性: Redis 的配置和部署非常灵活,可以根据需求进行定制和扩展。

redis的应用场景

缓存:  可以作为缓存系统,存储热点数据,加速数据访问速度。
会话管理:  可以用于存储用户会话信息,实现分布式会话管理。
消息队列:  发布订阅功能可以用于构建消息队列系统,实现异步消息传递。
计数器:  可以方便地实现各种计数功能,如网站访问量统计、商品库存计数、消息计数等,而且 Redis 提供的原子操作保证了计数器操作的线程安全性。
分布式锁:  可以用于实现分布式锁,保证多个节点之间的数据一致性。
实时排行榜:  有序集合和计数器可以用于实现实时排行榜功能,如热门商品排行、热门搜索排行等。

redis缓存示例

缓存数据

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RedisTemplate<String, User> redisTemplate;

    public User getUserById(Long id) {
        // 先从缓存中查询用户信息
        String key = "user:" + id;
        User user = redisTemplate.opsForValue().get(key);
        if (user == null) {
            // 如果缓存中不存在,则从数据库中查询
            user = userRepository.findById(id).orElse(null);
            if (user != null) {
                // 将查询结果存储到缓存中
                redisTemplate.opsForValue().set(key, user);
            }
        }
        return user;
    }
}

redis会话管理示例

登录/验证会话以及退出服务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class UserService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public String login(String username, String password) {
        // 模拟登录验证
        if ("admin".equals(username) && "password".equals(password)) {
            // 生成一个随机的 token 作为用户的会话 ID
            String token = UUID.randomUUID().toString();
            // 将用户的会话信息存储在 Redis 中,设置过期时间为 30 分钟
            redisTemplate.opsForValue().set(token, username, 30, TimeUnit.MINUTES);
            return token;
        } else {
            return null;
        }
    }

    public boolean checkPermission(String token) {
        // 根据 token 在 Redis 中查询用户的会话信息
        String username = redisTemplate.opsForValue().get(token);
        return username != null;
    }

    public void logout(String token) {
        // 从 Redis 中删除用户的会话信息
        redisTemplate.delete(token);
    }
}

redis消息队列示例

在 Redis 中实现消息队列通常使用 Redis 的发布与订阅(Pub/Sub)功能,它可以用于实现简单的消息发布和订阅模式,用于实现生产者和消费者之间的消息通信

首先,我们需要创建一个消息生产者,用于发布消息到 Redis 频道中:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class MessageProducer {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void sendMessage(String channel, String message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

接下来,我们创建一个消息消费者,用于订阅 Redis 频道并接收消息:

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;

@Component
public class MessageConsumer implements MessageListener {

    @Override
    public void onMessage(Message message, byte[] pattern) {
        System.out.println("Received message: " + message.toString());
    }
}

最后,我们需要配置 Redis 频道的订阅者,并将消息消费者注册到 Redis 频道上:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
                                                                       MessageConsumer messageConsumer) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(messageConsumer, new ChannelTopic("my_channel"));
        return container;
    }
}

我们通过 RedisMessageListenerContainer 来创建消息监听容器,并将消息消费者注册到 Redis 频道 "my_channel" 上。

现在,当消息生产者调用 sendMessage() 方法发送消息到 Redis 频道时,消息消费者就会收到并处理该消息。

redis分布式锁示例

Redis 可以通过设置键值对的方式来实现分布式锁,具体方法如下:

使用 SETNX(SET if Not eXists)命令来尝试获取锁。如果返回值为 1,表示获取锁成功,即锁不存在;如果返回值为 0,表示获取锁失败,即锁已经被其他客户端持有。

如果获取锁成功,设置一个过期时间,防止锁永远不被释放导致死锁。可以使用 SETEX 或者 PEXPIRE 命令来设置键的过期时间。

在业务处理完成后,通过 DEL 命令来释放锁。

示例:

import redis.clients.jedis.Jedis;

public class DistributedLock {

    private static final String LOCK_KEY = "my_lock";
    private static final int LOCK_EXPIRE_TIME_SECONDS = 30;

    private Jedis jedis;

    public DistributedLock() {
        jedis = new Jedis("localhost");
    }

    public boolean acquireLock() {
        long result = jedis.setnx(LOCK_KEY, "1");
        if (result == 1) {
            jedis.expire(LOCK_KEY, LOCK_EXPIRE_TIME_SECONDS);
            return true;
        }
        return false;
    }

    public void releaseLock() {
        jedis.del(LOCK_KEY);
    }

    public void close() {
        jedis.close();
    }

    public static void main(String[] args) {
        DistributedLock lock = new DistributedLock();
        try {
            if (lock.acquireLock()) {
                System.out.println("Lock acquired successfully!");
                // 这里可以执行需要加锁保护的业务逻辑
            } else {
                System.out.println("Failed to acquire lock!");
            }
        } finally {
            lock.releaseLock();
            lock.close();
        }
    }
}