跳转至

Redis 基础

Redis 是一个基于内存的高性能键值存储数据库,常用于缓存、会话管理和消息队列。它的核心优势在于读写速度极快(单线程模型 + IO多路复用),数据存储在内存中,支持持久化到磁盘。

五大数据类型

Redis 提供 5 种基本数据结构,每种底层实现优化了空间和时间复杂度(例如 String 用 SDS 动态字符串,O(1) 获取长度)。

类型 描述 常见命令 示例场景
String 最简单类型,支持字符串/数字/二进制 SET key value, GET key, INCR key 缓存对象、计数器
List 双向链表,支持队列/栈 LPUSH/RPUSH, LPOP/RPOP, LRANGE 消息队列(如 RPUSH + LPOP)
Set 无序集合,自动去重 SADD key member, SMEMBERS key, SINTER 标签系统、交集运算
Hash 键值对映射表,像 Map HSET key field value, HGETALL key 存储对象(如用户信息)
Zset 有序集合,按分数排序 ZADD key score member, ZRANGE 排行榜、优先级队列

集成

Java 集成示例

在 Java 项目中使用 Jedis(轻量客户端)或 Spring Data Redis。Jedis 直连简单,但生产用连接池避免频繁创建连接。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisExample {
    private static final JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost", 6379);

    public static void main(String[] args) {
        try (Jedis jedis = pool.getResource()) {
            // String 操作:缓存字符串
            jedis.set("user:1:name", "Alice");  // SET 命令,O(1)
            String name = jedis.get("user:1:name");  // GET,O(1)
            System.out.println("Name: " + name);  // 输出: Alice

            // List 操作:简单队列
            jedis.rpush("messages", "msg1", "msg2");  // 右推,队列尾部添加
            String msg = jedis.lpop("messages");  // 左弹出,O(1),输出: msg1
            System.out.println("Message: " + msg);

            // Hash 操作:存储对象
            jedis.hset("user:1", "age", "25");  // field-value,O(1)
            String age = jedis.hget("user:1", "age");
            System.out.println("Age: " + age);  // 输出: 25
        }
    }
}

Spring Boot集成Redis

Spring Boot 通过 Spring Data Redis 轻松集成 Redis,支持 Lettuce 或 Jedis 客户端,默认使用 Lettuce(非阻塞)。这简化了连接管理、序列化和操作,生产中常用于缓存加速数据库查询。

集成步骤 1. 添加依赖:在 pom.xml 中引入 spring-boot-starter-data-redis(自动包含 Lettuce)。 2. 配置连接:在 application.yml 设置 Redis 地址、密码等。 3. 使用 RedisTemplate 操作数据,支持泛型键值。

为什么这样?Spring Boot 自动配置 LettuceConnectionFactory 和 RedisTemplate,避免手动管理连接池;默认序列化用 JDK,但建议自定义 JacksonSerializer 处理 JSON 对象。

配置文件示例

spring:
  data:
    redis:
      host: localhost  # Redis 主机
      port: 6379       # 默认端口
      password:        # 可选密码
      lettuce:
        pool:
          max-active: 8   # 连接池最大连接数,为什么?并发场景下复用连接提高性能
          max-idle: 8
          min-idle: 0

关键:连接池配置防止连接耗尽,Lettuce 支持读写分离(主从)。 ​

Redis 配置类

import com.fasterxml.jackson.annotation.JsonAutoDetect;
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);

        // 键序列化:字符串
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 值序列化:JSON(为什么?支持复杂对象,如 User POJO)
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
}

关键语句:Jackson2JsonRedisSerializer 确保对象自动 JSON 序列化,避免 JDK 默认序列化问题(如不可读字节)。

服务层使用示例

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;

    public void cacheUser(String key, Object user) {
        redisTemplate.opsForValue().set(key, user, 60, TimeUnit.SECONDS);  // 缓存 60s,为什么?防止脏数据,过期自动清理
    }

    public Object getUser(String key) {
        return redisTemplate.opsForValue().get(key);  // String 类型操作,O(1)
    }

    // Hash 示例:存储用户属性
    public void hashUser(String hashKey, String field, Object value) {
        redisTemplate.opsForHash().put(hashKey, field, value);
    }
}

在 Controller 注入 UserService 调用。实际项目:结合 @Cacheable 注解简化,或用 Redisson 分布式锁。

问题

Redis 为什么这么快?

Redis 的高性能源于纯内存存储、单线程事件循环 + IO 多路复用、高效数据结构和简洁协议设计,这些机制让它单机轻松达到 10 万+ QPS。 ​ + 纯内存操作 + Redis 将所有数据存于内存,读写速度达纳秒级,避免了磁盘 I/O(毫秒级)的瓶颈。传统数据库如 MySQL 需频繁访问磁盘,而 Redis 直接内存操作,相差数个数量级。 + 为什么快?内存访问无需寻道/旋转延迟,结合非阻塞 IO,命令执行时间极短(主要耗时在网络)。 + 单线程 + IO 多路复用 + Redis 主线程单线程执行命令,避免多线程上下文切换(时间开销 ~1μs/次)和锁竞争。使用 epoll 等 IO 多路复用(Reactor 模式),单线程监听多个 socket,只处理就绪事件(如 accept/read/write)。 + 为什么高效?文件事件处理器将 socket 事件分发给处理器,一个线程并发处理万级连接,无阻塞、无额外线程开销(对比多线程模型的 CPU 浪费)。 + 优化数据结构 + 支持 String/List/Set/Hash/ZSet 等,底层用 ziplist/quicklist/skiplist 等紧凑编码,动态选择节省空间并 O(1)/O(log N) 操作。 + 为什么设计如此?平衡性能与内存,如小集合用 ziplist 避免指针开销,大数据自动转 hashtable。 + 简洁 RESP 协议 + RESP(REdis Serialization Protocol)二进制安全、解析快,客户端/服务端序列化开销小,提升交互速度。

Redis 和 Memcached 的区别和共同点

Redis 和 Memcached 都是高性能内存缓存系统,用于加速数据访问,但 Redis 功能更丰富,Memcached 更专注简单键值存储。 ​ 共同点

  • 内存存储:两者数据驻留在内存,读写速度纳秒级,远超磁盘数据库,适合缓存热点数据。
  • 高性能:QPS 可达 10 万+,支持过期策略(TTL),用于会话、页面缓存等场景。
  • 分布式支持:均可水平扩展,但需客户端分片(Memcached)或原生集群(Redis)。 ​

为什么共同?内存操作避免 I/O 瓶颈,缓存命中率高时显著降低后端压力。

区别对比

维度 Redis Memcached
数据类型 丰富(String/List/Set/Hash/ZSet 等)javaguide​ 仅简单 K/V(字符串)javaguide​
持久化 支持 RDB/AOF,重启恢复数据javaguide​ 无,重启丢失全部数据javaguide​
集群 原生支持(3.0+ 哈希槽)javaguide​ 客户端分片(如一致性哈希)javaguide​
线程模型 单线程 + IO 多路复用(6.0+ 读写多线程)javaguide​ 多线程,非阻塞 IOjavaguide​
额外特性 发布订阅、Lua 脚本、事务、Geo 等javaguide​ 无,仅基础 Get/Set/CASjavaguide​
内存效率 复杂对象高效(如 Hash ziplist),支持淘汰策略 简单 K/V 更省内存,大对象优于 Rediscnblogs​
协议 RESP(二进制安全)javaguide​ 文本协议csdn​

为什么这些区别? Redis 设计为 NoSQL 数据库,支持复杂场景(如排行榜用 ZSet);Memcached 专注纯缓存,轻量但功能单一。Java 项目中,Spring Data Redis 集成更成熟,推荐 Redis 作为默认选择。

缓存读写策略详解