🔴

Redis 完全学习指南

最流行的内存数据库

⚡ 内存数据库 💾 数据结构存储 🚀 百万级QPS 🔄 持久化 📡 发布订阅

📖 Redis 简介

Redis是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。支持多种数据结构,性能极高,适合高并发场景。

✨ 核心特性

  • ✅ 内存存储,性能极高
  • ✅ 支持多种数据结构
  • ✅ 持久化支持(RDB/AOF)
  • ✅ 发布订阅功能
  • ✅ 事务和Lua脚本
  • ✅ 主从复制和哨兵

🎯 应用场景

  • 💾 缓存系统(Session、热点数据)
  • 📊 排行榜和计数器
  • 🔔 实时消息通知
  • 🎫 限流和分布式锁
  • 📡 消息队列
  • 🔍 全文搜索(RediSearch)

📊 数据结构

类型 描述 典型用途
String 字符串 缓存、计数器、session
Hash 哈希表 用户信息、对象存储
List 双向链表 消息队列、最新动态
Set 无序集合 标签、好友关系
Sorted Set 有序集合 排行榜、延迟队列
Stream 消息流、日志流

🔧 Redis 安装和配置

🍎 macOS 安装

# 使用 Homebrew
brew install redis

# 启动服务
brew services start redis

# 开机自启
brew services enable redis

# 连接
redis-cli

# 测试
redis-cli ping

🐧 Linux 安装

# 下载最新版本
wget https://download.redis.io/releases/redis-7.0.tar.gz
tar xzf redis-7.0.tar.gz
cd redis-7.0

# 编译安装
make
sudo make install

# 启动Redis
redis-server

# 后台启动
redis-server --daemonize yes

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

# 启动服务
sudo systemctl start redis-server
sudo systemctl enable redis-server

🐳 Docker 部署

# 运行Redis容器
docker run -d -p 6379:6379 --name redis redis:7-alpine

# 带持久化
docker run -d -p 6379:6379 \
  -v /data/redis:/data \
  --name redis redis:7-alpine \
  redis-server --appendonly yes

# Docker Compose
version: '3.8'
services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - ./data:/data
    command: redis-server --appendonly yes

⚙️ 配置文件 redis.conf

# 网络配置
bind 127.0.0.1
port 6379
protected-mode yes

# 密码认证
requirepass yourpassword

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

# AOF配置
appendonly yes
appendfsync everysec  # always/everysec/no

# 内存管理
maxmemory 2gb
maxmemory-policy allkeys-lru

# 日志配置
loglevel notice
logfile /var/log/redis/redis.log

# 安全配置
rename-command FLUSHDB ""
rename-command FLUSHALL ""

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

💡 Redis 数据结构详解

🔤 String(字符串)

# 基本操作
SET mykey "value"
GET mykey
EXISTS mykey
DEL mykey

# 数值操作
INCR counter        # 递增1
INCRBY counter 10   # 增加10
DECR counter        # 递减1
DECRBY counter 5    # 减少5

# 位操作
SETBIT key offset value
GETBIT key offset
BITCOUNT key

# 过期时间
SET mykey "value" EX 3600  # 3600秒后过期
SET mykey "value" PX 1000  # 1000毫秒后过期
TTL mykey                   # 查看剩余时间
EXPIRE mykey 3600           # 设置过期时间

# 批量操作
MSET key1 "value1" key2 "value2"
MGET key1 key2

# 字符串操作
APPEND key "append"
GETRANGE key 0 4
STRLEN key

🗂️ Hash(哈希表)

# 基本操作
HSET user:1 name "John" age 30 email "john@example.com"
HGET user:1 name
HGETALL user:1
HKEYS user:1
HVALS user:1

# 数值操作
HINCRBY user:1 age 1
HINCRBYFLOAT user:1 balance 10.5

# 字段操作
HEXISTS user:1 name
HMSET user:1 name "Jane" city "Beijing"
HDEL user:1 age

# 获取多个字段
HMGET user:1 name age email

# 设置仅在字段不存在时
HSETNX user:1 name "Bob"

📋 List(列表)

# 基本操作(双端操作)
LPUSH queue "task1" "task2" "task3"  # 从左端推入
RPUSH queue "task4"                  # 从右端推入
LPOP queue                           # 从左端弹出
RPOP queue                           # 从右端弹出

# 查看列表
LRANGE queue 0 -1                    # 查看全部
LRANGE queue 0 2                     # 查看前3个
LINDEX queue 0                       # 获取指定索引元素
LLEN queue                           # 获取长度

# 阻塞操作(消息队列)
BRPOP queue 10                       # 阻塞10秒
BLPOP queue 10

# 移动操作
RPOPLPUSH source destination
BRPOPLPUSH source destination 10

🎯 Set(集合)

# 基本操作
SADD tags "redis" "database" "nosql"
SMEMBERS tags
SREM tags "database"
SCARD tags                           # 获取长度

# 随机获取
SRANDMEMBER tags                    # 随机一个,不移除
SPOP tags                            # 随机一个,移除

# 集合运算
SINTER tags1 tags2                   # 交集
SUNION tags1 tags2                   # 并集
SDIFF tags1 tags2                    # 差集

# 判断成员
SISMEMBER tags "redis"

# 移动元素
SMOVE tags1 tags2 "redis"

🏆 Sorted Set(有序集合)

# 基本操作
ZADD leaderboard 1000 "player1" 2000 "player2"
ZSCORE leaderboard "player1"
ZRANK leaderboard "player1"          # 从小到大排名
ZREVRANK leaderboard "player1"       # 从大到小排名

# 范围查询
ZRANGE leaderboard 0 -1 WITHSCORES
ZRANGE leaderboard 0 9               # Top 10
ZREVRANGE leaderboard 0 9             # Bottom 10

# 按分数查询
ZRANGEBYSCORE leaderboard 1000 2000
ZRANGEBYSCORE leaderboard (2000 +inf  # 大于2000

# 数值操作
ZINCRBY leaderboard 100 "player1"    # 增加分数
ZREM leaderboard "player1"

# 集合运算
ZUNIONSTORE new_leaderboard 2 leaderboard1 leaderboard2 AGGREGATE SUM
ZINTERSTORE new_leaderboard 2 leaderboard1 leaderboard2

# 计数
ZCARD leaderboard                     # 成员数量
ZCOUNT leaderboard 0 2000            # 指定分数范围的成员数

📡 Stream(流)

# 添加消息
XADD mystream * field1 "value1" field2 "value2"

# 读取消息
XREAD COUNT 10 STREAMS mystream 0
XREAD BLOCK 1000 STREAMS mystream $

# 消费者组
XGROUP CREATE mystream mygroup 0
XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >

# 确认消息
XACK mystream mygroup 123456789-0

# 待处理消息
XPENDING mystream mygroup
XINFO STREAM mystream

⚡ Redis 高级特性

📢 发布订阅

# 发布消息
PUBLISH channel "message"

# 订阅频道
SUBSCRIBE channel

# 订阅多个频道
SUBSCRIBE channel1 channel2 channel3

# 按模式订阅
PSUBSCRIBE news.*

# 取消订阅
UNSUBSCRIBE channel

# 查看订阅信息
PUBSUB CHANNELS
PUBSUB NUMSUB channel
PUBSUB NUMPAT

🔒 事务

# 基本事务
MULTI
SET key1 "value1"
SET key2 "value2"
EXEC

# 取消事务
MULTI
SET key1 "value1"
DISCARD

# 事务错误
MULTI
SET key1 "value1"
ERRORCOMMAND
EXEC  # 会忽略错误命令

# Watch(乐观锁)
WATCH key1
MULTI
INCR key1
EXEC  # 如果key1被修改,事务失败

📜 Lua脚本

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

# 带参数的脚本
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 key value

# 脚本命令
EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second

# 原子操作:限制请求频率
EVAL "
  local count = redis.call('incr', KEYS[1])
  if count == 1 then
    redis.call('expire', KEYS[1], tonumber(ARGV[1]))
  end
  return count
" 1 ip:127.0.0.1 60

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

💾 持久化策略

# RDB快照(手动)
SAVE              # 阻塞保存
BGSAVE            # 后台保存

# 配置自动保存
save 900 1
save 300 10
save 60 10000

# AOF追加文件
CONFIG SET appendonly yes
CONFIG SET appendfsync everysec

# RDB + AOF混合模式(Redis 4.0+)
CONFIG SET aof-use-rdb-preamble yes

# 性能对比
# RDB:
# - 优点: 文件小,恢复快
# - 缺点: 可能丢失数据

# AOF:
# - 优点: 数据更安全
# - 缺点: 文件大,恢复慢

# 最佳实践: 同时启用RDB和AOF

💡 持久化建议:

  • • 主从复制:主节点开启AOF
  • • 集群模式:混合模式(RDB+AOF)
  • • 监控磁盘IO,避免阻塞
  • • 定期备份RDB文件

🔄 主从复制

# 主服务器启动
redis-server --port 6379

# 从服务器配置
redis-server --port 6380 --slaveof 127.0.0.1 6379

# 动态设置从服务器
SLAVEOF 127.0.0.1 6379

# 断开复制
SLAVEOF NO ONE

# 查看复制状态
INFO replication

# 哨兵配置(sentinel.conf)
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000

# 启动哨兵
redis-sentinel sentinel.conf

✨ 复制优势:

  • • 数据冗余和容灾
  • • 读写分离提高性能
  • • 自动故障转移(哨兵)

🚀 Redis 性能优化

💾 内存优化

✅ 最佳实践

  • • 使用合适的数据结构
  • • 设置合理的过期时间
  • • 使用内存淘汰策略
  • • 压缩大数据对象
  • • 分片大集合

❌ 避免的陷阱

  • • 避免大key(>10KB)
  • • 不要存储超时不过期数据
  • • 避免内存泄漏
  • • 不要忽略内存监控

🗑️ 内存淘汰策略

# 配置最大内存
CONFIG SET maxmemory 2gb

# 淘汰策略
CONFIG SET maxmemory-policy allkeys-lru

# 策略说明
# noeviction - 不淘汰,写入报错(默认)
# allkeys-lru - 最近最少使用
# volatile-lru - 在有过期时间的key中选择
# allkeys-lfu - 最少频率使用
# volatile-lfu - 在有过期时间的key中选择
# allkeys-random - 随机淘汰
# volatile-random - 随机淘汰有过期时间的key
# volatile-ttl - 淘汰即将过期的key

# 查看内存使用
INFO memory
MEMORY USAGE key
MEMORY STATS

🔀 Redis Cluster

# 启动6个节点的集群(3主3从)
# 节点1
redis-server --port 7000 --cluster-enabled yes --cluster-config-file nodes-7000.conf

# 节点2-6类似...

# 创建集群
redis-cli --cluster create \
  127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
  127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
  --cluster-replicas 1

# 查看集群状态
redis-cli --cluster info 127.0.0.1:7000

# 添加节点
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000

# 重新分片
redis-cli --cluster reshard 127.0.0.1:7000

# 连接集群
redis-cli -c -p 7000

📊 监控和诊断

# 查看服务器信息
INFO

# 查看内存使用
INFO memory

# 查看客户端连接
CLIENT LIST
CLIENT GETNAME
CLIENT KILL ip:port

# 查看命令执行统计
INFO stats
INFO commandstats

# 慢查询日志
CONFIG SET slowlog-log-slower-than 10000
CONFIG SET slowlog-max-len 128
SLOWLOG GET 10

# 实时监控
MONITOR

# 查看配置
CONFIG GET *
CONFIG GET maxmemory

🏗️ 实战案例

💾 缓存系统实现

# 缓存策略:穿透、击穿、雪崩

# 1. 缓存穿透 - 查询不存在的key
# 解决:使用布隆过滤器或缓存空值
MULTI
SET cache:user:999 "{}" EX 300
EXEC

# 2. 缓存击穿 - 热点key过期
# 解决:互斥锁
SETNX lock:key "1" EX 10
# 获取锁后查询数据库并更新缓存
DEL lock:key

# 3. 缓存雪崩 - 大量key同时过期
# 解决:随机过期时间
SET cache:data "value" EX {300 + random(0, 60)}

# 更新缓存模式
# Cache-Aside(旁路缓存)
1. 查询缓存
2. 缓存未命中则查询数据库
3. 将结果写入缓存

# 缓存预热
KEYS cache:hot_*
# 启动时批量加载热点数据到缓存

🔒 分布式锁实现

# Lua脚本实现原子加锁
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local lock_timeout = tonumber(ARGV[2])
local result = redis.call('SET', lock_key, lock_value, 'NX', 'EX', lock_timeout)
return result

# Python实现
import redis
import uuid
import time

class RedisLock:
    def __init__(self, redis_client, key, timeout=10):
        self.client = redis_client
        self.key = key
        self.timeout = timeout
        self.identifier = uuid.uuid4().hex
    
    def acquire(self):
        return self.client.set(
            self.key, 
            self.identifier, 
            ex=self.timeout, 
            nx=True
        )
    
    def release(self):
        lua = """
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end
        """
        return self.client.eval(lua, 1, self.key, self.identifier)

# Redlock算法(多Redis实例)
# 多个Redis节点获得锁,获得超过半数即成功

🏆 排行榜实现

# 游戏排行榜
ZADD game:leaderboard 1000 "player1"
ZADD game:leaderboard 2000 "player2" 3000 "player3"

# Top 10玩家
ZREVRANGE game:leaderboard 0 9 WITHSCORES

# 玩家排名
ZREVRANK game:leaderboard "player1"

# 玩家得分
ZSCORE game:leaderboard "player1"

# 增加分数
ZINCRBY game:leaderboard 100 "player1"

# 区间查询(1000-3000分的玩家)
ZRANGEBYSCORE game:leaderboard 1000 3000

# 分段统计(铜牌、银牌、金牌)
ZCOUNT game:leaderboard 0 1000     # 铜牌
ZCOUNT game:leaderboard 1001 2000   # 银牌
ZCOUNT game:leaderboard 2001 +inf  # 金牌

⏱️ 限流器实现

# 1. 固定窗口限流
MULTI
INCR rate_limit:{ip}:{time}
EXPIRE rate_limit:{ip}:{time} 60
EXEC

# 2. 滑动窗口限流
ZADD rate:{ip} {timestamp} {timestamp}
ZREMRANGEBYSCORE rate:{ip} 0 {timestamp-60}
ZCARD rate:{ip}

# 3. 漏斗算法(Lua脚本)
local function funnel(key, capacity, leak_rate)
    local now = redis.call('TIME')[1]
    local data = redis.call('HGETALL', key)
    local last_check = tonumber(data[1]) or now
    local tokens = tonumber(data[2]) or capacity
    
    local elapsed = now - last_check
    tokens = math.min(capacity, tokens + elapsed * leak_rate)
    
    if tokens >= 1 then
        tokens = tokens - 1
        redis.call('HMSET', key, 'last_check', now, 'tokens', tokens)
        return true
    else
        return false
    end
end

# 4. 令牌桶算法
# 见附录中的完整Lua实现

📋 Redis 最佳实践总结

✅ 使用原则

  • • 选择合适的过期时间
  • • 避免大key(>10KB)
  • • 使用批量操作提高性能
  • • 合理使用连接池
  • • 监控内存使用情况
  • • 启用持久化保护数据

⚡ 性能优化

  • • 使用Pipeline减少网络往返
  • • 合理使用内存淘汰策略
  • • 避免在高并发下使用KEYS
  • • 使用SCAN代替KEYS
  • • 主从复制提高读性能
  • • 集群模式提高可用性

🔒 安全措施

  • • 设置密码认证
  • • 限制网络访问
  • • 禁用危险命令(FLUSHALL等)
  • • 使用SSL/TLS加密
  • • 定期备份持久化文件
  • • 监控异常访问模式

📊 运维监控

  • • 监控内存使用率
  • • 跟踪慢查询日志
  • • 监控复制延迟
  • • 定期检查过期key
  • • 使用Redis-cli进行健康检查
  • • 建立告警机制
← 返回数据库首页