前言
Redis 的缓存过期策略是指当数据存储在 Redis 中时,如何处理到达特定生命周期末端的数据。Redis 主要使用两种策略来管理键的过期:惰性过期(Lazy Expiration)和定期删除(Periodic Deletion)。
惰性过期
当客户端访问一个键时,Redis 会检查这个键是否已经达到过期时间。如果已过期,Redis 就会删除它,然后返回一个错误。
源码中(源码是C写的)这样写到:
int expireIfNeeded(redisDb *db, robj *key) { // 检查键是否有过期时间设置 if (!key->expire) return 0; // 获取当前时间 mstime_t now = mstime(); // 判断键是否过期 if (now > key->expire) { // 执行删除操作 dbDelete(db, key); return 1; } return 0; } |
expireIfNeeded 函数负责检查键是否过期。
定期删除
Redis 每隔一段时间执行一次自动清理操作。它会随机地选择一些键,并检查它们是否过期。过期的键会被删除。
源码中(源码是C写的)这样写到:
void activeExpireCycle(int type) { // 从数据库中随机抽取一部分键 for (int i = 0; i < REDIS_EXPIRELOOKUPS_PER_CRON; i++) { // 随机选择数据库 int db_id = rand() % server.dbnum; redisDb *db = server.db[db_id]; // 随机选择键 if (dictSize(db->expires) == 0) continue; dictEntry *de = dictGetRandomKey(db->expires); // 检查键是否过期 expireIfNeeded(db, dictGetKey(de)); } } |
activeExpireCycle 函数来定期检查和删除过期的键。
过期策略选择
Redis 的过期策略是折衷的。它不保证立即删除所有过期键,但保证过期键不会被永久访问到
注意事项
- 内存使用:过期键可能在过期后不会立即被删除,因此可能会暂时占用更多内存。
- 性能考量:定期删除策略避免了锁定 Redis 实例以删除大量的过期键,但它仍然需要使用一定的 CPU 资源来随机检查键。
- 持久化:当 Redis 重启时,它会根据持久化文件(AOF 或 RDB)来恢复过期键的状态。
代码案例(就以我相对擅长的java为例)
import redis.clients.jedis.Jedis; public class RedisExpirationDemo { public static void main(String[] args) { // Connect to the Redis server Jedis jedis = new Jedis("localhost", 6379); try { // Set a key with a value and an expiration time in seconds (10 seconds in this case) String key = "myKey"; String value = "Hello, Redis!"; int expireTime = 10; // Key expires in 10 seconds jedis.setex(key, expireTime, value); System.out.println("Key set with expiration time."); // Get the value right away String currentValue = jedis.get(key); System.out.println("Value immediately after setting: " + currentValue); // Wait for the key to expire Thread.sleep(10000); // Try to get the value after expiration time currentValue = jedis.get(key); System.out.println("Value after expiration: " + (currentValue == null ? "null (key expired)" : currentValue)); } catch (InterruptedException e) { e.printStackTrace(); } finally { // Close the connection jedis.close(); } } } |
在上述代码中,我们使用 setex 方法设置了一个键,为其提供了一个过期时间(10秒)。随后,我们尝试立即获取该键,应该能够获取到值。然后程序休眠10秒,再次尝试获取该键时,由于键已经过期,所以应该获取不到值。
请注意,为了简化代码,这里没有包含异常处理和资源管理的最佳实践,如在生产环境中应使用 try-with-resources 语句来自动关闭 Jedis 实例。此外,确保 Redis 服务在本地运行并监听默认端口(6379)。