Redis 缓存入门教程 — 高性能缓存必备

适用人群:后端开发者、全栈开发者
学习时长:约1-2天(每天3小时)
Redis版本:7.0+
重要程度:★★★★☆(性能优化必备)

一、Redis 是什么?

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

特点说明
性能10万+ QPS(每秒查询数)
数据结构String、List、Hash、Set、Sorted Set
持久化RDB快照 + AOF日志
主要用途缓存、会话存储、排行榜、消息队列
适用场景高并发读取、计数器、实时排行

二、安装与配置

2.1 安装

# Docker(推荐)
docker run -d --name redis -p 6379:6379 redis:7-alpine

# Ubuntu
sudo apt install redis-server

# CentOS
sudo yum install redis
sudo systemctl start redis
sudo systemctl enable redis

# macOS
brew install redis
brew services start redis

# 验证
redis-cli ping  # 应返回 PONG

2.2 配置

# /etc/redis/redis.conf

# 绑定地址
bind 0.0.0.0

# 密码
requirepass your_password

# 最大内存
maxmemory 256mb

# 内存淘汰策略
maxmemory-policy allkeys-lru

# 持久化
appendonly yes
appendfsync everysec


三、基本数据类型

3.1 String(字符串)

# 设置
SET name "张三"
SET counter 100
SET token "abc123" EX 3600  # 设置并设置过期时间(秒)

# 获取
GET name  # "张三"

# 自增/自减
INCR counter           # 101
INCRBY counter 10      # 111
DECR counter           # 110
DECRBY counter 5       # 105

# 批量操作
MSET key1 "val1" key2 "val2"
MGET key1 key2

# 不存在时设置(分布式锁)
SETNX lock "value"

# 追加
APPEND name " 先生"   # "张三 先生"

# 过期
EXPIRE name 3600       # 3600秒后过期
TTL name               # 查看剩余时间

3.2 List(列表)

# 添加元素
LPUSH mylist "c" "b" "a"  # 从左边添加
RPUSH mylist "d" "e"       # 从右边添加

# 获取元素
LINDEX mylist 0            # 获取指定索引
LRANGE mylist 0 -1         # 获取所有元素
LLEN mylist                # 获取长度

# 弹出元素
LPOP mylist                # 从左边弹出
RPOP mylist                # 从右边弹出

# 阻塞弹出(消息队列)
BLPOP mylist 30            # 阻塞30秒等待元素

# 应用场景:消息队列
LPUSH queue "task1" "task2" "task3"
BRPOP queue 0              # 阻塞等待任务

3.3 Hash(哈希)

# 设置字段
HSET user:1 name "张三"
HSET user:1 age 25
HSET user:1 email "zhangsan@example.com"

# 获取字段
HGET user:1 name           # "张三"
HGETALL user:1             # 获取所有字段和值

# 批量设置
HMSET user:2 name "李四" age 30 email "lisi@example.com"

# 判断字段存在
HEXISTS user:1 name        # 1 (存在)

# 删除字段
HDEL user:1 email

# 自增
HINCRBY user:1 age 1       # 26

# 应用场景:用户信息存储

3.4 Set(集合)

# 添加元素
SADD myset "a" "b" "c" "d"

# 获取所有元素
SMEMBERS myset

# 判断元素存在
SISMEMBER myset "a"        # 1 (存在)

# 集合操作
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
SINTER set1 set2           # 交集:["b", "c"]
SUNION set1 set2           # 并集:["a", "b", "c", "d"]
SDIFF set1 set2            # 差集:["a"]

# 随机获取
SRANDMEMBER myset 2        # 随机获取2个元素

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

3.5 Sorted Set(有序集合)

# 添加元素(带分数)
ZADD leaderboard 100 "张三"
ZADD leaderboard 85 "李四"
ZADD leaderboard 92 "王五"

# 获取排名(从高到低)
ZREVRANGE leaderboard 0 2 WITHSCORES  # 前3名

# 获取分数
ZSCORE leaderboard "张三"  # 100

# 获取排名
ZREVRANK leaderboard "张三"  # 0 (第一名)

# 自增分数
ZINCRBY leaderboard 5 "李四"  # 90

# 范围查询
ZRANGEBYSCORE leaderboard 80 100  # 分数80-100的成员

# 应用场景:排行榜


四、PHP 中使用 Redis

<?php
// 安装:composer require predis/predis

use Predis\Client;

$redis = new Client([
    'scheme' => 'tcp',
    'host'   => '127.0.0.1',
    'port'   => 6379,
    'password' => 'your_password',
]);

// String
$redis->set('name', '张三');
$name = $redis->get('name');
$redis->incr('counter');
$redis->expire('name', 3600);

// Hash
$redis->hset('user:1', 'name', '张三');
$redis->hset('user:1', 'age', 25);
$user = $redis->hgetall('user:1');

// List
$redis->lpush('queue', 'task1');
$redis->rpush('queue', 'task2');
$task = $redis->blpop('queue', 30);

// Set
$redis->sadd('tags', 'php', 'redis', 'mysql');
$tags = $redis->smembers('tags');

// Sorted Set(排行榜)
$redis->zadd('leaderboard', 100, '张三');
$redis->zadd('leaderboard', 85, '李四');
$top = $redis->zrevrange('leaderboard', 0, 2, true);

// 缓存模式
function getCache($key, $ttl, $callback) {
    global $redis;
    
    $data = $redis->get($key);
    if ($data) {
        return json_decode($data, true);
    }
    
    // 缓存未命中,从数据库获取
    $data = $callback();
    $redis->setex($key, $ttl, json_encode($data));
    return $data;
}

// 使用示例
$users = getCache('users:all', 3600, function() {
    // 从数据库查询
    return User::all()->toArray();
});


五、Python 中使用 Redis

# 安装:pip install redis

import redis
import json

# 连接
r = redis.Redis(
    host='localhost',
    port=6379,
    password='your_password',
    decode_responses=True  # 自动解码
)

# String
r.set('name', '张三')
name = r.get('name')
r.incr('counter')
r.expire('name', 3600)

# Hash
r.hset('user:1', mapping={'name': '张三', 'age': 25})
user = r.hgetall('user:1')

# List
r.lpush('queue', 'task1', 'task2')
task = r.blpop('queue', timeout=30)

# Set
r.sadd('tags', 'python', 'redis', 'mysql')
tags = r.smembers('tags')

# Sorted Set
r.zadd('leaderboard', {'张三': 100, '李四': 85, '王五': 92})
top = r.zrevrange('leaderboard', 0, 2, withscores=True)

# 缓存装饰器
def cache(key_template, ttl=3600):
    def decorator(func):
        def wrapper(*args, **kwargs):
            key = key_template.format(*args, **kwargs)
            data = r.get(key)
            if data:
                return json.loads(data)
            result = func(*args, **kwargs)
            r.setex(key, ttl, json.dumps(result, ensure_ascii=False))
            return result
        return wrapper
    return decorator

@cache('user:{0}', ttl=3600)
def get_user(user_id):
    # 从数据库查询
    return User.query.get(user_id).to_dict()


六、Go 中使用 Redis

// 安装:go get github.com/redis/go-redis/v9

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/redis/go-redis/v9"
)

var rdb *redis.Client

func init() {
    rdb = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "your_password",
        DB:       0,
    })
}

func main() {
    ctx := context.Background()
    
    // String
    rdb.Set(ctx, "name", "张三", time.Hour)
    name, _ := rdb.Get(ctx, "name").Result()
    fmt.Println(name)
    
    // Hash
    rdb.HSet(ctx, "user:1", "name", "张三", "age", 25)
    user, _ := rdb.HGetAll(ctx, "user:1").Result()
    fmt.Println(user)
    
    // List
    rdb.LPush(ctx, "queue", "task1", "task2")
    task, _ := rdb.BRPop(ctx, 0, "queue").Result()
    fmt.Println(task)
    
    // Sorted Set
    rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 100, Member: "张三"})
    rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 85, Member: "李四"})
    top, _ := rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, 2).Result()
    for _, z := range top {
        fmt.Printf("%s: %.0f\n", z.Member, z.Score)
    }
}


七、Java 中使用 Redis

// Spring Boot + Redis

// pom.xml 添加依赖
// <dependency>
//     <groupId>org.springframework.boot</groupId>
//     <artifactId>spring-boot-starter-data-redis</artifactId>
// </dependency>

// application.yml
// spring:
//   data:
//     redis:
//       host: localhost
//       port: 6379
//       password: your_password

// 使用RedisTemplate
@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // String操作
    public void set(String key, Object value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }
    
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    
    // 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);
    }
    
    // List操作
    public void lPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }
    
    // Sorted Set(排行榜)
    public void zAdd(String key, String member, double score) {
        redisTemplate.opsForZSet().add(key, member, score);
    }
    
    public Set<Object> zRevRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }
}


八、常见应用场景

8.1 缓存

# 缓存穿透:查询不存在的数据
# 解决:缓存空值或使用布隆过滤器

# 缓存击穿:热点key过期
# 解决:互斥锁或永不过期

# 缓存雪崩:大量key同时过期
# 解决:随机过期时间

8.2 分布式锁

import redis
import uuid
import time

class RedisLock:
    def __init__(self, redis_client, key, expire=10):
        self.redis = redis_client
        self.key = f"lock:{key}"
        self.value = str(uuid.uuid4())
        self.expire = expire
    
    def acquire(self, timeout=10):
        end = time.time() + timeout
        while time.time() < end:
            if self.redis.set(self.key, self.value, nx=True, ex=self.expire):
                return True
            time.sleep(0.1)
        return False
    
    def release(self):
        if self.redis.get(self.key) == self.value:
            self.redis.delete(self.key)

# 使用
lock = RedisLock(r, 'order:123')
if lock.acquire():
    try:
        # 执行业务逻辑
        pass
    finally:
        lock.release()

8.3 排行榜

def update_score(user_id, score):
    r.zincrby('leaderboard', score, user_id)

def get_top_n(n=10):
    return r.zrevrange('leaderboard', 0, n-1, withscores=True)

def get_rank(user_id):
    return r.zrevrank('leaderboard', user_id)

8.4 计数器

# 页面访问量
def incr_page_views(page_id):
    key = f"page:views:{page_id}"
    return r.incr(key)

# 获取访问量
def get_page_views(page_id):
    key = f"page:views:{page_id}"
    return int(r.get(key) or 0)


九、最佳实践

✅ 设置合理的过期时间
✅ 使用连接池
✅ 避免存储大Value(>10KB)
✅ 使用Pipeline批量操作
✅ 监控内存使用
✅ 配置持久化(AOF)
✅ 使用Sentinel或Cluster高可用
✅ Key命名规范:业务:对象:ID:属性


学习建议

  1. 先掌握5种数据类型,理解各自的适用场景
  2. 理解缓存策略,缓存穿透、击穿、雪崩的解决方案
  3. 学会在各语言中使用Redis客户端
  4. 实践常见场景:缓存、排行榜、分布式锁
  5. 了解Redis集群,生产环境高可用方案
返回首页