Redis入门与实践
资源下载: Redis学习资源包 密码: jdwa
Tips
本教程基于Redis 6.0及以上版本,同时兼顾Windows和Linux/macOS环境,适合Redis初学者及有一定使用经验的开发者。
1. Redis简介
Redis(Remote Dictionary Server)是一个开源的、基于内存的键值数据库,以其超高性能、灵活性和丰富的数据结构支持而闻名。
1.1 Redis的主要特点
- 高性能:基于内存操作,读写性能极高(10万/秒级别)
- 丰富的数据类型:支持String、List、Hash、Set、Sorted Set等多种数据类型
- 原子性操作:单个操作是原子性的,也支持批量操作
- 持久化机制:支持RDB和AOF两种持久化方式
- 主从复制:支持主从复制和读写分离
- 高可用:通过Redis Sentinel和Redis Cluster提供高可用解决方案
- 事务支持:支持事务操作,但不支持回滚
- Lua脚本:支持Lua脚本执行复杂操作
1.2 Redis的应用场景
- 缓存系统:存储热点数据,减轻数据库压力
- 计数器:如网站访问量计数、用户点赞数
- 消息队列:简单的发布订阅(pub/sub)或消息队列场景
- 排行榜:利用Sorted Set实现如游戏排行榜
- 会话存储:Web应用Session共享
- 分布式锁:利用SETNX命令实现简单的分布式锁
- 社交网络:如粉丝列表、关注列表等
- 地理位置应用:使用GEO命令实现附近的人、商店等功能
2. Redis安装与配置
2.1 Windows环境安装
虽然Redis官方不提供Windows版本,但可以使用Microsoft开源的Windows版Redis:
下载地址:https://github.com/microsoftarchive/redis/releases
下载zip文件并解压到指定目录,如
C:\Redis
运行Redis服务器:
C:\Redis\redis-server.exe
运行Redis客户端:
C:\Redis\redis-cli.exe
安装为Windows服务(可选):
C:\Redis\redis-server.exe --service-install redis.windows.conf
2.2 Linux环境安装
Ubuntu/Debian系统:
sudo apt update
sudo apt install redis-server
启动Redis服务:
sudo systemctl start redis-server
CentOS/RHEL系统:
sudo yum install epel-release
sudo yum install redis
sudo systemctl start redis
2.3 源码编译安装
最新版本的Redis需要通过源码编译安装:
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
tar xzf redis-6.2.6.tar.gz
cd redis-6.2.6
make
sudo make install
2.4 基本配置
Redis的配置文件(redis.conf)中有许多重要参数,以下是几个关键配置:
端口:默认是6379
port 6379
绑定IP:默认仅本地访问,生产环境应限制IP访问
bind 127.0.0.1
密码保护:设置AUTH密码
requirepass yourpassword
内存限制:设置最大内存使用量
maxmemory 1gb
内存策略:当达到内存限制时的淘汰策略
maxmemory-policy allkeys-lru
持久化配置:RDB持久化
save 900 1 # 900秒内有1个更改 save 300 10 # 300秒内有10个更改 save 60 10000 # 60秒内有10000个更改
AOF持久化:
appendonly yes appendfsync everysec
3. Redis数据类型与命令
Redis提供了丰富的数据类型,以下介绍各种数据类型的基本用法和常用命令。
3.1 String(字符串)
最基本的数据类型,可以存储字符串、整数或浮点数。
基本命令:
# 设置键值
SET key value
# 设置带过期时间的键值(10秒)
SET key value EX 10
# 获取值
GET key
# 递增
INCR key
# 递增指定量
INCRBY key increment
# 删除
DEL key
# 检查存在
EXISTS key
# 设置过期时间(秒)
EXPIRE key seconds
使用场景:
- 存储用户信息、配置信息
- 计数器(如页面访问量)
- 缓存对象(JSON字符串)
- 实现分布式锁(SETNX)
3.2 List(列表)
有序的字符串列表,基于双向链表实现,支持两端操作。
基本命令:
# 左侧添加
LPUSH key value1 [value2]
# 右侧添加
RPUSH key value1 [value2]
# 左侧弹出
LPOP key
# 右侧弹出
RPOP key
# 获取列表元素
LRANGE key start stop
# 获取列表长度
LLEN key
# 根据索引获取元素
LINDEX key index
# 裁剪列表
LTRIM key start stop
使用场景:
- 消息队列(生产者-消费者模式)
- 最近更新的列表(如朋友圈最新状态)
- 实现栈或队列
3.3 Hash(哈希)
存储字段和值的映射表,适合存储对象。
基本命令:
# 设置单个字段
HSET key field value
# 设置多个字段
HMSET key field1 value1 field2 value2
# 获取单个字段
HGET key field
# 获取多个字段
HMGET key field1 field2
# 获取所有字段和值
HGETALL key
# 删除字段
HDEL key field1 [field2]
# 增加数字
HINCRBY key field increment
# 检查字段是否存在
HEXISTS key field
# 获取所有字段名
HKEYS key
# 获取所有值
HVALS key
# 获取字段数量
HLEN key
使用场景:
- 存储用户信息、商品信息等对象数据
- 购物车(用户ID为key,商品ID为field,数量为value)
- 计数器(多个相关计数一起存储)
3.4 Set(集合)
无序的字符串集合,不允许重复元素。
基本命令:
# 添加元素
SADD key member1 [member2]
# 删除元素
SREM key member1 [member2]
# 获取所有元素
SMEMBERS key
# 判断元素是否在集合中
SISMEMBER key member
# 获取集合元素数量
SCARD key
# 随机获取元素
SRANDMEMBER key [count]
# 弹出随机元素
SPOP key [count]
# 交集
SINTER key1 key2
# 并集
SUNION key1 key2
# 差集
SDIFF key1 key2
使用场景:
- 标签系统
- 用户关注/粉丝列表
- 黑名单/白名单
- 抽奖系统
3.5 Sorted Set(有序集合)
有序的字符串集合,每个元素关联一个分数用于排序。
基本命令:
# 添加元素
ZADD key score1 member1 [score2 member2]
# 获取元素排名(从小到大)
ZRANK key member
# 获取元素排名(从大到小)
ZREVRANK key member
# 获取元素分数
ZSCORE key member
# 获取指定排名范围的元素
ZRANGE key start stop [WITHSCORES]
# 按分数获取元素
ZRANGEBYSCORE key min max [WITHSCORES]
# 增加元素分数
ZINCRBY key increment member
# 删除元素
ZREM key member1 [member2]
# 获取集合元素数量
ZCARD key
# 获取指定分数范围内的元素数量
ZCOUNT key min max
使用场景:
- 排行榜系统
- 带权重的队列
- 延迟队列(以时间戳为分数)
- 评分系统
3.6 其他数据类型
Bitmap:位图,可以对位进行操作
# 设置位图中某位的值
SETBIT key offset value
# 获取位图中某位的值
GETBIT key offset
# 统计位图中值为1的位的数量
BITCOUNT key [start end]
HyperLogLog:用于基数统计的概率性数据结构
# 添加元素
PFADD key element1 [element2]
# 获取基数估计值
PFCOUNT key [key2]
Geospatial:地理位置索引
# 添加地理位置
GEOADD key longitude latitude member
# 计算两个位置间的距离
GEODIST key member1 member2 [unit]
# 获取指定范围内的位置
GEORADIUS key longitude latitude radius unit
4. Redis高级特性
4.1 事务
Redis事务允许一次执行多个命令,它的主要特点:
- 命令在事务中按顺序执行
- 事务执行过程中不会被其他命令中断
- Redis事务不支持回滚
# 开始事务
MULTI
# 命令入队
SET user:1:name "Tom"
INCR user:1:visits
# 执行事务
EXEC
# 或放弃事务
DISCARD
4.2 发布订阅
Redis提供发布订阅功能,支持消息的发布和订阅。
# 订阅频道
SUBSCRIBE channel1 [channel2]
# 发布消息
PUBLISH channel1 "Hello World"
# 基于模式订阅
PSUBSCRIBE news.*
4.3 持久化
Redis支持两种持久化方式:RDB和AOF。
RDB(Redis Database):
- 按指定时间间隔执行数据集的时间点快照
- 配置示例:
save 900 1 save 300 10 save 60 10000
AOF(Append Only File):
- 记录服务器接收的每个写操作
- 配置示例:
appendonly yes appendfsync everysec
4.4 Lua脚本
Redis支持使用Lua脚本执行复杂的原子操作:
# 执行Lua脚本
EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 arg1 arg2
# 加载脚本到缓存并返回SHA1摘要
SCRIPT LOAD "return redis.call('get', KEYS[1])"
# 使用SHA1摘要执行缓存的脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
4.5 Pipeline(管道)
Pipeline可以一次性发送多个命令到服务器,减少网络往返时间:
# 示例代码(伪代码)
pipeline = redis.pipeline()
pipeline.set("key1", "value1")
pipeline.set("key2", "value2")
pipeline.get("key1")
pipeline.get("key2")
results = pipeline.execute()
5. Redis实战应用
5.1 实现分布式锁
Redis实现分布式锁的基本思路:
# 获取锁(NX表示key不存在时才设置,PX设置过期时间为毫秒)
SET resource_name unique_value NX PX 10000
# 释放锁(使用Lua脚本确保原子性)
EVAL "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 resource_name unique_value
Java实现示例:
public boolean acquireLock(String lockKey, String clientId, int expireTime) {
String result = jedis.set(lockKey, clientId, "NX", "PX", expireTime);
return "OK".equals(result);
}
public boolean releaseLock(String lockKey, String clientId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));
return Integer.valueOf(1).equals(result);
}
5.2 缓存设计模式
Cache-Aside(旁路缓存):
- 读取:先查缓存,缓存命中返回;缓存未命中查数据库,并将结果放入缓存
- 写入:先写数据库,再删除缓存(不要更新缓存)
// 伪代码
public Data getDataById(String id) {
// 查询缓存
String cacheKey = "data:" + id;
Data data = redis.get(cacheKey);
// 缓存命中
if (data != null) {
return data;
}
// 缓存未命中,查询数据库
data = db.query("SELECT * FROM data WHERE id = ?", id);
// 将数据放入缓存
if (data != null) {
redis.set(cacheKey, data);
}
return data;
}
public void updateData(Data data) {
// 更新数据库
db.update("UPDATE data SET ... WHERE id = ?", data.getId());
// 删除缓存
redis.del("data:" + data.getId());
}
缓存穿透防护:
缓存穿透是指查询一个不存在的数据,导致每次都要去数据库查询。
解决方案:
- 对空值进行缓存
- 使用布隆过滤器
// 缓存空值
public Data getDataById(String id) {
String cacheKey = "data:" + id;
String value = redis.get(cacheKey);
if (value != null) {
if (value.equals("nil")) {
return null; // 空值已被缓存
}
return deserialize(value);
}
Data data = db.query("SELECT * FROM data WHERE id = ?", id);
if (data != null) {
redis.set(cacheKey, serialize(data));
return data;
} else {
// 缓存空值,避免缓存穿透
redis.set(cacheKey, "nil", "EX", 60); // 设置较短的过期时间
return null;
}
}
缓存雪崩防护:
缓存雪崩是指大量缓存同时过期,导致大量请求落到数据库。
解决方案:
- 设置随机过期时间
- 更新时增加互斥锁
- 使用Redis集群提高可用性
// 设置随机过期时间
public void setWithRandomExpire(String key, String value) {
// 在基础过期时间(如3600秒)上增加随机秒数(如0-300秒)
int randomExpireTime = 3600 + new Random().nextInt(300);
redis.set(key, value, "EX", randomExpireTime);
}
5.3 简单计数器实现
使用Redis实现计数器非常简单:
# 增加计数
INCR page_view:article:1001
# 获取计数
GET page_view:article:1001
# 设置过期时间(如24小时)
EXPIRE page_view:article:1001 86400
Java实现:
public long incrementPageView(String articleId) {
String key = "page_view:article:" + articleId;
long count = jedis.incr(key);
// 如果是新键,设置过期时间
if (count == 1) {
jedis.expire(key, 86400);
}
return count;
}
5.4 实现排行榜
使用Sorted Set实现排行榜:
# 添加或更新分数
ZADD leaderboard 1000 user1
ZADD leaderboard 2000 user2
ZADD leaderboard 1500 user3
# 获取前三名
ZREVRANGE leaderboard 0 2 WITHSCORES
# 获取用户排名
ZREVRANK leaderboard user2
# 增加用户分数
ZINCRBY leaderboard 100 user1
Java实现:
public void updateScore(String userId, double score) {
jedis.zadd("leaderboard", score, userId);
}
public List<Map.Entry<String, Double>> getTopPlayers(int count) {
Set<Tuple> tuples = jedis.zrevrangeWithScores("leaderboard", 0, count - 1);
List<Map.Entry<String, Double>> result = new ArrayList<>();
for (Tuple tuple : tuples) {
result.add(new AbstractMap.SimpleEntry<>(tuple.getElement(), tuple.getScore()));
}
return result;
}
public long getRank(String userId) {
Long rank = jedis.zrevrank("leaderboard", userId);
return rank != null ? rank + 1 : 0; // +1转为人类可读的排名(从1开始)
}
6. Redis高可用与集群
6.1 主从复制
主从复制可以实现数据备份和读写分离:
# 在从服务器上执行
SLAVEOF master_ip master_port
配置文件方式:
slaveof 192.168.1.10 6379
6.2 Sentinel(哨兵)
Redis Sentinel提供高可用性,通过监控主服务器状态并在主服务器故障时自动进行故障转移。
sentinel.conf基本配置:
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
6.3 Redis Cluster
Redis Cluster提供分片功能,可以将数据自动分散到多个节点上。
基本配置:
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
7. Redis性能优化
7.1 内存优化
- 使用合适的数据结构
- 设置合理的键过期时间
- 使用Redis存储小而精简的对象
- 压缩键和值
- 合理设置maxmemory和maxmemory-policy
7.2 命令优化
- 使用批量命令:MGET/MSET代替多次GET/SET
- 使用Pipeline减少网络往返时间
- 避免使用KEYS命令(使用SCAN代替)
- 避免使用高耗时命令如SORT、LREM、SINTER
7.3 连接优化
- 使用连接池管理连接
- 复用连接
- 及时关闭不使用的连接
8. Redis部署与运维
8.1 Docker部署Redis
Docker是部署Redis的便捷方式:
# 拉取Redis镜像
docker pull redis
# 运行Redis容器
docker run --name redis-server -p 6379:6379 -d redis
# 使用自定义配置文件
docker run --name redis-server -v /path/to/redis.conf:/etc/redis/redis.conf -p 6379:6379 -d redis redis-server /etc/redis/redis.conf
8.2 Redis监控
监控Redis的关键指标:
- 内存使用情况
- 连接数
- 命令执行情况
- 命中率和未命中率
- 网络流量
监控工具:
- Redis INFO命令
- Redis-stat
- Redis-monitor
- Prometheus + Grafana
8.3 备份与恢复
备份:
# 使用SAVE命令(同步)
redis-cli SAVE
# 使用BGSAVE命令(异步)
redis-cli BGSAVE
# 复制RDB文件
cp /var/lib/redis/dump.rdb /backup/dump-$(date +%Y%m%d).rdb
恢复:
# 停止Redis服务
systemctl stop redis
# 复制RDB文件到Redis数据目录
cp /backup/dump.rdb /var/lib/redis/
# 启动Redis服务
systemctl start redis
总结
Redis是一个强大而灵活的内存数据库,本教程介绍了Redis的基础知识、常用命令、高级特性和实际应用场景。掌握Redis可以帮助我们构建更高性能、更可靠的应用系统。
作为一个内存数据库,Redis特别适合用于需要高性能读写的场景,如缓存、计数器、分布式锁、排行榜等。通过合理使用Redis的数据结构和功能,我们可以大幅提升应用系统的性能和可扩展性。
随着对Redis的深入学习和实践,你会发现它不仅是一个缓存系统,更是一个多功能的数据处理平台,可以解决很多复杂的业务问题。
如有问题或建议,欢迎联系作者: 1412800823@qq.com