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);
    }
}
相关推荐
2501_933670792 小时前
2026秋招数据分析岗备考路线:SQL、BI、项目与面试题拆解
数据库
我要见SA姐14 小时前
告别 Copilot?Codex 本地化部署指南
运维·数据库·机器学习·oracle·回归
xcLeigh5 小时前
聊聊国产化替换:好用数据迁移工具KDMS怎么帮咱们搞定评估难
数据库·sql·数据迁移·kes·kdms
Elastic 中国社区官方博客5 小时前
列式存储并不等同于列式数据库。Columnar 模式为 Elasticsearch 带来了什么
大数据·运维·数据库·elasticsearch·搜索引擎
我要见SA姐16 小时前
用 Claude Code 重构遗留系统:从评估到落地的完整实践指南
数据库·ide·vscode·oracle·编辑器
Nturmoils7 小时前
一份 KDMS 评估报告,怎样排出迁移先后顺序
数据库
这个DBA有点耶7 小时前
异构数据集成怎么做?5 种同步方案对比 + 金融级 CDC 实战解析
数据库·oracle·架构
独泪了无痕7 小时前
SQL函数实战:GREATEST与LEAST的技巧
数据库·sql·mysql
码少女8 小时前
Linux--多路转接之select
java·服务器·数据库
梁辰兴8 小时前
软件工程:软件维护的副作用
数据库·软件工程·梁辰兴·控制方法·软件维护的副作用·副作用类型·副作用原因