Redis基础知识
Redis基础知识
一、Redis简介
1.1 什么是Redis?
Redis是一个开源的、基于内存的数据结构存储系统,可以用作:
- 数据库
- 缓存
- 消息中间件
- 分布式锁
1.2 Redis特点
- 高性能:基于内存操作
- 支持多种数据结构
- 支持数据持久化
- 支持主从复制
- 支持事务
- 支持Lua脚本
1.3 应用场景
- 缓存
- 计数器
- 排行榜
- 消息队列
- 分布式锁
- 会话管理
二、Redis安装与配置
2.1 安装Redis
# Windows安装
# 1. 下载Redis for Windows
# 2. 运行安装程序
# 3. 配置环境变量
# Linux安装
sudo apt-get update
sudo apt-get install redis-server
# Mac安装
brew install redis
2.2 启动Redis
# Windows启动
redis-server
# Linux启动
sudo systemctl start redis-server
# Mac启动
brew services start redis
2.3 连接Redis
# 命令行连接
redis-cli
# 指定主机和端口连接
redis-cli -h localhost -p 6379
# 使用密码连接
redis-cli -a password
三、Redis数据类型
3.1 字符串(String)
# 设置值
SET key value
# 获取值
GET key
# 设置过期时间
SETEX key seconds value
# 批量设置
MSET key1 value1 key2 value2
# 批量获取
MGET key1 key2
# 递增
INCR key
# 递减
DECR key
3.2 哈希(Hash)
# 设置字段值
HSET key field value
# 获取字段值
HGET key field
# 获取所有字段
HGETALL key
# 删除字段
HDEL key field
# 判断字段是否存在
HEXISTS key field
3.3 列表(List)
# 左侧插入
LPUSH key value
# 右侧插入
RPUSH key value
# 左侧弹出
LPOP key
# 右侧弹出
RPOP key
# 获取列表范围
LRANGE key start stop
# 获取列表长度
LLEN key
3.4 集合(Set)
# 添加元素
SADD key member
# 删除元素
SREM key member
# 获取所有元素
SMEMBERS key
# 判断元素是否存在
SISMEMBER key member
# 获取集合大小
SCARD key
3.5 有序集合(Sorted Set)
# 添加元素
ZADD key score member
# 删除元素
ZREM key member
# 获取分数范围内的元素
ZRANGEBYSCORE key min max
# 获取排名
ZRANK key member
# 获取分数
ZSCORE key member
四、Redis高级特性
4.1 事务
# 开始事务
MULTI
# 执行命令
SET key1 value1
SET key2 value2
# 提交事务
EXEC
# 取消事务
DISCARD
4.2 发布订阅
# 订阅频道
SUBSCRIBE channel
# 发布消息
PUBLISH channel message
# 取消订阅
UNSUBSCRIBE channel
4.3 持久化
# RDB持久化配置
save 900 1
save 300 10
save 60 10000
# AOF持久化配置
appendonly yes
appendfsync everysec
五、Redis集群
5.1 主从复制
# 配置从节点
SLAVEOF master_ip master_port
# 查看复制状态
INFO replication
5.2 哨兵模式
# 启动哨兵
redis-sentinel sentinel.conf
# 哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
5.3 集群模式
# 创建集群
redis-cli --cluster create node1:6379 node2:6379 node3:6379
# 添加节点
redis-cli --cluster add-node new_node:6379 existing_node:6379
六、Redis性能优化
6.1 内存优化
- 使用适当的数据类型
- 设置过期时间
- 使用压缩
- 定期清理
6.2 配置优化
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec
6.3 监控
# 查看内存使用
INFO memory
# 查看客户端连接
INFO clients
# 查看命令统计
INFO commandstats
七、Redis安全
7.1 访问控制
# 设置密码
CONFIG SET requirepass password
# 修改密码
AUTH old_password new_password
7.2 网络安全
# redis.conf
bind 127.0.0.1
protected-mode yes
八、Redis最佳实践
8.1 开发规范
-
键名设计
- 使用有意义的名称
- 使用冒号分隔
- 控制键名长度
-
数据设计
- 合理使用数据类型
- 避免大key
- 设置过期时间
-
性能优化
- 使用pipeline
- 使用连接池
- 避免频繁操作
8.2 运维规范
-
监控
- 监控内存使用
- 监控连接数
- 监控命令执行
-
备份
- 定期备份数据
- 测试恢复流程
- 保存备份记录
-
安全
- 启用密码认证
- 限制网络访问
- 定期更新密码
九、Redis与Java集成
9.1 Jedis客户端
// 添加Jedis依赖
// Maven
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
// Gradle
implementation 'redis.clients:jedis:4.3.1'
9.2 基本操作示例
// 创建Jedis客户端
Jedis jedis = new Jedis("localhost", 6379);
// 设置密码(如果有)
jedis.auth("password");
// 字符串操作
jedis.set("key", "value");
String value = jedis.get("key");
// 哈希操作
jedis.hset("user:1", "name", "张三");
jedis.hset("user:1", "age", "25");
Map<String, String> user = jedis.hgetAll("user:1");
// 列表操作
jedis.lpush("messages", "消息1");
jedis.lpush("messages", "消息2");
List<String> messages = jedis.lrange("messages", 0, -1);
// 集合操作
jedis.sadd("tags", "Java", "Redis", "Spring");
Set<String> tags = jedis.smembers("tags");
// 有序集合操作
jedis.zadd("scores", 89.5, "张三");
jedis.zadd("scores", 92.0, "李四");
Set<String> topScores = jedis.zrevrange("scores", 0, 1);
// 关闭连接
jedis.close();
9.3 使用连接池
// 创建连接池配置
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
poolConfig.setMinEvictableIdleTimeMillis(60000);
poolConfig.setTimeBetweenEvictionRunsMillis(30000);
poolConfig.setNumTestsPerEvictionRun(3);
poolConfig.setBlockWhenExhausted(true);
// 创建连接池
JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379, 2000, "password");
// 从连接池获取连接
try (Jedis jedis = jedisPool.getResource()) {
// 使用Jedis进行操作
jedis.set("key", "value");
String value = jedis.get("key");
}
// 关闭连接池
jedisPool.close();
9.4 使用Pipeline批量操作
Jedis jedis = new Jedis("localhost", 6379);
Pipeline pipeline = jedis.pipelined();
// 批量设置
for (int i = 0; i < 1000; i++) {
pipeline.set("key:" + i, "value:" + i);
}
// 执行Pipeline
pipeline.sync();
// 关闭连接
jedis.close();
十、Redis与Spring Boot集成
10.1 添加Spring Data Redis依赖
<!-- Maven -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Gradle -->
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
10.2 配置Redis连接
# application.yml
spring:
redis:
host: localhost
port: 6379
password: password
database: 0
timeout: 10000
lettuce:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
10.3 创建Redis配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerializer序列化值
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, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
// 设置key和value的序列化规则
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
10.4 使用RedisTemplate
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 存储用户信息
public void saveUser(String id, User user) {
redisTemplate.opsForValue().set("user:" + id, user);
}
// 获取用户信息
public User getUser(String id) {
return (User) redisTemplate.opsForValue().get("user:" + id);
}
// 删除用户信息
public void deleteUser(String id) {
redisTemplate.delete("user:" + id);
}
// 设置过期时间
public void setExpire(String key, long timeout, TimeUnit unit) {
redisTemplate.expire(key, timeout, unit);
}
}
10.5 使用RedisRepository
@RedisHash("users")
public class User {
@Id
private String id;
private String name;
private int age;
// 构造函数、getter和setter方法
}
@Repository
public interface UserRepository extends CrudRepository<User, String> {
List<User> findByAge(int age);
List<User> findByNameStartingWith(String prefix);
}
十一、Redis与Docker部署
11.1 使用Docker运行Redis
# 拉取Redis镜像
docker pull redis:latest
# 运行Redis容器
docker run -d --name redis -p 6379:6379 redis:latest
# 运行Redis容器(带数据持久化)
docker run -d --name redis -p 6379:6379 -v /data/redis:/data redis:latest
# 运行Redis容器(带密码)
docker run -d --name redis -p 6379:6379 redis:latest redis-server --requirepass password
11.2 使用Docker Compose部署Redis
# docker-compose.yml
version: '3'
services:
redis:
image: redis:latest
container_name: redis
ports:
- "6379:6379"
volumes:
- ./data:/data
command: redis-server --requirepass password
networks:
- redis-network
redis-commander:
image: rediscommander/redis-commander:latest
container_name: redis-commander
ports:
- "8081:8081"
environment:
- REDIS_HOSTS=local:redis:6379:0:password
depends_on:
- redis
networks:
- redis-network
networks:
redis-network:
driver: bridge
11.3 使用Docker部署Redis集群
# docker-compose-cluster.yml
version: '3'
services:
redis1:
image: redis:latest
container_name: redis1
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports:
- "6371:6379"
volumes:
- ./data1:/data
networks:
- redis-cluster-network
redis2:
image: redis:latest
container_name: redis2
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports:
- "6372:6379"
volumes:
- ./data2:/data
networks:
- redis-cluster-network
redis3:
image: redis:latest
container_name: redis3
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports:
- "6373:6379"
volumes:
- ./data3:/data
networks:
- redis-cluster-network
redis4:
image: redis:latest
container_name: redis4
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports:
- "6374:6379"
volumes:
- ./data4:/data
networks:
- redis-cluster-network
redis5:
image: redis:latest
container_name: redis5
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports:
- "6375:6379"
volumes:
- ./data5:/data
networks:
- redis-cluster-network
redis6:
image: redis:latest
container_name: redis6
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports:
- "6376:6379"
volumes:
- ./data6:/data
networks:
- redis-cluster-network
networks:
redis-cluster-network:
driver: bridge
# 创建Redis集群
docker exec -it redis1 redis-cli --cluster create \
redis1:6379 redis2:6379 redis3:6379 redis4:6379 redis5:6379 redis6:6379 \
--cluster-replicas 1
十二、Redis常见问题解答
12.1 内存问题
问题: Redis内存占用过高
解决方案:
- 设置最大内存限制:
CONFIG SET maxmemory 2gb
- 配置内存淘汰策略:
CONFIG SET maxmemory-policy allkeys-lru
- 使用适当的数据类型,避免大key
- 设置过期时间,让不常用的数据自动过期
- 使用压缩算法减少内存占用
12.2 性能问题
问题: Redis响应慢
解决方案:
- 检查是否有慢查询:
SLOWLOG GET 10
- 优化数据结构,避免使用大key
- 使用Pipeline批量操作
- 增加Redis实例,使用集群分散负载
- 检查网络延迟和带宽
12.3 连接问题
问题: 客户端连接Redis失败
解决方案:
- 检查Redis服务是否正在运行
- 检查防火墙设置,确保端口6379开放
- 检查Redis配置文件中的bind设置
- 如果使用密码,确保密码正确
- 检查客户端连接池配置
12.4 数据一致性问题
问题: 主从复制数据不一致
解决方案:
- 检查主从复制状态:
INFO replication
- 检查网络连接是否稳定
- 如果从节点落后太多,考虑重新同步
- 使用哨兵模式自动处理故障转移
- 考虑使用Redis集群提高可用性
12.5 持久化问题
问题: Redis重启后数据丢失
解决方案:
- 启用RDB持久化:
CONFIG SET save "900 1 300 10 60 10000"
- 启用AOF持久化:
CONFIG SET appendonly yes
- 配置AOF同步策略:
CONFIG SET appendfsync everysec
- 定期备份数据文件
- 测试恢复流程确保数据可以正确恢复
十三、Redis高级应用场景
13.1 分布式锁
// 使用Redis实现分布式锁
public class RedisLock {
private RedisTemplate<String, String> redisTemplate;
private String lockKey;
public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey) {
this.redisTemplate = redisTemplate;
this.lockKey = lockKey;
}
public boolean tryLock(String requestId, long expireTime) {
return Boolean.TRUE.equals(redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS));
}
public boolean releaseLock(String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
return Boolean.TRUE.equals(redisTemplate.execute(
new DefaultRedisScript<>(script, Boolean.class),
Collections.singletonList(lockKey),
requestId));
}
}
// 使用示例
String requestId = UUID.randomUUID().toString();
RedisLock lock = new RedisLock(redisTemplate, "lock:resource");
try {
if (lock.tryLock(requestId, 30000)) {
// 获取锁成功,执行业务逻辑
// ...
} else {
// 获取锁失败
}
} finally {
// 释放锁
lock.releaseLock(requestId);
}
13.2 限流器
// 使用Redis实现限流器
public class RedisRateLimiter {
private RedisTemplate<String, String> redisTemplate;
private String key;
private int limit;
private int period;
public RedisRateLimiter(RedisTemplate<String, String> redisTemplate,
String key, int limit, int period) {
this.redisTemplate = redisTemplate;
this.key = key;
this.limit = limit;
this.period = period;
}
public boolean tryAcquire() {
String script = "local current = redis.call('incr', KEYS[1]) " +
"if current == 1 then " +
"redis.call('expire', KEYS[1], ARGV[1]) " +
"end " +
"return current <= tonumber(ARGV[2])";
return Boolean.TRUE.equals(redisTemplate.execute(
new DefaultRedisScript<>(script, Boolean.class),
Collections.singletonList(key),
String.valueOf(period), String.valueOf(limit)));
}
}
// 使用示例
RedisRateLimiter limiter = new RedisRateLimiter(redisTemplate, "rate:api", 100, 60);
if (limiter.tryAcquire()) {
// 允许请求通过
// ...
} else {
// 请求被限流
}
13.3 排行榜
// 使用Redis实现排行榜
public class RedisLeaderboard {
private RedisTemplate<String, String> redisTemplate;
private String key;
public RedisLeaderboard(RedisTemplate<String, String> redisTemplate, String key) {
this.redisTemplate = redisTemplate;
this.key = key;
}
public void addScore(String member, double score) {
redisTemplate.opsForZSet().add(key, member, score);
}
public List<String> getTopMembers(int count) {
return new ArrayList<>(redisTemplate.opsForZSet().reverseRange(key, 0, count - 1));
}
public List<String> getTopMembersWithScores(int count) {
Set<ZSetOperations.TypedTuple<String>> tuples =
redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, count - 1);
List<String> result = new ArrayList<>();
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
result.add(tuple.getValue() + ":" + tuple.getScore());
}
return result;
}
public Long getRank(String member) {
return redisTemplate.opsForZSet().reverseRank(key, member);
}
}
// 使用示例
RedisLeaderboard leaderboard = new RedisLeaderboard(redisTemplate, "leaderboard:game");
leaderboard.addScore("player1", 100);
leaderboard.addScore("player2", 200);
leaderboard.addScore("player3", 150);
List<String> topPlayers = leaderboard.getTopMembers(3);
// 结果: [player2, player3, player1]
13.4 消息队列
// 使用Redis实现消息队列
public class RedisMessageQueue {
private RedisTemplate<String, String> redisTemplate;
private String queueKey;
public RedisMessageQueue(RedisTemplate<String, String> redisTemplate, String queueKey) {
this.redisTemplate = redisTemplate;
this.queueKey = queueKey;
}
public void push(String message) {
redisTemplate.opsForList().rightPush(queueKey, message);
}
public String pop() {
return redisTemplate.opsForList().leftPop(queueKey);
}
public String popWithTimeout(long timeout, TimeUnit unit) {
return redisTemplate.opsForList().leftPop(queueKey, timeout, unit);
}
public long size() {
return redisTemplate.opsForList().size(queueKey);
}
}
// 生产者
RedisMessageQueue queue = new RedisMessageQueue(redisTemplate, "queue:messages");
queue.push("消息1");
queue.push("消息2");
// 消费者
while (true) {
String message = queue.popWithTimeout(1, TimeUnit.SECONDS);
if (message != null) {
// 处理消息
// ...
}
}
13.5 缓存策略
// 使用Redis实现缓存策略
public class RedisCache {
private RedisTemplate<String, Object> redisTemplate;
private long defaultExpire;
public RedisCache(RedisTemplate<String, Object> redisTemplate, long defaultExpire) {
this.redisTemplate = redisTemplate;
this.defaultExpire = defaultExpire;
}
public <T> T get(String key, Class<T> clazz) {
return (T) redisTemplate.opsForValue().get(key);
}
public void set(String key, Object value) {
set(key, value, defaultExpire);
}
public void set(String key, Object value, long expire) {
redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
}
public void delete(String key) {
redisTemplate.delete(key);
}
public <T> T getOrSet(String key, Supplier<T> supplier, Class<T> clazz) {
return getOrSet(key, supplier, defaultExpire, clazz);
}
public <T> T getOrSet(String key, Supplier<T> supplier, long expire, Class<T> clazz) {
T value = get(key, clazz);
if (value == null) {
value = supplier.get();
set(key, value, expire);
}
return value;
}
}
// 使用示例
RedisCache cache = new RedisCache(redisTemplate, 3600);
User user = cache.getOrSet("user:1", () -> userService.findById(1), User.class);
十四、Redis最佳实践总结
14.1 键名设计最佳实践
-
命名规范
- 使用冒号分隔不同部分:
业务:类型:ID
- 使用小写字母
- 避免使用特殊字符
- 控制键名长度
- 使用冒号分隔不同部分:
-
常见模式
- 用户信息:
user:1
- 用户会话:
session:abc123
- 计数器:
counter:pageviews
- 排行榜:
leaderboard:game
- 缓存:
cache:product:1
- 用户信息:
14.2 数据类型选择最佳实践
-
字符串(String)
- 适用于简单数据:计数器、缓存、会话ID
- 可以存储序列化的对象(JSON、二进制)
-
哈希(Hash)
- 适用于对象属性:用户信息、产品详情
- 可以部分更新,节省内存
-
列表(List)
- 适用于有序集合:消息队列、最新动态
- 可以实现分页
-
集合(Set)
- 适用于无序唯一集合:标签、关注者
- 支持集合运算:交集、并集、差集
-
有序集合(Sorted Set)
- 适用于排序集合:排行榜、权重队列
- 支持范围查询和排序
14.3 性能优化最佳实践
-
内存优化
- 使用适当的数据类型
- 避免大key(超过10KB)
- 设置过期时间
- 使用压缩算法
-
查询优化
- 使用Pipeline批量操作
- 使用连接池
- 避免频繁操作
- 使用适当的命令(如HGETALL替代多次HGET)
-
配置优化
- 调整内存限制和淘汰策略
- 配置持久化策略
- 调整连接池大小
- 启用慢查询日志
14.4 高可用最佳实践
-
主从复制
- 至少配置一个从节点
- 监控复制状态
- 定期备份数据
-
哨兵模式
- 至少配置三个哨兵节点
- 配置合理的故障检测参数
- 测试故障转移流程
-
集群模式
- 至少配置三个主节点和三个从节点
- 合理分配槽位
- 监控集群状态
14.5 安全最佳实践
-
访问控制
- 设置强密码
- 限制命令执行权限
- 定期更新密码
-
网络安全
- 限制IP访问
- 使用SSL/TLS加密
- 禁用危险命令
-
数据安全
- 定期备份数据
- 加密敏感数据
- 实施数据访问控制