最流行的内存数据库
Redis是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。支持多种数据结构,性能极高,适合高并发场景。
| 类型 | 描述 | 典型用途 |
|---|---|---|
| String | 字符串 | 缓存、计数器、session |
| Hash | 哈希表 | 用户信息、对象存储 |
| List | 双向链表 | 消息队列、最新动态 |
| Set | 无序集合 | 标签、好友关系 |
| Sorted Set | 有序集合 | 排行榜、延迟队列 |
| Stream | 流 | 消息流、日志流 |
# 使用 Homebrew
brew install redis
# 启动服务
brew services start redis
# 开机自启
brew services enable redis
# 连接
redis-cli
# 测试
redis-cli ping
# 下载最新版本
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
# 运行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
# 网络配置
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
# 基本操作
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
# 基本操作
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"
# 基本操作(双端操作)
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
# 基本操作
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"
# 基本操作
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 # 指定分数范围的成员数
# 添加消息
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
# 发布消息
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脚本
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
💡 持久化建议:
# 主服务器启动
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
✨ 复制优势:
# 配置最大内存
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
# 启动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实现