Redis 使用场景

1、分布式锁

在实际项目中,建议直接使用 Redisson,它提供了完善的分布式锁实现(包括可重入、公平锁、红锁等):

复制代码
RLock lock = redisson.getLock("myLock");
lock.lock(); // 自动续期(watchdog)
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

Java 分布式锁实现

复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Collections;
import java.util.UUID;

public class RedisDistributedLock {

    private final JedisPool jedisPool;
    private final String lockKey;
    private final int expireTimeMs; // 锁自动过期时间(毫秒)
    private String lockValue;       // 当前客户端持有的唯一标识

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;

    // Lua 脚本:只有 value 匹配才删除锁(原子操作)
    private static final String UNLOCK_SCRIPT =
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "   return redis.call('del', KEYS[1]) " +
        "else " +
        "   return 0 " +
        "end";

    public RedisDistributedLock(JedisPool jedisPool, String lockKey, int expireTimeMs) {
        this.jedisPool = jedisPool;
        this.lockKey = lockKey;
        this.expireTimeMs = expireTimeMs;
    }

    /**
     * 尝试获取锁(非阻塞)
     */
    public boolean tryLock() {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            lockValue = UUID.randomUUID().toString();
            // SET key value NX PX expireTime
            String result = jedis.set(lockKey, lockValue, "NX", "PX", expireTimeMs);
            return LOCK_SUCCESS.equals(result);
        } catch (Exception e) {
            // 日志记录异常(此处简化)
            e.printStackTrace();
            return false;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 阻塞式获取锁,带超时时间(单位:毫秒)
     */
    public boolean lock(long timeoutMs) {
        long start = System.currentTimeMillis();
        while (true) {
            if (tryLock()) {
                return true;
            }
            // 等待一段时间再重试
            try {
                Thread.sleep(20); // 20ms 轮询
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }

            // 检查是否超时
            if (System.currentTimeMillis() - start > timeoutMs) {
                return false;
            }
        }
    }

    /**
     * 释放锁(必须由持有者调用)
     */
    public boolean unlock() {
        if (lockValue == null) {
            return false;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Object result = jedis.eval(UNLOCK_SCRIPT, Collections.singletonList(lockKey),
                    Collections.singletonList(lockValue));
            return RELEASE_SUCCESS.equals(result);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (jedis != null) {
                jedis.close();
            }
            lockValue = null; // 清空标识
        }
    }

    // ===== 可选:支持 try-with-resources 的方式使用锁 =====
    public AutoCloseableLock autoLock(long timeoutMs) {
        return new AutoCloseableLock(timeoutMs);
    }

    public class AutoCloseableLock implements AutoCloseable {
        private final boolean locked;

        public AutoCloseableLock(long timeoutMs) {
            this.locked = lock(timeoutMs);
            if (!locked) {
                throw new RuntimeException("Failed to acquire lock: " + lockKey);
            }
        }

        @Override
        public void close() {
            if (locked) {
                unlock();
            }
        }
    }
}

使用示例

复制代码
public class DistributedLockExample {
    public static void main(String[] args) {
        // 配置 Jedis 连接池(假设 Redis 在本地)
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(10);
        JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);

        String lockKey = "order:process:123";
        RedisDistributedLock lock = new RedisDistributedLock(jedisPool, lockKey, 10000); // 10秒过期

        // 方式一:手动加锁/解锁
        if (lock.lock(5000)) { // 最多等待5秒
            try {
                System.out.println("获取锁成功,执行业务逻辑...");
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
                System.out.println("锁已释放");
            }
        } else {
            System.out.println("未能在5秒内获取锁");
        }

        // 方式二:使用 try-with-resources(推荐)
        try (RedisDistributedLock.AutoCloseableLock autoLock = lock.autoLock(5000)) {
            System.out.println("进入临界区(自动管理锁)");
            Thread.sleep(1000);
        } catch (Exception e) {
            System.err.println("加锁失败或业务异常: " + e.getMessage());
        }

        // 关闭连接池
        jedisPool.close();
    }
}

2、缓存

以下例子基于 Spring Boot + RedisTemplate

配置 RedisTemplate(支持 JSON 序列化)

复制代码
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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 使用 String 序列化 key
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 使用 JSON 序列化 value(支持复杂对象)
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        template.afterPropertiesSet();
        return template;
    }
}

缓存服务类(核心逻辑)

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class UserService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserRepository userRepository; // 假设这是你的数据库访问层

    private static final String USER_CACHE_PREFIX = "user:";

    /**
     * 根据 ID 查询用户(带缓存)
     */
    public User getUserById(Long id) {
        if (id == null) return null;

        String cacheKey = USER_CACHE_PREFIX + id;

        // 1. 先查缓存
        User user = (User) redisTemplate.opsForValue().get(cacheKey);
        if (user != null) {
            System.out.println("命中缓存: " + cacheKey);
            return user;
        }

        // 2. 缓存未命中,查数据库
        user = userRepository.findById(id);
        if (user != null) {
            // 3. 写入缓存(设置过期时间,防止雪崩)
            redisTemplate.opsForValue().set(cacheKey, user, 30, TimeUnit.MINUTES);
            System.out.println("缓存写入: " + cacheKey);
        } else {
            // 4. 防止缓存穿透:对空结果也缓存(短时间)
            redisTemplate.opsForValue().set(cacheKey, "", 2, TimeUnit.MINUTES);
        }

        return user;
    }

    /**
     * 更新用户(先更新 DB,再删除缓存 ------ Cache-Aside 模式)
     */
    public void updateUser(User user) {
        // 1. 更新数据库
        userRepository.update(user);

        // 2. 删除缓存(比更新缓存更安全,避免脏数据)
        String cacheKey = USER_CACHE_PREFIX + user.getId();
        redisTemplate.delete(cacheKey);
        System.out.println("缓存已删除: " + cacheKey);
    }

    /**
     * 删除用户
     */
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
        redisTemplate.delete(USER_CACHE_PREFIX + id);
    }
}
相关推荐
全栈工程师修炼指南2 小时前
DBA | Oracle RMAN 实战:物理备份与数据恢复全解析
数据库·oracle·dba
小雨的光2 小时前
QuickRedis
spring boot·redis
现在,此刻2 小时前
clickhouse和pgSql跨库查询方案对比
数据库·sql·clickhouse·性能优化
while(1){yan}2 小时前
数据库的基本操作
数据库·oracle
翻斗花园牛图图-2 小时前
MySQL——库的操作
数据库·mysql
大猫会长2 小时前
supabase备份数据库中某个schema的方法
数据库·oracle
-指短琴长-2 小时前
MySQL快速入门——内置函数
android·数据库·mysql
小码过河.4 小时前
告别 mysqldump 痛点!用 mydumper 实现 MySQL 高效备份与恢复
数据库·mysql
孙同学_4 小时前
面试题 16.25. LRU 缓存
leetcode·缓存