Redis 的键空间是什么?
Redis 的键空间(key space)是指 Redis 数据库中存储的所有键值对的集合。可以理解为 Redis 数据库的一张大表,其中每一行是一个键值对。键空间内的键支持多种数据类型(如字符串、哈希、列表、集合、有序集合等),并且可以为键设置过期时间,来实现自动删除。
如何设置 Redis 的过期时间?
Redis 提供了多种方式来设置键的过期时间:
-
EXPIRE 命令:
用于设置键的过期时间(以秒为单位)。
示例:EXPIRE key 10(10秒后过期)。 -
PEXPIRE 命令:
设置键的过期时间(以毫秒为单位)。
示例:PEXPIRE key 10000(10秒后过期)。 -
EXPIREAT 和 PEXPIREAT 命令:
通过指定 Unix 时间戳来设置过期时间(秒或毫秒)。
示例:EXPIREAT key 1700000000(在特定时间点过期)。 -
SETEX 命令:
设置键值同时指定过期时间。
示例:SETEX key 10 value(10秒后过期)。 -
通过 Redis 客户端库:
使用某些编程语言的 Redis 客户端(如 Python 的redis-py),设置过期时间通常直接通过库函数实现。
在 Redis 中,键的过期时间存储在一个专门的数据结构中,与键值分开管理。
1. 过期时间的存储结构
- Redis 使用一个额外的哈希表(hash table),专门存储键的过期时间。
- 键(Key):指向 Redis 主键,与数据存储的键一致。
- 值(Value):记录该键的过期时间(以毫秒或秒为单位的 Unix 时间戳)。
例如:
| 键名 | 过期时间 |
|---|---|
key1 | 1700845200000(毫秒) |
key2 | 1700845300000(毫秒) |
注意:
- 如果某个键没有设置过期时间,则不会在过期时间哈希表中记录。
- Redis 内部通过两个哈希表分别管理数据和过期时间。
2. 过期时间的单位
Redis 默认以毫秒为单位存储过期时间,但通过命令设置时可以选择秒级或毫秒级。
- 秒级过期时间:使用
EXPIRE或EXPIREAT命令设置。 - 毫秒级过期时间:使用
PEXPIRE或PEXPIREAT命令设置。
Redis 内部会将所有过期时间统一转换为毫秒级 Unix 时间戳存储。
3. 如何检查键的过期时间?
-
TTL(Time-To-Live)命令:查看键剩余的过期时间。
TTL key1返回值:
-
0:键的剩余存活时间(单位:秒)。
- -1:键没有设置过期时间。
- -2:键不存在或已过期。
-
-
PTTL 命令:查看剩余时间(以毫秒为单位)。
PTTL key1
4. 过期键的删除与触发
Redis 的过期时间存储仅影响键何时被删除,具体删除依赖于以下策略:
- 惰性删除:当键被访问时,Redis 检查它的过期时间,过期即删除。
- 定期删除:Redis 定时扫描一部分键,删除过期键。
- 内存淘汰:当内存不足时,结合过期时间和淘汰策略清理数据。
5. 示例:查看过期时间
通过 Jedis 库操作 Redis 键的过期时间:
import redis.clients.jedis.Jedis;
public class RedisExpireTimeDemo {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
// 设置键并指定过期时间(秒级)
jedis.setex("key1", 60, "value1");
// 查看过期时间
long ttl = jedis.ttl("key1");
System.out.println("key1 的剩余过期时间(秒): " + ttl);
// 设置键并指定毫秒级过期时间
jedis.pexpire("key2", 30000); // 30 秒后过期
long pttl = jedis.pttl("key2");
System.out.println("key2 的剩余过期时间(毫秒): " + pttl);
jedis.close();
}
}
Redis 的过期键删除策略是什么?
过期键是设置了过期时间的键,Redis 会在过期后将其从键空间中删除。删除策略共有 三种:
1. 惰性删除(Lazy Deletion):
- 机制:仅当键被访问时,Redis 检查其是否过期,如果过期则立即删除。
- 优点:开销小,只有键被访问时才会检查。
- 缺点:可能导致大量过期键占用内存,特别是如果过期键很少被访问。
2. 定期删除(Periodic Deletion):
- 机制:Redis 会定期扫描一部分键,检查是否过期,并删除其中的过期键。
- 优点:自动清理过期键,减少内存占用。
- 缺点:如果扫描频率过低或扫描的键数量不足,可能导致过期键堆积;如果扫描过于频繁,则可能影响性能。
3. 内存淘汰(Memory Eviction):
- 机制:当 Redis 内存到达限制时,触发淘汰策略,优先清理过期键。
- 优点:在内存紧张时减少占用。
- 缺点:需要触发内存淘汰,且无法实时清理所有过期键。
Redis 的内存淘汰策略是什么?
当 Redis 的内存使用达到限制(由 maxmemory 配置指定)时,为了腾出空间存储新数据,会触发内存淘汰策略。Redis 提供了 六种内存淘汰策略:
1. noeviction(不淘汰):
- 机制:拒绝写入操作,直接返回错误。
- 优点:数据完全保留,适合读多写少场景。
- 缺点:可能影响写操作的正常进行。
2. allkeys-lru:
- 机制:从所有键中淘汰最近最少使用的键。
- 优点:优先保留热点数据。
- 缺点:需要额外维护 LRU 数据结构,增加内存开销。
3. volatile-lru:
- 机制:从设置了过期时间的键中淘汰最近最少使用的键。
- 优点:结合过期时间管理,减少无用数据。
- 缺点:如果大部分键没有过期时间,会导致淘汰效果不明显。
4. allkeys-random:
- 机制:随机淘汰键(所有键中随机选择)。
- 优点:实现简单,适用于无显著热点的场景。
- 缺点:可能导致热点数据被淘汰。
5. volatile-random:
- 机制:从设置了过期时间的键中随机淘汰。
- 优点:只淘汰可过期的键,减少重要数据被误删的概率。
- 缺点:淘汰效果不够精准。
6. volatile-ttl:
- 机制:从设置了过期时间的键中优先淘汰即将过期的键。
- 优点:更智能的淘汰策略,减少即将无效数据的占用。
- 缺点:需要频繁维护过期时间排序,开销较高。
如何设置Redis 的过期键删除策略和内存淘汰策略?
Redis 的过期键删除策略如何设置?
Redis 的过期键删除策略不能直接通过客户端设置,而是由 Redis 服务端自动处理。用户可以通过调整 Redis 配置文件来优化:
-
配置文件设置(redis.conf):
- 定期删除相关参数:
hz 10 # Redis 每秒的主循环频率,影响定期删除检查频率,默认是10。 - 惰性删除和定期删除均为 Redis 默认的机制,无需手动启用。
- 定期删除相关参数:
-
Java 示例:设置键的过期时间
使用 Java 的Jedis库:import redis.clients.jedis.Jedis; public class RedisExpirationDemo { public static void main(String[] args) { // 连接 Redis Jedis jedis = new Jedis("localhost", 6379); // 设置一个键,并指定 10 秒后过期 jedis.setex("key1", 10, "value1"); // 设置一个键,并使用 EXPIRE 命令指定过期时间 jedis.set("key2", "value2"); jedis.expire("key2", 20); System.out.println("key1: " + jedis.get("key1")); System.out.println("key2: " + jedis.get("key2")); // 关闭连接 jedis.close(); } }
Redis 的内存淘汰策略
通过设置 Redis 的 maxmemory 和 maxmemory-policy 配置。
在 Redis 配置文件 redis.conf 中:
# 配置过期键删除策略
# noeviction:不淘汰
# allkeys-lru:所有键中最近最少使用的键
# volatile-lru:仅过期键中最近最少使用的键
# allkeys-random:所有键中随机淘汰
# volatile-random:仅过期键中随机淘汰
# volatile-ttl:仅过期键中优先淘汰即将过期的键
maxmemory-policy volatile-lru
或者在运行时通过命令设置:
127.0.0.1:6379> CONFIG SET maxmemory-policy volatile-lru
示例:
# 设置 Redis 内存上限
maxmemory 256mb
# 设置内存淘汰策略
maxmemory-policy allkeys-lru
同样,也可以通过运行时命令设置:
127.0.0.1:6379> CONFIG SET maxmemory 268435456 # 设置为256MB
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru
实际项目中的应用场景
- 电商秒杀系统:
- 场景:限时优惠的商品数据需要设置过期时间,避免占用内存。
- 策略:
- 过期键删除:定期删除 + 惰性删除。
- 淘汰策略:volatile-ttl,优先清理即将过期的键。
- 电商秒杀场景需要设置过期时间,确保商品活动结束后,数据不再占用 Redis。
import redis.clients.jedis.Jedis;
public class RedisSeckillSystem {
public static void main(String[] args) {
// 连接 Redis
Jedis jedis = new Jedis("localhost", 6379);
// 设置过期时间的商品库存
String productKey = "product:1001:stock";
jedis.setex(productKey, 3600, "100"); // 设置商品库存,过期时间为1小时
// 模拟秒杀逻辑
int remainingStock = Integer.parseInt(jedis.get(productKey));
if (remainingStock > 0) {
jedis.decr(productKey); // 减少库存
System.out.println("秒杀成功!剩余库存:" + jedis.get(productKey));
} else {
System.out.println("秒杀结束,商品已售罄!");
}
// 关闭连接
jedis.close();
}
}
- 实时数据缓存:
- 场景:热点数据需要经常更新,避免无关数据占用内存。
- 策略:
- 淘汰策略:allkeys-lru,优先保留最常访问的热点数据。
- 热点数据缓存,使用 LRU 策略优先保留最近访问的数据。
import redis.clients.jedis.Jedis;
public class RedisCache {
public static void main(String[] args) {
// 连接 Redis
Jedis jedis = new Jedis("localhost", 6379);
// 配置 Redis 内存上限和淘汰策略
jedis.configSet("maxmemory", "256mb");
jedis.configSet("maxmemory-policy", "allkeys-lru");
// 模拟写入热点数据
for (int i = 1; i <= 1000; i++) {
String key = "user:" + i;
jedis.set(key, "data" + i);
}
// 模拟访问热点数据
jedis.get("user:1");
jedis.get("user:2");
jedis.get("user:3");
System.out.println("热点数据缓存模拟完成!");
// 关闭连接
jedis.close();
}
}
- 用户会话管理:
- 场景:用户登录状态需要定期过期。
- 策略:
- 过期键删除:惰性删除,仅在会话被访问时清理。
- 淘汰策略:volatile-lru,优先淘汰过期会话数据。
- 用户会话数据需要设置过期时间,采用
volatile-lru策略管理内存。
import redis.clients.jedis.Jedis;
public class RedisSessionManagement {
public static void main(String[] args) {
// 连接 Redis
Jedis jedis = new Jedis("localhost", 6379);
// 设置内存淘汰策略
jedis.configSet("maxmemory", "128mb");
jedis.configSet("maxmemory-policy", "volatile-lru");
// 模拟用户登录会话数据
String sessionKey = "session:user:123";
jedis.setex(sessionKey, 1800, "user_data"); // 设置会话数据过期时间为30分钟
// 检查会话是否有效
String sessionData = jedis.get(sessionKey);
if (sessionData != null) {
System.out.println("用户会话有效,数据:" + sessionData);
} else {
System.out.println("用户会话已过期!");
}
// 关闭连接
jedis.close();
}
}
通过合理组合过期键删除策略和内存淘汰策略,可以根据项目需求优化性能和资源利用率。