Redis和RedisTemplate详细教程 - 从入门到精通

目录

1. 什么是Redis

1.1 简单理解

1.2 技术定义

1.3 核心特点

2. Redis安装和配置

2.1 Docker安装(推荐)

2.2 Linux系统安装

2.3 基础配置

Redis配置文件(redis.conf)

性能优化配置

3. Redis数据类型详解

3.1 String(字符串)

Java代码示例

3.2 Hash(哈希)

Java代码示例

3.3 List(列表)

Java代码示例

3.4 Set(集合)

Java代码示例

3.5 Sorted Set(有序集合)

Java代码示例

4. Redis命令操作

4.1 通用命令

4.2 性能分析命令

5. Spring Boot集成Redis

5.1 项目依赖

5.2 配置文件

5.3 Redis配置类

6. RedisTemplate详解

6.1 RedisTemplate基本使用

6.2 自定义Redis工具类

6.3 缓存注解使用

7. 实际应用场景

7.1 会话管理

7.2 分布式锁

7.3 限流控制

7.4 缓存穿透、击穿、雪崩解决方案

8. 性能优化技巧

8.1 连接池优化

8.2 序列化优化

8.3 Pipeline批量操作

8.4 内存优化

9. 集群和高可用

9.1 Redis Sentinel配置

9.2 Redis Cluster配置

9.3 集群操作工具

10. 最佳实践

10.1 Key设计规范

10.2 缓存策略

10.3 监控和运维

11. 常见问题解答

11.1 连接问题

11.2 性能问题

11.3 数据一致性问题

12. 监控和运维

12.1 监控指标

12.2 告警配置

总结

🎯 核心收获

📋 关键要点回顾

数据类型特点

最佳实践

性能优化

🚀 进阶方向

💡 实践建议


1. 什么是Redis

1.1 简单理解

想象一下你的大脑记忆系统:

短期记忆(Redis)

  • 快速存取,瞬间想起
  • 容量有限,但速度极快
  • 用于存储最近使用的信息

长期记忆(数据库)

  • 容量巨大,但访问较慢
  • 需要时间去"回忆"
  • 存储所有历史信息

1.2 技术定义

Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作:

  • 缓存:加速数据访问
  • 数据库:持久化存储
  • 消息队列:异步通信

1.3 核心特点

Redis的优势:
  速度极快: 所有数据存储在内存中
  数据结构丰富: 支持多种数据类型
  持久化支持: 可以将数据保存到磁盘
  集群支持: 支持分布式部署
  原子操作: 保证数据一致性

生活类比

  • Redis就像便利店:东西不多但拿取很快
  • 数据库就像大型超市:东西很全但需要找一会儿

2. Redis安装和配置

2.1 Docker安装(推荐)

最简单的安装方式:

# 1. 拉取Redis镜像
docker pull redis:7.0

# 2. 启动Redis容器
docker run -d \
  --name redis-server \
  -p 6379:6379 \
  -v /data/redis:/data \
  redis:7.0 redis-server --appendonly yes

# 3. 进入Redis客户端
docker exec -it redis-server redis-cli

# 4. 测试连接
127.0.0.1:6379> ping
PONG

2.2 Linux系统安装

# Ubuntu/Debian安装
sudo apt update
sudo apt install redis-server

# CentOS/RHEL安装
sudo yum install epel-release
sudo yum install redis

# 启动Redis服务
sudo systemctl start redis
sudo systemctl enable redis

# 检查服务状态
sudo systemctl status redis

2.3 基础配置

Redis配置文件(redis.conf)
# 基本配置
port 6379                    # 端口号
bind 127.0.0.1              # 绑定IP地址
daemonize yes                # 后台运行

# 内存配置
maxmemory 2gb                # 最大内存限制
maxmemory-policy allkeys-lru # 内存满时的淘汰策略

# 持久化配置
save 900 1                   # 900秒内至少1个key变化时保存
save 300 10                  # 300秒内至少10个key变化时保存
save 60 10000               # 60秒内至少10000个key变化时保存

# AOF持久化
appendonly yes               # 启用AOF持久化
appendfsync everysec        # 每秒同步一次

# 安全配置
requirepass your_password    # 设置密码
性能优化配置
# TCP配置
tcp-keepalive 300           # TCP保活时间
tcp-backlog 511             # TCP监听队列长度

# 客户端配置
timeout 0                   # 客户端空闲超时时间(0表示永不超时)
maxclients 10000           # 最大客户端连接数

# 内存优化
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
set-max-intset-entries 512

3. Redis数据类型详解

3.1 String(字符串)

生活类比:就像在纸条上写字,一张纸条存一个信息。

# 基本操作
SET name "张三"              # 设置值
GET name                     # 获取值
DEL name                     # 删除

# 带过期时间
SETEX session_id 3600 "abc123"  # 设置并指定3600秒过期
TTL session_id               # 查看剩余过期时间

# 数字操作
SET counter 1
INCR counter                 # 自增1,结果为2
INCRBY counter 5            # 增加5,结果为7
DECR counter                # 自减1,结果为6
Java代码示例
@Service
public class StringRedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 基本字符串操作
     */
    public void stringOperations() {
        // 设置值
        redisTemplate.opsForValue().set("user:name", "张三");
        
        // 设置带过期时间的值
        redisTemplate.opsForValue().set("session:123", "login_token", 30, TimeUnit.MINUTES);
        
        // 获取值
        String name = (String) redisTemplate.opsForValue().get("user:name");
        System.out.println("用户名: " + name);
        
        // 检查key是否存在
        Boolean exists = redisTemplate.hasKey("user:name");
        System.out.println("key存在: " + exists);
        
        // 设置过期时间
        redisTemplate.expire("user:name", 1, TimeUnit.HOURS);
    }
    
    /**
     * 数字操作
     */
    public void numberOperations() {
        // 计数器操作
        redisTemplate.opsForValue().set("page_views", 0);
        
        // 增加访问量
        Long views = redisTemplate.opsForValue().increment("page_views");
        System.out.println("页面访问量: " + views);
        
        // 增加指定数量
        Long newViews = redisTemplate.opsForValue().increment("page_views", 10);
        System.out.println("新的访问量: " + newViews);
    }
    
    /**
     * 批量操作
     */
    public void batchOperations() {
        // 批量设置
        Map<String, Object> values = new HashMap<>();
        values.put("user:1:name", "张三");
        values.put("user:1:age", 25);
        values.put("user:1:city", "北京");
        redisTemplate.opsForValue().multiSet(values);
        
        // 批量获取
        List<String> keys = Arrays.asList("user:1:name", "user:1:age", "user:1:city");
        List<Object> results = redisTemplate.opsForValue().multiGet(keys);
        
        for (int i = 0; i < keys.size(); i++) {
            System.out.println(keys.get(i) + ": " + results.get(i));
        }
    }
}

3.2 Hash(哈希)

生活类比:就像一个联系人卡片,一张卡片上有姓名、电话、地址等多个字段。

# 基本操作
HSET user:1 name "张三" age 25 city "北京"  # 设置多个字段
HGET user:1 name                           # 获取单个字段
HMGET user:1 name age city                # 获取多个字段
HGETALL user:1                            # 获取所有字段
HDEL user:1 city                          # 删除字段
Java代码示例
@Service
public class HashRedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 用户信息管理
     */
    public void userManagement() {
        String userKey = "user:1001";
        
        // 设置用户信息
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("name", "李四");
        userInfo.put("age", 28);
        userInfo.put("email", "[email protected]");
        userInfo.put("phone", "13812345678");
        userInfo.put("department", "技术部");
        
        redisTemplate.opsForHash().putAll(userKey, userInfo);
        
        // 设置单个字段
        redisTemplate.opsForHash().put(userKey, "last_login", new Date().toString());
        
        // 获取单个字段
        String name = (String) redisTemplate.opsForHash().get(userKey, "name");
        System.out.println("用户姓名: " + name);
        
        // 获取多个字段
        List<Object> fields = redisTemplate.opsForHash().multiGet(userKey, 
            Arrays.asList("name", "email", "department"));
        System.out.println("用户信息: " + fields);
        
        // 获取所有字段
        Map<Object, Object> allInfo = redisTemplate.opsForHash().entries(userKey);
        allInfo.forEach((key, value) -> {
            System.out.println(key + ": " + value);
        });
        
        // 检查字段是否存在
        Boolean hasEmail = redisTemplate.opsForHash().hasKey(userKey, "email");
        System.out.println("有邮箱信息: " + hasEmail);
        
        // 获取所有字段名
        Set<Object> keys = redisTemplate.opsForHash().keys(userKey);
        System.out.println("所有字段: " + keys);
        
        // 删除字段
        redisTemplate.opsForHash().delete(userKey, "phone");
    }
    
    /**
     * 商品信息管理示例
     */
    public void productManagement() {
        String productKey = "product:2001";
        
        // 商品基本信息
        redisTemplate.opsForHash().put(productKey, "name", "iPhone 15");
        redisTemplate.opsForHash().put(productKey, "price", "6999.00");
        redisTemplate.opsForHash().put(productKey, "category", "手机");
        redisTemplate.opsForHash().put(productKey, "brand", "Apple");
        redisTemplate.opsForHash().put(productKey, "stock", "100");
        
        // 价格操作(数字字段)
        redisTemplate.opsForHash().increment(productKey, "stock", -1); // 库存减1
        
        // 获取库存
        Object stock = redisTemplate.opsForHash().get(productKey, "stock");
        System.out.println("当前库存: " + stock);
        
        // 获取商品详情
        Map<Object, Object> productInfo = redisTemplate.opsForHash().entries(productKey);
        System.out.println("商品信息: " + productInfo);
    }
}

3.3 List(列表)

生活类比:就像购物清单待办事项,有顺序,可以从头部或尾部添加删除。

# 基本操作
LPUSH shopping_list "苹果" "香蕉"     # 从左边添加
RPUSH shopping_list "牛奶"           # 从右边添加
LPOP shopping_list                   # 从左边弹出
RPOP shopping_list                   # 从右边弹出
LLEN shopping_list                   # 获取长度
LRANGE shopping_list 0 -1           # 获取所有元素
Java代码示例
@Service
public class ListRedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 消息队列示例
     */
    public void messageQueue() {
        String queueKey = "message_queue";
        
        // 生产者:发送消息(从左边推入)
        redisTemplate.opsForList().leftPush(queueKey, "消息1: 用户注册成功");
        redisTemplate.opsForList().leftPush(queueKey, "消息2: 订单支付完成");
        redisTemplate.opsForList().leftPush(queueKey, "消息3: 商品库存告警");
        
        System.out.println("队列长度: " + redisTemplate.opsForList().size(queueKey));
        
        // 消费者:处理消息(从右边弹出)
        while (redisTemplate.opsForList().size(queueKey) > 0) {
            String message = (String) redisTemplate.opsForList().rightPop(queueKey);
            System.out.println("处理消息: " + message);
        }
    }
    
    /**
     * 最近访问记录
     */
    public void recentVisitedPages() {
        String userKey = "user:1001:recent_pages";
        
        // 记录访问页面(保持最新的10个页面)
        String[] pages = {"首页", "商品列表", "商品详情", "购物车", "订单页面"};
        
        for (String page : pages) {
            // 添加到列表头部
            redisTemplate.opsForList().leftPush(userKey, page);
            
            // 保持列表长度不超过10
            redisTemplate.opsForList().trim(userKey, 0, 9);
        }
        
        // 获取最近访问的页面
        List<Object> recentPages = redisTemplate.opsForList().range(userKey, 0, -1);
        System.out.println("最近访问页面: " + recentPages);
        
        // 获取最新访问的3个页面
        List<Object> top3Pages = redisTemplate.opsForList().range(userKey, 0, 2);
        System.out.println("最新3个页面: " + top3Pages);
    }
    
    /**
     * 文章评论管理
     */
    public void articleComments() {
        String articleKey = "article:1001:comments";
        
        // 添加评论(按时间顺序)
        redisTemplate.opsForList().rightPush(articleKey, "这篇文章写得很好!");
        redisTemplate.opsForList().rightPush(articleKey, "学到了很多知识");
        redisTemplate.opsForList().rightPush(articleKey, "期待更多这样的文章");
        redisTemplate.opsForList().rightPush(articleKey, "作者辛苦了");
        
        // 获取评论总数
        Long commentCount = redisTemplate.opsForList().size(articleKey);
        System.out.println("评论总数: " + commentCount);
        
        // 分页获取评论(每页3条)
        int page = 1;
        int pageSize = 3;
        long start = (page - 1) * pageSize;
        long end = start + pageSize - 1;
        
        List<Object> comments = redisTemplate.opsForList().range(articleKey, start, end);
        System.out.println("第" + page + "页评论: " + comments);
        
        // 获取最新的评论
        Object latestComment = redisTemplate.opsForList().index(articleKey, -1);
        System.out.println("最新评论: " + latestComment);
    }
    
    /**
     * 阻塞队列操作
     */
    public void blockingQueue() {
        String queueKey = "blocking_queue";
        
        // 在另一个线程中消费消息
        new Thread(() -> {
            while (true) {
                try {
                    // 阻塞式弹出(等待10秒)
                    List<Object> result = redisTemplate.opsForList().rightPop(queueKey, 10, TimeUnit.SECONDS);
                    if (result != null && !result.isEmpty()) {
                        System.out.println("消费消息: " + result.get(1));
                    } else {
                        System.out.println("等待超时,没有新消息");
                    }
                } catch (Exception e) {
                    System.out.println("消费异常: " + e.getMessage());
                    break;
                }
            }
        }).start();
        
        // 生产消息
        try {
            Thread.sleep(2000);
            redisTemplate.opsForList().leftPush(queueKey, "延迟消息1");
            
            Thread.sleep(5000);
            redisTemplate.opsForList().leftPush(queueKey, "延迟消息2");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.4 Set(集合)

生活类比:就像朋友圈,每个人只能出现一次,可以找共同好友。

# 基本操作
SADD tags "Java" "Redis" "Spring"    # 添加元素
SMEMBERS tags                        # 获取所有元素
SISMEMBER tags "Java"               # 检查元素是否存在
SCARD tags                          # 获取元素数量
SREM tags "Redis"                   # 删除元素

# 集合运算
SINTER set1 set2                    # 交集
SUNION set1 set2                    # 并集
SDIFF set1 set2                     # 差集
Java代码示例
@Service
public class SetRedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 用户标签管理
     */
    public void userTags() {
        String userKey = "user:1001:tags";
        
        // 添加用户标签
        redisTemplate.opsForSet().add(userKey, "Java开发者", "技术爱好者", "游戏玩家", "美食达人");
        
        // 检查是否有某个标签
        Boolean hasTag = redisTemplate.opsForSet().isMember(userKey, "Java开发者");
        System.out.println("是Java开发者: " + hasTag);
        
        // 获取所有标签
        Set<Object> allTags = redisTemplate.opsForSet().members(userKey);
        System.out.println("用户标签: " + allTags);
        
        // 获取标签数量
        Long tagCount = redisTemplate.opsForSet().size(userKey);
        System.out.println("标签数量: " + tagCount);
        
        // 随机获取一个标签
        Object randomTag = redisTemplate.opsForSet().randomMember(userKey);
        System.out.println("随机标签: " + randomTag);
        
        // 删除标签
        redisTemplate.opsForSet().remove(userKey, "游戏玩家");
    }
    
    /**
     * 共同好友功能
     */
    public void commonFriends() {
        // 用户A的好友
        String userAFriends = "user:A:friends";
        redisTemplate.opsForSet().add(userAFriends, "张三", "李四", "王五", "赵六");
        
        // 用户B的好友
        String userBFriends = "user:B:friends";
        redisTemplate.opsForSet().add(userBFriends, "李四", "王五", "钱七", "孙八");
        
        // 计算共同好友(交集)
        Set<Object> commonFriends = redisTemplate.opsForSet().intersect(userAFriends, userBFriends);
        System.out.println("A和B的共同好友: " + commonFriends);
        
        // 计算A独有的好友(差集)
        Set<Object> uniqueFriendsA = redisTemplate.opsForSet().difference(userAFriends, userBFriends);
        System.out.println("A独有的好友: " + uniqueFriendsA);
        
        // 计算所有好友(并集)
        Set<Object> allFriends = redisTemplate.opsForSet().union(userAFriends, userBFriends);
        System.out.println("所有好友: " + allFriends);
        
        // 保存共同好友到新的集合
        String commonKey = "user:A:B:common_friends";
        redisTemplate.opsForSet().intersectAndStore(userAFriends, userBFriends, commonKey);
    }
    
    /**
     * 文章标签系统
     */
    public void articleTagSystem() {
        // 文章1的标签
        String article1Tags = "article:1001:tags";
        redisTemplate.opsForSet().add(article1Tags, "Java", "Spring Boot", "Redis", "教程");
        
        // 文章2的标签
        String article2Tags = "article:1002:tags";
        redisTemplate.opsForSet().add(article2Tags, "Python", "Django", "Redis", "Web开发");
        
        // 根据标签推荐相关文章
        Set<Object> relatedTags = redisTemplate.opsForSet().intersect(article1Tags, article2Tags);
        System.out.println("相关标签: " + relatedTags);
        
        // 按标签分类文章
        String redisTagArticles = "tag:Redis:articles";
        redisTemplate.opsForSet().add(redisTagArticles, "article:1001", "article:1002");
        
        // 获取Redis标签下的所有文章
        Set<Object> redisArticles = redisTemplate.opsForSet().members(redisTagArticles);
        System.out.println("Redis相关文章: " + redisArticles);
    }
    
    /**
     * 去重和抽奖功能
     */
    public void deduplicationAndLottery() {
        String participantsKey = "lottery:participants";
        
        // 添加参与者(自动去重)
        redisTemplate.opsForSet().add(participantsKey, 
            "user1001", "user1002", "user1003", "user1001", "user1004", "user1002");
        
        System.out.println("参与者数量: " + redisTemplate.opsForSet().size(participantsKey));
        
        // 随机抽取中奖者
        Set<Object> winners = redisTemplate.opsForSet().distinctRandomMembers(participantsKey, 2);
        System.out.println("中奖者: " + winners);
        
        // 弹出式抽奖(中奖后移除)
        Object luckyWinner = redisTemplate.opsForSet().pop(participantsKey);
        System.out.println("幸运大奖得主: " + luckyWinner);
        
        System.out.println("剩余参与者: " + redisTemplate.opsForSet().size(participantsKey));
    }
}

3.5 Sorted Set(有序集合)

生活类比:就像排行榜,每个人都有分数,按分数排序。

# 基本操作
ZADD leaderboard 100 "张三" 95 "李四" 88 "王五"  # 添加带分数的元素
ZRANGE leaderboard 0 -1 WITHSCORES              # 按分数升序获取
ZREVRANGE leaderboard 0 -1 WITHSCORES           # 按分数降序获取
ZSCORE leaderboard "张三"                        # 获取某个元素的分数
ZRANK leaderboard "张三"                         # 获取排名(升序)
ZREVRANK leaderboard "张三"                      # 获取排名(降序)
Java代码示例
@Service
public class SortedSetRedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 游戏排行榜
     */
    public void gameLeaderboard() {
        String leaderboardKey = "game:leaderboard";
        
        // 添加玩家分数
        redisTemplate.opsForZSet().add(leaderboardKey, "张三", 1500);
        redisTemplate.opsForZSet().add(leaderboardKey, "李四", 1200);
        redisTemplate.opsForZSet().add(leaderboardKey, "王五", 1800);
        redisTemplate.opsForZSet().add(leaderboardKey, "赵六", 1350);
        redisTemplate.opsForZSet().add(leaderboardKey, "钱七", 1650);
        
        // 获取排行榜前3名(分数降序)
        Set<ZSetOperations.TypedTuple<Object>> top3 = 
            redisTemplate.opsForZSet().reverseRangeWithScores(leaderboardKey, 0, 2);
        
        System.out.println("=== 游戏排行榜前3名 ===");
        int rank = 1;
        for (ZSetOperations.TypedTuple<Object> entry : top3) {
            System.out.println("第" + rank + "名: " + entry.getValue() + " - " + entry.getScore() + "分");
            rank++;
        }
        
        // 获取某个玩家的排名和分数
        String playerName = "张三";
        Double score = redisTemplate.opsForZSet().score(leaderboardKey, playerName);
        Long ranking = redisTemplate.opsForZSet().reverseRank(leaderboardKey, playerName);
        
        System.out.println(playerName + " 的分数: " + score + ", 排名: " + (ranking + 1));
        
        // 增加分数
        Double newScore = redisTemplate.opsForZSet().incrementScore(leaderboardKey, playerName, 100);
        System.out.println(playerName + " 获得100分奖励,新分数: " + newScore);
        
        // 获取分数范围内的玩家
        Set<Object> middlePlayers = redisTemplate.opsForZSet().rangeByScore(leaderboardKey, 1300, 1600);
        System.out.println("分数在1300-1600之间的玩家: " + middlePlayers);
        
        // 获取总玩家数
        Long totalPlayers = redisTemplate.opsForZSet().zCard(leaderboardKey);
        System.out.println("总玩家数: " + totalPlayers);
    }
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值