核心要点
Memcached 的过期时间是一个非常重要的特性,它主要用于:

- 自动失效:当数据到达指定时间后,Memcached 会自动将其从内存中删除,无需客户端手动干预。
- 减少数据库压力:对于频繁访问但数据不常变更的信息(如配置、用户会话、热点数据),可以设置一个合理的过期时间,保证数据的“新鲜度”,同时避免频繁查询数据库。
- 内存管理:通过自动清理过期数据,Memcached 可以高效地利用有限的内存空间。
如何在 Java 客户端中设置过期时间
在 Java 中,我们通常使用一个客户端库来与 Memcached 交互,最流行的是 Xmemcached 和 SpyMemcached,下面以 Xmemcached 为例进行说明,因为它性能更好,API也更现代化。
Xmemcached 设置过期时间
Xmemcached 的 set、add、replace 等方法都支持一个 expiry 参数来设置过期时间。
过期时间参数的单位是秒。
基本用法示例:

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.XMemcachedClient;
import net.rubyeye.xmemcached.exception.MemcachedException;
import java.util.concurrent.TimeoutException;
public class MemcachedExpiryExample {
public static void main(String[] args) {
// 1. 创建 Memcached 客户端
// 参数为 Memcached 服务器地址和端口
try (MemcachedClient client = new XMemcachedClient("localhost", 11211)) {
// --- 设置不同类型的过期时间 ---
// a. 设置一个永不失效的项 (过期时间为 0)
// 注意:永不失效并不意味着永远存在,它可能会因为内存不足而被 LRU 算法淘汰。
// "never expire" 的特殊值是 0。
client.set("user:session:123", 0, "some_session_data");
System.out.println("设置了一个永不失效的键: user:session:123");
// b. 设置一个 10 秒后过期的项
client.set("config:app_settings", 10, "{\"theme\":\"dark\",\"lang\":\"zh-CN\"}");
System.out.println("设置了一个10秒后过期的键: config:app_settings");
// c. 设置一个 1 小时 (3600秒) 后过期的项
client.set("cache:product_list", 3600, "product_data_string_or_object");
System.out.println("设置了一个1小时后过期的键: cache:product_list");
// --- 验证过期 ---
System.out.println("\n立即获取 config:app_settings: " + client.get("config:app_settings"));
// 等待11秒,让数据过期
Thread.sleep(11000);
System.out.println("11秒后再次获取 config:app_settings: " + client.get("config:app_settings"));
// 输出应该是 null,因为数据已经过期并被删除
} catch (Exception e) {
e.printStackTrace();
}
}
}
SpyMemcached 设置过期时间
SpyMemcached 的用法类似,也是在 set 方法中传入一个 int 类型的过期时间(单位秒)。
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.internal.OperationFuture;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
public class SpyMemcachedExpiryExample {
public static void main(String[] args) {
try {
MemcachedClient client = new MemcachedClient(
new InetSocketAddress("localhost", 11211));
// 设置一个 30 秒后过期的项
OperationFuture<Boolean> setFuture = client.set("key:with:expiry", 30, "Hello, Memcached!");
System.out.println("Set operation status: " + setFuture.get());
// 获取值
System.out.println("Get key:with:expiry: " + client.get("key:with:expiry"));
// 等待31秒
TimeUnit.SECONDS.sleep(31);
// 再次获取,应为 null
System.out.println("Get key:with:expiry after 31s: " + client.get("key:with:expiry"));
client.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
过期时间的特殊值
Memcached 的过期时间参数支持一些特殊值,这在实际开发中非常有用。
| 值 (单位: 秒) | 含义 |
|---|---|
0 |
永不失效,数据不会因为过期而被删除,但可能会因为内存不足而被 LRU(最近最少使用)算法淘汰。 |
| *`< 60 60 24 30` (即小于30天)** | 表示一个具体的秒数。3600 表示1小时后过期,这是最常用的方式。 |
| *`>= 60 60 24 30` (即大于等于30天)** | Memcached 会将其解释为一个 Unix 时间戳(UTC时间),而不是从现在开始的秒数。 |
使用 Unix 时间戳的示例:
如果你想设置一个在某个特定日期和时间过期的项,可以计算其 Unix 时间戳。

import java.time.Instant;
import java.time.temporal.ChronoUnit;
// 假设今天是 2025-10-27 10:00:00 UTC
// 我们想设置它在今天的 23:59:59 UTC 过期
Instant now = Instant.now();
Instant expiryTime = now.plus(13, ChronoUnit.HOURS).plus(59, ChronoUnit.MINUTES).plus(59, ChronoUnit.SECONDS);
// 获取 Unix 时间戳(秒)
long expiryTimestamp = expiryTime.getEpochSecond();
// 使用 Xmemcached 设置
client.set("daily_report_summary", (int) expiryTimestamp, "report data for 2025-10-27");
过期时间的底层机制
了解 Memcached 如何处理过期时间,有助于更好地使用它。
惰性删除
Memcached 主要采用 惰性删除 机制。
- 工作原理:当客户端请求数据时,Memcached 在返回数据之前,会检查这个数据是否已经过期,如果已过期,它不会返回数据,而是直接将其从内存中删除,然后向客户端返回“未找到”(类似
null)。 - 优点:对性能影响小,因为它只在数据被访问时才进行检查,不需要后台线程去扫描所有数据。
- 缺点:已过期的数据会一直占用内存,直到有客户端再次访问它,如果某些数据过期后不再被访问,它们就会“赖”在内存里,造成浪费。
定期删除
为了弥补惰性删除的不足,Memcached 还有一个 定期删除 机制。
- 工作原理:Memcached 内部有一个后台线程,它会每隔一段时间(通常是1秒)随机检查一部分(每个 slab 中随机检查一个 item)数据的过期时间。
- 优点:可以更及时地清理掉大量过期但未被访问的数据,减轻惰性删除的压力,防止内存被长期无效数据占用。
- 缺点:这个检查是随机的,所以不能保证所有过期数据都能被立即发现和删除。
Java 开发中的最佳实践
-
设置合理的过期时间:
- 不要过长:避免将热点数据设置成永不过期,这会导致数据“陈旧”,并可能占用过多内存。
- 不要过短:过期时间太短会导致缓存频繁失效,穿透到后端数据库,增加数据库压力,反而失去了缓存的意义。
- 根据业务场景定:
- 用户会话:30 分钟到几小时。
- 热点商品信息:几分钟到几小时。
- 系统配置:可以设置较长的时间(如几小时或1天),或者当配置变更时主动删除缓存。
- 验证码:5 分钟。
-
主动更新 vs. 被动过期:
- 被动过期:依赖
set时设置的expiry时间,这是最简单的方式。 - 主动更新:当数据库中的数据被修改时,主动去删除或更新 Memcached 中的对应项,这可以保证缓存中的数据与数据库的强一致性,适用于对数据一致性要求高的场景。
- 被动过期:依赖
-
处理缓存穿透:
- 缓存穿透是指查询一个根本不存在的数据,由于缓存中没有,请求会直接打到数据库上。
- 解决方案:即使数据库中没有的数据,也将其在缓存中存一个“空值”或特殊标记,并设置一个较短的过期时间(如1分钟),这样下次请求就不会再打到数据库了。
-
使用连接池:
Xmemcached 和 SpyMemcached 都内置了高效的连接池,确保在
