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 expireTimeString 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);}}@Overridepublic 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 {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 使用 String 序列化 keytemplate.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 {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate 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);}
}
