Redis 基础以及高频使用场景详解
目录
Redis简介
什么是Redis
Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,并提供了丰富的操作命令。
主要特点
- 高性能: 基于内存操作,读写性能极高
- 数据结构丰富: 支持字符串、哈希、列表、集合、有序集合等
- 持久化: 支持RDB和AOF两种持久化方式
- 主从复制: 支持主从复制和哨兵模式
- 集群模式: 支持Redis Cluster分布式集群
- 事务支持: 支持简单的事务操作
- Lua脚本: 支持Lua脚本执行
适用场景
- 缓存系统
- 会话存储
- 排行榜系统
- 消息队列
- 分布式锁
- 计数器系统
- 实时统计
核心概念
数据结构
Redis数据结构
├── String - 字符串类型
├── Hash - 哈希类型
├── List - 列表类型
├── Set - 集合类型
├── Sorted Set - 有序集合类型
├── Bitmap - 位图类型
├── HyperLogLog - 基数统计类型
└── Stream - 流类型
内存模型
Redis内存结构
├── 数据内存 - 存储实际数据
├── 索引内存 - 存储索引信息
├── 缓冲区内存 - 客户端缓冲区
└── 其他内存 - 系统开销
使用技巧
1. 字符串操作
# 基本操作
SET key value
GET key
DEL key
EXISTS key
# 批量操作
MSET key1 value1 key2 value2
MGET key1 key2
# 数值操作
INCR key
INCRBY key increment
DECR key
DECRBY key decrement
# 过期时间
EXPIRE key seconds
TTL key
PERSIST key
2. 哈希操作
# 基本操作
HSET key field value
HGET key field
HDEL key field
HEXISTS key field
# 批量操作
HMSET key field1 value1 field2 value2
HMGET key field1 field2
HGETALL key
# 数值操作
HINCRBY key field increment
HINCRBYFLOAT key field increment
3. 列表操作
# 基本操作
LPUSH key value
RPUSH key value
LPOP key
RPOP key
LRANGE key start stop
# 阻塞操作
BLPOP key timeout
BRPOP key timeout
# 列表维护
LTRIM key start stop
LLEN key
LINDEX key index
4. 集合操作
# 基本操作
SADD key member
SREM key member
SISMEMBER key member
SMEMBERS key
# 集合运算
SINTER key1 key2
SUNION key1 key2
SDIFF key1 key2
# 集合统计
SCARD key
SRANDMEMBER key count
5. 有序集合操作
# 基本操作
ZADD key score member
ZREM key member
ZSCORE key member
ZRANK key member
# 范围查询
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]
ZRANGEBYSCORE key min max [WITHSCORES]
# 统计操作
ZCARD key
ZCOUNT key min max
6. 高级特性
# 发布订阅
PUBLISH channel message
SUBSCRIBE channel
UNSUBSCRIBE channel
# 事务操作
MULTI
SET key1 value1
SET key2 value2
EXEC
# Lua脚本
EVAL "return redis.call('GET', KEYS[1])" 1 key1
重难点解析
1. 持久化机制
# RDB持久化配置
save 900 1 # 900秒内至少1个key变化
save 300 10 # 300秒内至少10个key变化
save 60 10000 # 60秒内至少10000个key变化
# AOF持久化配置
appendonly yes
appendfsync always # 每次写入都同步
appendfsync everysec # 每秒同步一次
appendfsync no # 由操作系统决定
# 混合持久化
aof-use-rdb-preamble yes
2. 内存管理
# 内存策略配置
maxmemory 2gb
maxmemory-policy allkeys-lru # 最近最少使用
# 其他策略: volatile-lru, allkeys-random, volatile-random, volatile-ttl, noeviction
# 内存优化
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
3. 主从复制
# 主节点配置
bind 0.0.0.0
port 6379
daemonize yes
# 从节点配置
slaveof master_ip master_port
slave-read-only yes
slave-priority 100
# 复制优化
repl-backlog-size 1mb
repl-backlog-ttl 3600
4. 哨兵模式
# 哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
# 启动哨兵
redis-sentinel sentinel.conf
5. 集群模式
# 集群配置
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-migration-barrier 1
cluster-require-full-coverage yes
# 创建集群
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
Spring Boot集成
1. 依赖配置
<!-- Maven -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
// Gradle
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.apache.commons:commons-pool2'
2. 配置文件
# application.yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
# 连接池配置
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
# 集群配置
# cluster:
# nodes:
# - 127.0.0.1:7000
# - 127.0.0.1:7001
# - 127.0.0.1:7002
# max-redirects: 3
# 哨兵配置
# sentinel:
# master: mymaster
# nodes:
# - 127.0.0.1:26379
# - 127.0.0.1:26380
# - 127.0.0.1:26381
3. 配置类
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 设置key序列化器
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 设置value序列化器
Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jsonSerializer.setObjectMapper(objectMapper);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
4. 服务层实现
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 基本操作
public void setUser(String key, User user) {
redisTemplate.opsForValue().set(key, user, Duration.ofHours(1));
}
public User getUser(String key) {
return (User) redisTemplate.opsForValue().get(key);
}
// 哈希操作
public void setUserHash(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
public Object getUserHash(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}
// 列表操作
public void addToList(String key, Object value) {
redisTemplate.opsForList().rightPush(key, value);
}
public List<Object> getList(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
// 集合操作
public void addToSet(String key, Object value) {
redisTemplate.opsForSet().add(key, value);
}
public Set<Object> getSet(String key) {
return redisTemplate.opsForSet().members(key);
}
// 有序集合操作
public void addToSortedSet(String key, Object value, double score) {
redisTemplate.opsForZSet().add(key, value, score);
}
public Set<Object> getTopFromSortedSet(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
// 分布式锁
public boolean acquireLock(String lockKey, String requestId, long expireTime) {
String script = "if redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2], 'NX') then return 1 else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Arrays.asList(lockKey), requestId, expireTime);
return result != null && result == 1L;
}
public boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Arrays.asList(lockKey), requestId);
return result != null && result == 1L;
}
// 缓存注解
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 从数据库查询用户
return userRepository.findById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userRepository.save(user);
}
}
5. 控制器层
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/cache")
public ResponseEntity<Void> cacheUser(@RequestBody User user) {
userService.setUser("user:" + user.getId(), user);
return ResponseEntity.ok().build();
}
@GetMapping("/cache/{id}")
public ResponseEntity<User> getCachedUser(@PathVariable String id) {
User user = userService.getUser("user:" + id);
return ResponseEntity.ok(user);
}
@PostMapping("/lock")
public ResponseEntity<String> acquireLock(@RequestParam String key) {
String requestId = UUID.randomUUID().toString();
boolean acquired = userService.acquireLock(key, requestId, 30);
if (acquired) {
return ResponseEntity.ok(requestId);
} else {
return ResponseEntity.status(HttpStatus.CONFLICT).body("Lock acquisition failed");
}
}
@DeleteMapping("/lock")
public ResponseEntity<Void> releaseLock(@RequestParam String key, @RequestParam String requestId) {
boolean released = userService.releaseLock(key, requestId);
if (released) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
}
}
分布式场景使用
1. 分布式锁
@Component
public class DistributedLockService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final long DEFAULT_EXPIRE_TIME = 30;
/**
* 获取分布式锁
*/
public boolean acquireLock(String lockKey, String requestId, long expireTime) {
String key = LOCK_PREFIX + lockKey;
String script = "if redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2], 'NX') then return 1 else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Arrays.asList(key), requestId, expireTime);
return result != null && result == 1L;
}
/**
* 释放分布式锁
*/
public boolean releaseLock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Arrays.asList(key), requestId);
return result != null && result == 1L;
}
/**
* 可重入锁
*/
public boolean acquireReentrantLock(String lockKey, String requestId, long expireTime) {
String key = LOCK_PREFIX + lockKey;
String script = "if redis.call('exists', KEYS[1]) == 0 then " +
"redis.call('hset', KEYS[1], ARGV[1], 1) " +
"redis.call('expire', KEYS[1], ARGV[2]) " +
"return 1 " +
"end " +
"if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then " +
"redis.call('hincrby', KEYS[1], ARGV[1], 1) " +
"redis.call('expire', KEYS[1], ARGV[2]) " +
"return 1 " +
"end " +
"return 0";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Arrays.asList(key), requestId, expireTime);
return result != null && result == 1L;
}
}
2. 分布式缓存
@Service
public class DistributedCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 设置缓存
*/
public void set(String key, Object value, Duration timeout) {
redisTemplate.opsForValue().set(key, value, timeout);
}
/**
* 获取缓存
*/
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 删除缓存
*/
public void delete(String key) {
redisTemplate.delete(key);
}
/**
* 批量删除缓存
*/
public void deleteBatch(Collection<String> keys) {
redisTemplate.delete(keys);
}
/**
* 设置缓存并设置过期时间
*/
public void setWithExpire(String key, Object value, Duration timeout) {
redisTemplate.opsForValue().set(key, value, timeout);
}
/**
* 缓存穿透保护
*/
public Object getWithProtection(String key, Supplier<Object> loader, Duration timeout) {
Object value = get(key);
if (value != null) {
return value;
}
// 防止缓存穿透
String lockKey = "lock:" + key;
String requestId = UUID.randomUUID().toString();
try {
if (acquireLock(lockKey, requestId, 10)) {
// 双重检查
value = get(key);
if (value != null) {
return value;
}
// 从数据源加载
value = loader.get();
if (value != null) {
set(key, value, timeout);
} else {
// 设置空值防止缓存穿透
set(key, new NullValue(), Duration.ofMinutes(5));
}
return value;
} else {
// 等待其他线程加载完成
Thread.sleep(100);
return get(key);
}
} catch (Exception e) {
throw new RuntimeException("Failed to get cache", e);
} finally {
releaseLock(lockKey, requestId);
}
}
}
3. 分布式限流
@Component
public class RateLimiterService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 滑动窗口限流
*/
public boolean isAllowed(String key, int maxRequests, Duration window) {
String script = "local current = redis.call('get', KEYS[1]) " +
"if current == false then " +
"redis.call('setex', KEYS[1], ARGV[2], 1) " +
"return 1 " +
"end " +
"if tonumber(current) < tonumber(ARGV[1]) then " +
"redis.call('incr', KEYS[1]) " +
"return 1 " +
"end " +
"return 0";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Arrays.asList(key), maxRequests, window.getSeconds());
return result != null && result == 1L;
}
/**
* 令牌桶限流
*/
public boolean isAllowedTokenBucket(String key, int capacity, int rate, Duration window) {
String script = "local current = redis.call('get', KEYS[1]) " +
"local now = ARGV[1] " +
"local last = ARGV[2] " +
"local rate = tonumber(ARGV[3]) " +
"local capacity = tonumber(ARGV[4]) " +
"local tokens = tonumber(current or capacity) " +
"local elapsed = now - last " +
"tokens = math.min(capacity, tokens + elapsed * rate) " +
"if tokens >= 1 then " +
"tokens = tokens - 1 " +
"redis.call('setex', KEYS[1], ARGV[5], tokens) " +
"redis.call('setex', KEYS[2], ARGV[5], now) " +
"return 1 " +
"end " +
"return 0";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
long now = System.currentTimeMillis();
long last = getLastRefillTime(key + ":last");
Long result = redisTemplate.execute(redisScript,
Arrays.asList(key, key + ":last"),
now, last, rate, capacity, window.getSeconds());
return result != null && result == 1L;
}
private long getLastRefillTime(String key) {
Object value = redisTemplate.opsForValue().get(key);
return value != null ? (Long) value : System.currentTimeMillis();
}
}
4. 分布式会话
@Configuration
public class SessionConfig {
@Bean
public RedisSessionRepository redisSessionRepository(RedisConnectionFactory connectionFactory) {
return new RedisSessionRepository(connectionFactory);
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
@Component
public class SessionService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String SESSION_PREFIX = "session:";
/**
* 创建会话
*/
public String createSession(String userId, Map<String, Object> attributes, Duration timeout) {
String sessionId = UUID.randomUUID().toString();
String key = SESSION_PREFIX + sessionId;
Map<String, Object> sessionData = new HashMap<>();
sessionData.put("userId", userId);
sessionData.put("attributes", attributes);
sessionData.put("createdAt", System.currentTimeMillis());
redisTemplate.opsForValue().set(key, sessionData, timeout);
return sessionId;
}
/**
* 获取会话
*/
public Map<String, Object> getSession(String sessionId) {
String key = SESSION_PREFIX + sessionId;
return (Map<String, Object>) redisTemplate.opsForValue().get(key);
}
/**
* 更新会话
*/
public void updateSession(String sessionId, Map<String, Object> attributes, Duration timeout) {
String key = SESSION_PREFIX + sessionId;
Map<String, Object> sessionData = getSession(sessionId);
if (sessionData != null) {
sessionData.put("attributes", attributes);
sessionData.put("updatedAt", System.currentTimeMillis());
redisTemplate.opsForValue().set(key, sessionData, timeout);
}
}
/**
* 删除会话
*/
public void deleteSession(String sessionId) {
String key = SESSION_PREFIX + sessionId;
redisTemplate.delete(key);
}
}
最佳实践
1. 性能优化
- 连接池配置: 合理设置连接池大小
- 批量操作: 使用pipeline和multi-exec
- 序列化优化: 选择合适的序列化方式
- 内存优化: 合理设置内存策略
2. 高可用配置
- 主从复制: 配置主从节点
- 哨兵模式: 自动故障转移
- 集群模式: 水平扩展和高可用
- 持久化: 配置RDB和AOF
3. 安全配置
- 密码认证: 设置访问密码
- 网络隔离: 限制网络访问
- 权限控制: 配置用户权限
- TLS加密: 启用SSL/TLS
4. 监控维护
- 性能监控: 监控响应时间和吞吐量
- 内存监控: 监控内存使用情况
- 连接监控: 监控连接数和连接状态
- 日志分析: 分析慢查询和错误日志
总结
Redis作为高性能的内存数据库,具有丰富的数据结构、强大的缓存能力和完善的分布式特性。通过合理的配置和优化,可以构建高性能、高可用的缓存和存储系统。
关键要点:
- 理解Redis的数据结构和操作命令
- 掌握持久化和内存管理机制
- 熟练配置主从复制和集群模式
- 正确集成Spring Boot应用
- 合理使用分布式锁、缓存、限流等特性
- 遵循最佳实践确保性能和稳定性
通过本指南的学习,您应该能够熟练使用Redis并成功集成到Spring Boot项目中,构建高性能的分布式应用。
4224

被折叠的 条评论
为什么被折叠?



