【Redis】数据结构讲解

Redis 从入门到实战


一、Redis 是什么?

Redis(Remote Dictionary Server)本质是:

一个 基于内存的高性能 Key-Value 数据库

你可以把它理解成:

text 复制代码
Redis ≈ 一个超快的 HashMap(但功能远不止)

Redis 的核心特点

  • 超高性能(10万+ QPS)
  • 基于内存(读写极快)
  • 支持持久化(不会完全丢数据)
  • 支持丰富数据结构
  • 支持分布式(集群)

二、Redis 为什么这么快?(面试高频)

核心原因 5 点:

1️⃣ 基于内存

不走磁盘 IO,速度直接起飞


2️⃣ 单线程模型(重点)

Redis 是单线程,但它:

避免了线程切换开销 + 锁竞争


3️⃣ IO 多路复用

底层用类似:

  • epoll(Linux)
  • select

一个线程处理多个连接


4️⃣ 数据结构简单高效

后面会讲(核心)


5️⃣ C语言实现

更贴近底层,性能更高


三、Redis 核心数据结构(重点中的重点)

Redis ≠ 只会 String!

它有 5 大核心结构:


1️⃣ String(最常用)

bash 复制代码
SET name "Tom"
GET name

特点:

  • 可以存字符串 / 数字 / JSON
  • 最大 512MB

使用场景:

  • 缓存
  • 计数器(点赞数)
bash 复制代码
INCR likes

2️⃣ Hash(对象存储)

bash 复制代码
HSET user:1 name Tom age 20
HGET user:1 name

类似 Java:

java 复制代码
Map<String, Object>

使用场景:

  • 存用户信息
  • 商品信息

3️⃣ List(列表)

bash 复制代码
LPUSH list 1 2 3
RPOP list

特点:

  • 有序
  • 可重复

使用场景:

  • 消息队列(简单版)
  • 最新动态

4️⃣ Set(去重集合)

bash 复制代码
SADD users 1 2 3
SMEMBERS users

特点:

  • 无序
  • 不重复

使用场景:

  • 去重
  • 标签系统

5️⃣ ZSet(有序集合 )

bash 复制代码
ZADD score 100 user1
ZADD score 90 user2
ZREVRANGE score 0 1

特点:

  • 自动排序(按 score)

使用场景:

  • 排行榜(核心场景)

四、Redis 底层数据结构(进阶)

面试加分项!

Redis类型 底层结构
String SDS(简单动态字符串)
List quicklist
Hash hashTable / ziplist
Set hashTable
ZSet 跳表(skiplist)

什么是跳表(SkipList)?

类似"多层索引的链表"

优势:

  • 查询效率接近二叉树
  • 插入删除比红黑树简单

五、Redis 如何使用(实战)


1️⃣ 安装(简单了解)

Linux:

bash 复制代码
apt install redis

启动:

bash 复制代码
redis-server

客户端:

bash 复制代码
redis-cli

2️⃣ Java 操作 Redis(重点)

常用客户端:

  • Jedis
  • Lettuce
  • Spring Data Redis(最常用 )

Spring Boot 示例

依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379

使用

java 复制代码
@Autowired
private StringRedisTemplate redisTemplate;

public void test() {
    redisTemplate.opsForValue().set("name", "Tom");
    String name = redisTemplate.opsForValue().get("name");
}

六、Redis 常见使用场景(面试必问)


1️⃣ 缓存(最核心)

text 复制代码
数据库 → Redis → 用户

大幅降低数据库压力


2️⃣ 分布式锁(高频)

bash 复制代码
SET lock_key value NX EX 10

代码示例:

复制代码
@Service
public class OrderService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LOCK_KEY = "order:lock:";
    private static final long LOCK_EXPIRE = 10L; // 秒
    
    public boolean createOrder(String orderId) {
        // 1. 尝试获取锁
        String lockKey = LOCK_KEY + orderId;
        String value = UUID.randomUUID().toString(); // 唯一值
        
        // SET lock_key unique_value NX EX 10
        Boolean success = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, value, LOCK_EXPIRE, TimeUnit.SECONDS);
        
        if (Boolean.TRUE.equals(success)) {
            try {
                // 2. 获取锁成功,执行业务逻辑
                return doCreateOrder(orderId);
            } finally {
                // 3. 释放锁(使用Lua脚本保证原子性)
                String luaScript = 
                    "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "return redis.call('del', KEYS[1]) " +
                    "else return 0 end";
                redisTemplate.execute(
                    new DefaultRedisScript<>(luaScript, Long.class),
                    Collections.singletonList(lockKey),
                    value
                );
            }
        }
        // 获取锁失败
        return false;
    }
}

防止重复提交、库存超卖


3️⃣ 排行榜

ZSet 实现:

bash 复制代码
ZADD rank 100 user1

代码示例:

复制代码
@Service
public class RankService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String RANK_KEY = "game:rank";
    
    // 增加用户分数
    public void addScore(String userId, double score) {
        // ZADD rank 100 user1
        redisTemplate.opsForZSet().add(RANK_KEY, userId, score);
    }
    
    // 获取Top N
    public List<UserScore> getTopN(int n) {
        // ZREVRANGE rank 0 n-1 WITHSCORES
        Set<ZSetOperations.TypedTuple<String>> set = 
            redisTemplate.opsForZSet().reverseRangeWithScores(RANK_KEY, 0, n - 1);
        
        List<UserScore> result = new ArrayList<>();
        set.forEach(tuple -> {
            result.add(new UserScore(tuple.getValue(), tuple.getScore()));
        });
        return result;
    }
    
    // 获取用户排名
    public Long getUserRank(String userId) {
        // ZREVRANK rank user1 (返回从0开始的排名)
        return redisTemplate.opsForZSet().reverseRank(RANK_KEY, userId);
    }
}

4️⃣ 计数器

bash 复制代码
INCR page_view

代码示例:

复制代码
@Service
public class CounterService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 文章阅读量+1
    public Long incrementViewCount(Long articleId) {
        String key = "article:view:" + articleId;
        // INCR page_view
        return redisTemplate.opsForValue().increment(key);
    }
    
    // 获取阅读量
    public Long getViewCount(Long articleId) {
        String key = "article:view:" + articleId;
        String value = redisTemplate.opsForValue().get(key);
        return value == null ? 0L : Long.parseLong(value);
    }
    
    // 批量增加(如点赞+10)
    public Long incrementBy(Long articleId, long delta) {
        String key = "article:like:" + articleId;
        return redisTemplate.opsForValue().increment(key, delta);
    }
}

5️⃣ 消息队列(简单)

List:

bash 复制代码
LPUSH queue msg
RPOP queue

代码示例:

复制代码
@Component
public class SimpleMessageQueue {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String QUEUE_KEY = "task:queue";
    
    // 生产者:发送消息
    public void sendMessage(String message) {
        // LPUSH queue msg
        redisTemplate.opsForList().leftPush(QUEUE_KEY, message);
    }
    
    // 消费者:阻塞获取消息
    public String receiveMessage() {
        // BRPOP queue 0 (0表示无限等待)
        List<String> result = redisTemplate.execute(
            (RedisCallback<List<String>>) connection -> {
                byte[][] keys = new byte[][]{QUEUE_KEY.getBytes()};
                List<byte[]> bytesList = connection.bRPop(0, keys);
                if (bytesList != null && !bytesList.isEmpty()) {
                    return bytesList.stream()
                        .map(String::new)
                        .collect(Collectors.toList());
                }
                return null;
            }
        );
        return result != null ? result.get(1) : null; // result[0]=key, result[1]=value
    }
    
    // 非阻塞获取
    public String popMessage() {
        // RPOP queue
        return redisTemplate.opsForList().rightPop(QUEUE_KEY);
    }
}

七、Redis 易错点


1. 缓存穿透

查不存在的数据

解决:

  • 布隆过滤器
  • 缓存空值

2. 缓存击穿

热点 key 突然失效

解决:

  • 加锁
  • 永不过期 + 异步更新

3. 缓存雪崩

大量 key 同时过期

解决:

  • 随机过期时间
  • 多级缓存

4. 数据一致性问题

Redis 和数据库不一致

解决:

  • 先更新数据库再删缓存(常用)
  • 双删策略

八、Redis 持久化机制(面试重点)


1️⃣ RDB(快照)

定时保存数据

优点:

  • 恢复快

缺点:

  • 可能丢数据

2️⃣ AOF(日志)

记录每条命令

优点:

  • 更安全

缺点:

  • 文件大

面试回答模板

"生产一般 AOF + RDB 混合使用"


九、Redis 面试高频问题总结


1. Redis 为什么快?

内存 + 单线程 + IO多路复用


2. Redis 和 MySQL 区别?

Redis MySQL
内存 磁盘
相对慢
KV 关系型

3. Redis 是线程安全吗?

单线程 → 天然线程安全


4. Redis 如何实现分布式锁?

SET NX EX


5. Redis 会丢数据吗?

会(看持久化策略)


十、总结(建议背下来)

Redis 核心可以总结为:

text 复制代码
1. 五大数据结构(String/Hash/List/Set/ZSet)
2. 三大缓存问题(穿透/击穿/雪崩)
3. 两种持久化(RDB/AOF)
4. 一个核心优势(快)
相关推荐
阿Y加油吧1 小时前
力扣打卡day06——滑动窗口最大值、最小覆盖子串
数据结构·算法·leetcode
沉鱼.441 小时前
日期题目集
数据结构·算法
ID_180079054731 小时前
小红书笔记详情 API 接口系列 + 标准 JSON 返回参考(完整版)
数据库·笔记·json
wertyuytrewm2 小时前
用Python实现自动化的Web测试(Selenium)
jvm·数据库·python
Book思议-2 小时前
【数据结构考研真题】链表题
c语言·数据结构·算法·链表·408·计算机考研
⁤⁢初遇2 小时前
数据结构---排序
数据结构·算法·排序算法
我真会写代码2 小时前
Java事务核心原理与实战避坑指南
java·开发语言·数据库
Gauss松鼠会2 小时前
【GaussDB】GaussDB如何创建和管理序列、定时任务
数据库·性能优化·database·gaussdb
Forget_85502 小时前
RHEL——NoSQL集群技术
数据库·nosql