Skip to content

Memcached - 获取 CAS 数据

CAS(Check-And-Set,检查并设置)是 Memcached 的一个强大功能,允许您执行原子性的读-修改-写操作。它能防止多个客户端同时尝试更新同一个键,从而可能覆盖彼此更改的竞态条件。gets 命令是 CAS 操作的第一步;它检索一个项及其唯一的 64 位 ‘CAS 令牌’。

语法与 get 命令相同,但服务器的响应中包含一个额外的值:CAS 令牌。

gets key
# 首先,设置一个要操作的值
set user:profile 0 900 20
{"name":"Alice","visits":10}
STORED
# 现在,使用 gets 获取值及其 CAS 令牌
gets user:profile
VALUE user:profile 0 20 52
{"name":"Alice","visits":10}
END

在 VALUE 行中,数字 52 是此项当前状态的唯一 CAS 令牌。必须将此令牌与 cas 命令一起发送回去,才能成功更新该项。如果另一个客户端首先修改了该项,CAS 令牌将发生变化,您的更新将失败。

完整的 Check-And-Set 工作流包括三个步骤:1) 使用 gets 检索值及其令牌。2) 在应用程序代码中修改值。3) 使用 cas 将新值写回,并提供原始令牌。只有当服务器上的令牌与您提供的令牌仍然匹配时,更新才会成功。

import net.spy.memcached.CASValue;
import net.spy.memcached.CASResponse;
import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;
public class CasExample {
public static void main(String[] args) {
MemcachedClient mcc = null;
try {
mcc = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
System.out.println("Connection to server successful.");
// 1. 初始化我们要原子修改的值
String key = "user:settings";
mcc.set(key, 900, "theme:dark").get(); // .get() 等待操作完成
System.out.println("Initial value set to: 'theme:dark'");
// --- CAS 操作开始 ---
// 2. 获取值及其 CAS 令牌
CASValue<Object> casValue = mcc.gets(key);
if (casValue == null) {
System.err.println("缓存未命中。找不到键:" + key);
return; // 如果键不存在则退出
}
System.out.println("Retrieved value: '" + casValue.getValue() + "' with CAS token: " + casValue.getCas());
// 3. 在应用程序逻辑中修改值
String newValue = "theme:light";
// 4. 尝试使用 cas() 命令更新
// cas 方法接受:键、原始 CAS 令牌、过期时间以及新值。
CASResponse response = mcc.cas(key, casValue.getCas(), 900, newValue);
System.out.println("Attempting CAS update with new value '" + newValue + "'...");
System.out.println("CAS Operation Result: " + response);
if (response == CASResponse.OK) {
System.out.println("Update successful!");
} else {
System.out.println("Update failed. The value was likely modified by another client.");
}
// 为了证明,我们获取最终值
System.out.println("Final value in cache: " + mcc.get(key));
} catch (Exception e) {
System.err.println("An error occurred: " + e.getMessage());
} finally {
if (mcc != null) {
mcc.shutdown();
}
}
}
}
Connection to server successful.
Initial value set to: 'theme:dark'
Retrieved value: 'theme:dark' with CAS token: 53
Attempting CAS update with new value 'theme:light'...
CAS Operation Result: OK
Update successful!
Final value in cache: theme:light

当 cas 操作返回 EXISTS(表示数据被另一个进程更改)或 NOT_FOUND(表示数据已被删除)时,您的应用程序不应放弃。标准做法是实现一个重试循环。此循环将重新运行整个 gets -> 修改 -> cas 周期,直到成功或达到最大重试次数以防止无限循环。