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);
    }
}
相关推荐
数据库小组8 小时前
2026 年,MySQL 到 SelectDB 同步为何更关注实时、可观测与可校验?
数据库·mysql·数据库管理工具·数据同步·ninedata·selectdb·迁移工具
华科易迅8 小时前
MybatisPlus增删改查操作
android·java·数据库
Kethy__8 小时前
计算机中级-数据库系统工程师-计算机体系结构与存储系统
大数据·数据库·数据库系统工程师·计算机中级
SHoM SSER8 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
熬夜的咕噜猫9 小时前
MySQL备份与恢复
数据库·oracle
jnrjian9 小时前
recover database using backup controlfile until cancel 假recover,真一致
数据库·oracle
lifewange9 小时前
java连接Mysql数据库
java·数据库·mysql
大妮哟10 小时前
postgresql数据库日志量异常原因排查
数据库·postgresql·oracle
还是做不到嘛\.10 小时前
Dvwa靶场-SQL Injection (Blind)-基于sqlmap
数据库·sql·web安全
不写八个11 小时前
PHP教程004:php链接mysql数据库
数据库·mysql·php