Redis学习教程

Redis 学习教程

目录


1. Redis 简介

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息中间件。

1.1 Redis 特点

  • 高性能: 读写速度可达 10万次/秒
  • 丰富的数据类型: String、List、Set、Hash、ZSet 等
  • 持久化: 支持 RDB 和 AOF 两种持久化方式
  • 主从复制: 支持数据备份
  • 高可用: 支持 Redis Cluster 和 Sentinel
  • 单线程模型: 避免线程切换和锁竞争

1.2 Redis 应用场景

  • 缓存: 会话缓存、页面缓存
  • 排行榜: 利用 ZSet 实现
  • 计数器: 文章点赞数、浏览量
  • 分布式锁: 利用 SETNX 实现
  • 消息队列: 利用 List 的 Pub/Sub
  • 社交网络: 共同好友、关注列表
  • 限流: 滑动窗口算法

2. Redis 安装部署

2.1 Linux/Mac 安装

方式一:使用包管理器安装
# Mac 使用 Homebrew
brew install redis

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install redis-server

# CentOS/RHEL
sudo yum install redis
方式二:源码编译安装
# 1. 下载 Redis
wget https://download.redis.io/redis-stable.tar.gz

# 2. 解压
tar xzf redis-stable.tar.gz
cd redis-stable

# 3. 编译安装
make
make test
sudo make install

# 4. 启动 Redis
redis-server

2.2 Docker 安装

# 拉取 Redis 镜像
docker pull redis:latest

# 运行 Redis 容器
docker run -d \
  --name my-redis \
  -p 6379:6379 \
  -v /data/redis:/data \
  redis:latest \
  redis-server --appendonly yes

# 带密码运行
docker run -d \
  --name my-redis \
  -p 6379:6379 \
  redis:latest \
  redis-server --requirepass "your-password"

2.3 Windows 安装

Redis 官方不支持 Windows,可以使用以下方式:

  1. 使用 WSL (Windows Subsystem for Linux)
  2. 使用 Docker Desktop
  3. 下载非官方编译版本

2.4 Redis 配置文件

配置文件位置:/etc/redis/redis.conf

# 绑定 IP
bind 127.0.0.1

# 端口
port 6379

# 守护进程
daemonize yes

# 数据库数量
databases 16

# RDB 持久化
save 900 1
save 300 10
save 60 10000

# AOF 持久化
appendonly yes
appendfsync everysec

# 密码
requirepass yourpassword

# 最大内存
maxmemory 256mb
maxmemory-policy allkeys-lru

2.5 Redis 启动与管理

# 启动 Redis
redis-server /path/to/redis.conf

# 连接 Redis
redis-cli

# 带密码连接
redis-cli -a yourpassword

# 远程连接
redis-cli -h 127.0.0.1 -p 6379 -a yourpassword

# 测试连接
redis-cli ping
# 返回: PONG

# 关闭 Redis
redis-cli shutdown

3. Redis 常用命令

3.1 基础命令

# 查看所有键
KEYS *

# 查看键数量
DBSIZE

# 查看键类型
TYPE keyname

# 检查键是否存在
EXISTS keyname

# 删除键
DEL keyname

# 设置过期时间(秒)
EXPIRE keyname 10

# 查看剩余时间
TTL keyname

# 重命名键
RENAME oldkey newkey

# 选择数据库
SELECT index

# 清空当前数据库
FLUSHDB

# 清空所有数据库
FLUSHALL

# 查看数据库信息
INFO

3.2 String 字符串操作

# 设置值
SET key value

# 获取值
GET key

# 设置值并指定过期时间(秒)
SETEX key seconds value

# 设置多个值
MSET key1 value1 key2 value2

# 获取多个值
MGET key1 key2

# 自增
INCR key

# 自增指定值
INCRBY key increment

# 自减
DECR key

# 追加字符串
APPEND key value

# 获取字符串长度
STRLEN key

3.3 List 列表操作

# 左侧插入
LPUSH key value1 value2

# 右侧插入
RPUSH key value1 value2

# 获取列表范围
LRANGE key 0 -1

# 左侧弹出
LPOP key

# 右侧弹出
RPOP key

# 获取列表长度
LLEN key

# 移除指定值
LREM key count value

# 按索引获取元素
LINDEX key index

# 设置索引元素
LSET key index value

# 保留指定范围
LTRIM key start stop

3.4 Set 集合操作

# 添加元素
SADD key member1 member2

# 获取所有元素
SMEMBERS key

# 检查元素是否存在
SISMEMBER key member

# 获取集合元素个数
SCARD key

# 删除元素
SREM key member

# 随机弹出元素
SPOP key

# 集合交集
SINTER set1 set2

# 集合并集
SUNION set1 set2

# 集合差集
SDIFF set1 set2

3.5 Hash 哈希操作

# 设置字段值
HSET key field value

# 获取字段值
HGET key field

# 设置多个字段
HMSET key field1 value1 field2 value2

# 获取多个字段
HMGET key field1 field2

# 获取所有字段
HGETALL key

# 获取所有字段名
HKEYS key

# 获取所有值
HVALS key

# 删除字段
HDEL key field

# 检查字段是否存在
HEXISTS key field

# 获取字段数量
HLEN key

# 字段值自增
HINCRBY key field increment

3.6 ZSet 有序集合操作

# 添加成员
ZADD key score member

# 获取排名范围
ZRANGE key start stop

# 带分数获取
ZRANGE key start stop WITHSCORES

# 按分数范围获取
ZRANGEBYSCORE key min max

# 获取成员分数
ZSCORE key member

# 获取成员排名
ZRANK key member

# 增加分数
ZINCRBY key increment member

# 删除成员
ZREM key member

# 获取集合大小
ZCARD key

4. Redis 数据类型详解

4.1 String(字符串)

应用场景: 缓存、计数器、分布式锁、Session共享

# 缓存用户信息
SET user:1001 '{"id":1001,"name":"张三"}'

# 计数器
INCR article:readcount:1001

# 分布式锁
SET lock:resource "uuid" NX EX 10

4.2 Hash(哈希)

应用场景: 对象存储、购物车、用户信息

# 存储用户信息
HSET user:1001 name "张三" age 25 email "zhangsan@example.com"

# 购物车
HADD cart:1001 product:1001 2
HADD cart:1001 product:1002 1

4.3 List(列表)

应用场景: 消息队列、文章列表、最新动态

# 消息队列(左侧生产,右侧消费)
LPUSH queue:email '{"to":"user@example.com","subject":"Hello"}'
RPOP queue:email

# 最新动态
LPUSH news:user:1001 "发布了新文章"
LRANGE news:user:1001 0 9

4.4 Set(集合)

应用场景: 标签、共同好友、抽奖

# 用户标签
SADD tags:user:1001 "java" "redis" "mysql"

# 共同好友
SINTER friends:user:1001 friends:user:1002

# 抽奖
SADD lottery:user:1001 user1001 user1002 user1003
SPOP lottery:user:1001

4.5 ZSet(有序集合)

应用场景: 排行榜、优先队列、延时队列

# 排行榜
ZADD rank:score 100 user1001 95 user1002 88 user1003
ZREVRANGE rank:score 0 9 WITHSCORES

# 延时队列
ZADD delay:queue 1709251200 "task1"

5. Java 操作 Redis

5.1 添加依赖

使用 Spring Boot + Jedis/Lettuce:

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
</dependencies>

5.2 配置 Redis

# application.yml
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: yourpassword
    database: 0
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms

5.3 Redis 配置类

package com.example.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
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.Jackson2JsonRedisSerializer;
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);

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

        // Value 使用 JSON 序列化
        Jackson2JsonRedisSerializer<Object> jsonSerializer =
            new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance,
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        jsonSerializer.setObjectMapper(mapper);

        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

5.4 Redis 工具类

package com.example.redis.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // ==================== String 操作 ====================

    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public boolean del(String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    public long del(Collection<String> keys) {
        Long count = redisTemplate.delete(keys);
        return count != null ? count : 0;
    }

    public boolean expire(String key, long timeout, TimeUnit unit) {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
    }

    public long getExpire(String key) {
        Long expire = redisTemplate.getExpire(key);
        return expire != null ? expire : -1;
    }

    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    public long incr(String key) {
        Long count = redisTemplate.opsForValue().increment(key);
        return count != null ? count : 0;
    }

    public long incrBy(String key, long delta) {
        Long count = redisTemplate.opsForValue().increment(key, delta);
        return count != null ? count : 0;
    }

    // ==================== Hash 操作 ====================

    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    public void hDel(String key, Object... fields) {
        redisTemplate.opsForHash().delete(key, fields);
    }

    public boolean hHasKey(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    public long hIncrBy(String key, String field, long delta) {
        Long count = redisTemplate.opsForHash().increment(key, field, delta);
        return count != null ? count : 0;
    }

    // ==================== List 操作 ====================

    public long lPush(String key, Object value) {
        Long count = redisTemplate.opsForList().leftPush(key, value);
        return count != null ? count : 0;
    }

    public long lPushAll(String key, Collection<Object> values) {
        Long count = redisTemplate.opsForList().leftPushAll(key, values);
        return count != null ? count : 0;
    }

    public Object lPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    public long rPush(String key, Object value) {
        Long count = redisTemplate.opsForList().rightPush(key, value);
        return count != null ? count : 0;
    }

    public Object rPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    public long lLen(String key) {
        Long count = redisTemplate.opsForList().size(key);
        return count != null ? count : 0;
    }

    // ==================== Set 操作 ====================

    public long sAdd(String key, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        return count != null ? count : 0;
    }

    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    public boolean sIsMember(String key, Object value) {
        return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
    }

    public long sSize(String key) {
        Long count = redisTemplate.opsForSet().size(key);
        return count != null ? count : 0;
    }

    public long sRem(String key, Object... values) {
        Long count = redisTemplate.opsForSet().remove(key, values);
        return count != null ? count : 0;
    }

    // ==================== ZSet 操作 ====================

    public boolean zAdd(String key, Object value, double score) {
        return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, value, score));
    }

    public long zRem(String key, Object... values) {
        Long count = redisTemplate.opsForZSet().remove(key, values);
        return count != null ? count : 0;
    }

    public Set<Object> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    public Set<Object> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    public long zRank(String key, Object value) {
        Long rank = redisTemplate.opsForZSet().rank(key, value);
        return rank != null ? rank : -1;
    }

    public double zScore(String key, Object value) {
        Double score = redisTemplate.opsForZSet().score(key, value);
        return score != null ? score : 0;
    }

    public long zSize(String key) {
        Long count = redisTemplate.opsForZSet().size(key);
        return count != null ? count : 0;
    }
}

6. Redis 实战示例

6.1 缓存实现

package com.example.redis.service;

import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class CacheService {

    @Autowired
    private RedisUtil redisUtil;

    // 缓存查询
    public <T> T get(String key, Class<T> clazz) {
        Object value = redisUtil.get(key);
        if (value != null) {
            return (T) value;
        }
        return null;
    }

    // 缓存写入
    public void set(String key, Object value) {
        redisUtil.set(key, value, 30, TimeUnit.MINUTES);
    }

    // 缓存写入(指定过期时间)
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisUtil.set(key, value, timeout, unit);
    }

    // 删除缓存
    public void delete(String key) {
        redisUtil.del(key);
    }
}

6.2 分布式锁实现

package com.example.redis.service;

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 DistributedLockService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String LOCK_PREFIX = "lock:";

    /**
     * 尝试获取锁
     * @param lockKey 锁的key
     * @param requestId 请求标识(用于释放锁验证)
     * @param expireTime 过期时间(秒)
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        String key = LOCK_PREFIX + lockKey;
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(key, requestId, expireTime, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(result);
    }

    /**
     * 释放锁
     * @param lockKey 锁的key
     * @param requestId 请求标识
     */
    public void unlock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        Object value = redisTemplate.opsForValue().get(key);

        // 验证是否是自己的锁
        if (requestId.equals(value)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 使用示例
     */
    public void businessMethod() {
        String lockKey = "resource:001";
        String requestId = UUID.randomUUID().toString();

        try {
            // 尝试获取锁,过期时间30秒
            if (tryLock(lockKey, requestId, 30)) {
                // 执行业务逻辑
                System.out.println("执行业务逻辑");
            } else {
                System.out.println("获取锁失败,业务繁忙");
            }
        } finally {
            // 释放锁
            unlock(lockKey, requestId);
        }
    }
}

6.3 限流实现

package com.example.redis.service;

import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RateLimitService {

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 滑动窗口限流
     * @param key 限流key
     * @param limit 限流次数
     * @param timeout 时间窗口(秒)
     * @return 是否允许访问
     */
    public boolean isAllowed(String key, int limit, long timeout) {
        String currentKey = "ratelimit:" + key;

        // 当前请求数+1
        long current = redisUtil.incr(currentKey);

        // 第一次请求时设置过期时间
        if (current == 1) {
            redisUtil.expire(currentKey, timeout, TimeUnit.SECONDS);
        }

        // 判断是否超过限流
        return current <= limit;
    }
}

6.4 排行榜实现

package com.example.redis.service;

import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class RankingService {

    @Autowired
    private RedisUtil redisUtil;

    private static final String RANKING_KEY = "ranking:score";

    /**
     * 增加分数
     */
    public void addScore(String userId, double score) {
        redisUtil.zAdd(RANKING_KEY, userId, score);
    }

    /**
     * 增加分数(增量)
     */
    public void incrementScore(String userId, double delta) {
        double currentScore = redisUtil.zScore(RANKING_KEY, userId);
        redisUtil.zAdd(RANKING_KEY, userId, currentScore + delta);
    }

    /**
     * 获取排行榜(前N名)
     */
    public List<Map<String, Object>> getTopRanking(int topN) {
        Set<Object> set = redisUtil.zReverseRange(RANKING_KEY, 0, topN - 1);
        return set.stream()
            .map(userId -> {
                Map<String, Object> map = Map.of(
                    "userId", userId,
                    "score", redisUtil.zScore(RANKING_KEY, userId),
                    "rank", redisUtil.zRank(RANKING_KEY, userId) + 1
                );
                return map;
            })
            .collect(Collectors.toList());
    }

    /**
     * 获取用户排名
     */
    public long getUserRank(String userId) {
        return redisUtil.zRank(RANKING_KEY, userId) + 1;
    }

    /**
     * 获取用户分数
     */
    public double getUserScore(String userId) {
        return redisUtil.zScore(RANKING_KEY, userId);
    }
}

6.5 购物车实现

package com.example.redis.service;

import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ShoppingCartService {

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 添加商品到购物车
     */
    public void addCartItem(String userId, String productId, int quantity) {
        String key = "cart:" + userId;
        redisUtil.hSet(key, productId, quantity);
    }

    /**
     * 修改商品数量
     */
    public void updateCartItem(String userId, String productId, int quantity) {
        String key = "cart:" + userId;
        if (quantity <= 0) {
            redisUtil.hDel(key, productId);
        } else {
            redisUtil.hSet(key, productId, quantity);
        }
    }

    /**
     * 删除商品
     */
    public void removeCartItem(String userId, String productId) {
        String key = "cart:" + userId;
        redisUtil.hDel(key, productId);
    }

    /**
     * 获取购物车所有商品
     */
    public Map<String, Integer> getCartItems(String userId) {
        String key = "cart:" + userId;
        Map<Object, Object> rawMap = redisUtil.hGetAll(key);

        Map<String, Integer> cartItems = new HashMap<>();
        rawMap.forEach((k, v) -> {
            cartItems.put(k.toString(), Integer.valueOf(v.toString()));
        });
        return cartItems;
    }

    /**
     * 清空购物车
     */
    public void clearCart(String userId) {
        String key = "cart:" + userId;
        redisUtil.del(key);
    }

    /**
     * 获取购物车商品数量
     */
    public int getCartItemCount(String userId) {
        String key = "cart:" + userId;
        Map<Object, Object> rawMap = redisUtil.hGetAll(key);
        return rawMap.values().stream()
            .mapToInt(v -> Integer.valueOf(v.toString()))
            .sum();
    }
}

6.6 消息队列实现

package com.example.redis.service;

import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class MessageQueueService {

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 发送消息
     */
    public void sendMessage(String queueName, Object message) {
        String key = "queue:" + queueName;
        redisUtil.lPush(key, message);
    }

    /**
     * 批量发送消息
     */
    public void sendMessages(String queueName, List<Object> messages) {
        String key = "queue:" + queueName;
        redisUtil.lPushAll(key, messages);
    }

    /**
     * 接收消息
     */
    public Object receiveMessage(String queueName) {
        String key = "queue:" + queueName;
        return redisUtil.rPop(key);
    }

    /**
     * 批量接收消息
     */
    public List<Object> receiveMessages(String queueName, int count) {
        String key = "queue:" + queueName;
        List<Object> messages = new ArrayList<>();

        for (int i = 0; i < count; i++) {
            Object message = redisUtil.rPop(key);
            if (message == null) {
                break;
            }
            messages.add(message);
        }

        return messages;
    }

    /**
     * 获取队列长度
     */
    public long getQueueSize(String queueName) {
        String key = "queue:" + queueName;
        return redisUtil.lLen(key);
    }
}

7. Redis 持久化

7.1 RDB(快照持久化)

RDB 是在指定的时间间隔内生成数据集的时间点快照。

优点:

  • 紧凑的单一文件
  • 恢复速度快
  • 适合备份

缺点:

  • 可能丢失最后一次持久后的数据
  • fork 子进程时可能阻塞

配置示例:

save 900 1      # 900秒内至少有1个key变化
save 300 10     # 300秒内至少有10个key变化
save 60 10000   # 60秒内至少有10000个key变化

rdbcompression yes    # 压缩RDB文件
rdbfilename dump.rdb  # RDB文件名
dir /var/lib/redis    # 文件目录

7.2 AOF(追加文件持久化)

AOF 记录服务器执行的所有写操作命令。

优点:

  • 数据安全性高
  • 可读性强,易于修复

缺点:

  • 文件体积大
  • 恢复速度较慢

配置示例:

appendonly yes           # 开启AOF
appendfilename "appendonly.aof"
appendfsync everysec     # 每秒同步一次
no-appendfsync-on-rewrite no

AOF 重写:

auto-aof-rewrite-percentage 100    # AOF文件比上次重写增长100%时触发
auto-aof-rewrite-min-size 64mb     # AOF文件至少64MB时触发

8. Redis 高级特性

8.1 发布订阅

# 订阅频道
SUBSCRIBE channel1

# 发布消息
PUBLISH channel1 "hello"

# 取消订阅
UNSUBSCRIBE channel1

8.2 事务

# 开启事务
MULTI

# 执行命令
SET key1 value1
SET key2 value2

# 执行事务
EXEC

# 取消事务
DISCARD

8.3 Lua 脚本

# 执行脚本
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey myvalue

# 加载脚本
SCRIPT LOAD "return redis.call('GET', KEYS[1])"

# 执行已加载脚本
EVALSHA sha值 1 mykey

8.4 Pipeline

// Java Pipeline 示例
public void pipelineExample() {
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        for (int i = 0; i < 10000; i++) {
            connection.set(("key:" + i).getBytes(), ("value:" + i).getBytes());
        }
        return null;
    });
}

9. Redis 常见问题

9.1 缓存穿透

问题: 查询不存在的数据,导致每次都查数据库

解决方案:

  1. 缓存空值
  2. 布隆过滤器
  3. 请求参数校验
public Object getByCache(String key) {
    Object value = redisUtil.get(key);
    if (value != null) {
        return "NULL".equals(value) ? null : value;
    }

    // 查询数据库
    value = database.query(key);
    if (value == null) {
        // 缓存空值
        redisUtil.set(key, "NULL", 5, TimeUnit.MINUTES);
    } else {
        redisUtil.set(key, value, 30, TimeUnit.MINUTES);
    }

    return value;
}

9.2 缓存击穿

问题: 热点key过期,大量请求直接打到数据库

解决方案:

  1. 热点数据永不过期
  2. 互斥锁
  3. 异步刷新
public Object getWithLock(String key) {
    Object value = redisUtil.get(key);
    if (value != null) {
        return value;
    }

    String lockKey = "lock:" + key;
    try {
        // 获取锁
        if (distributedLock.tryLock(lockKey, requestId, 10)) {
            // 双重检查
            value = redisUtil.get(key);
            if (value != null) {
                return value;
            }

            // 查询数据库
            value = database.query(key);
            redisUtil.set(key, value, 30, TimeUnit.MINUTES);
            return value;
        }
    } finally {
        distributedLock.unlock(lockKey, requestId);
    }

    // 获取锁失败,稍后重试或返回默认值
    return null;
}

9.3 缓存雪崩

问题: 大量key同时过期,数据库压力骤增

解决方案:

  1. 过期时间加随机值
  2. 多级缓存
  3. 限流降级
// 设置过期时间时加随机值
int randomTime = RandomUtil.randomInt(5, 15);
redisUtil.set(key, value, 30 + randomTime, TimeUnit.MINUTES);

9.4 内存淘汰策略

# 配置淘汰策略
maxmemory 256mb
maxmemory-policy allkeys-lru

淘汰策略说明:

  • noeviction: 不淘汰,写入报错
  • allkeys-lru: 淘汰最少使用的key
  • allkeys-lfu: 淘汰使用频率最低的key
  • allkeys-random: 随机淘汰
  • volatile-lru: 淘汰设置了TTL中最少使用的
  • volatile-lfu: 淘汰设置了TTL中使用频率最低的
  • volatile-random: 随机淘汰设置了TTL的
  • volatile-ttl: 淘汰即将过期的

10. 总结

本教程涵盖了 Redis 的核心内容:

  1. 安装部署: Linux/Mac/Windows/Docker 多种安装方式
  2. 基础命令: 5种数据类型的常用操作
  3. Java 集成: Spring Boot + RedisTemplate 完整配置
  4. 实战应用: 缓存、分布式锁、限流、排行榜、购物车、消息队列
  5. 高级特性: 持久化、发布订阅、事务、Lua脚本
  6. 问题解决: 缓存穿透、击穿、雪崩的经典解决方案

学习建议

  1. 先掌握基础命令和数据类型
  2. 动手实践每个 Java 示例
  3. 理解 Redis 的应用场景
  4. 学习 Redis 的高可用方案(主从、哨兵、集群)
  5. 关注性能优化和监控

推荐资源

  • 官方文档: https://redis.io/docs/
  • 在线测试: https://try.redis.io/
  • 命令参考: https://redis.io/commands/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值