Memcached CAS 命令

Memcached CAS 命令详解(2025 版)

CASCheck and Set)是 Memcached 的 乐观锁核心机制,用于 解决并发更新冲突,是高并发场景下的 原子更新神器

场景:多个线程同时读取 → 修改 → 写回,set 会导致覆盖CAS 保证 “只有数据未被修改才更新”


1. 什么是 CAS?

1. get(带 cas_token)
2. 修改 value
3. cas(检查 cas_token 是否一致)
   ├─> STORED(未被他人修改)
   └─> EXISTS(已被他人修改)

类似数据库的 UPDATE ... WHERE version = old_version


2. 核心命令:getscas

2.1 gets — 获取带 CAS token 的值

gets <key>\r\n

响应格式

VALUE <key> <flags> <bytes> <cas_token>\r\n
<value>\r\n
END\r\n
字段说明
<cas_token>64 位整数,每更新一次递增

2.2 cas — 带版本检查的写入

cas <key> <flags> <exptime> <bytes> <cas_token> [noreply]\r\n
<value>\r\n
参数说明
<cas_token>gets 获取的版本号
响应STORED / EXISTS / NOT_FOUND
  • STORED → 版本一致,更新成功
  • EXISTS → 版本不一致,已被他人修改
  • NOT_FOUND → key 不存在

3. telnet 完整流程示例

telnet 127.0.0.1 11211
# 1. 首次写入
set counter:likes 0 0 1
0
STORED

# 2. 读取带 cas_token
gets counter:likes
VALUE counter:likes 0 1 1
0
END

# 3. 模拟两个客户端并发修改
# 客户端 A:
cas counter:likes 0 0 1 1
1
STORED

# 客户端 B(使用旧 cas_token):
cas counter:likes 0 0 1 1
2
EXISTS    ← 失败!被 A 修改过

# 客户端 B 重新 gets
gets counter:likes
VALUE counter:likes 0 1 2   ← cas_token 已变为 2
1
END

# 客户端 B 再 cas
cas counter:likes 0 0 1 2
2
STORED

4. 客户端使用 CAS

Python(pymemcache)

from pymemcache.client.base import Client
from pymemcache import ClientError

client = Client(('127.0.0.1', 11211))

# 初始化
client.set('user:1001:score', 100)

# CAS 更新
def increment_score(uid, delta):
    key = f'user:{uid}:score'
    while True:
        result = client.gets(key)
        if result is None:
            return False

        value, cas_token = result
        new_value = int(value) + delta

        try:
            if client.cas(key, str(new_value), cas_token):
                print(f"更新成功: {value} → {new_value}")
                return True
            else:
                print("版本冲突,重新尝试...")
        except ClientError as e:
            if "EXISTS" in str(e):
                continue
            raise

# 测试
increment_score(1001, 5)  # 100 → 105

PHP

$mc = new Memcached();
$mc->addServer('127.0.0.1', 11211);

$mc->set('visits', 100);

// CAS 循环
do {
    $data = $mc->get('visits', null, $cas);
    $new = $data + 1;
} while ($mc->cas($cas, 'visits', $new) === false);

echo "当前访问: " . $mc->get('visits');

Java(XMemcached)

CASValue<Object> casValue = client.gets("counter");
long cas = casValue.getCas();
Object value = casValue.getValue();

CASResponse response = client.cas("counter", 0, (Integer)value + 1, cas);
if (response == CASResponse.OK) {
    System.out.println("CAS 成功");
} else if (response == CASResponse.EXISTS) {
    System.out.println("版本冲突");
}

5. 经典应用场景

场景说明
计数器点赞、浏览量、库存扣减
购物车并发添加商品
秒杀库存cas 扣减,防止超卖
配置热更新多节点同时修改配置
状态机订单状态流转

秒杀库存扣减(Python)

def seckill(stock_key, user_id):
    while True:
        result = client.gets(stock_key)
        if not result:
            return False
        stock, cas = result
        stock = int(stock)
        if stock <= 0:
            return False

        if client.cas(stock_key, str(stock - 1), cas):
            # 扣减成功,记录用户
            client.sadd('seckill:users', user_id)
            return True
        # 否则重试

6. CAS 工作原理(底层)

每更新一次 value → cas_token + 1
操作cas_token 变化
set / add / replace+1
append / prepend+1
incr / decr+1
delete失效

gets 返回的 cas_token当前最新版本


7. 常见错误与响应

响应含义处理
STORED版本一致,更新成功完成
EXISTS版本冲突重新 gets → 修改 → cas
NOT_FOUNDkey 不存在初始化或放弃
ERROR命令格式错检查参数

8. 最佳实践

建议说明
所有并发写用 CAS避免覆盖
循环重试EXISTSgets 重来
设置最大重试次数防死循环
结合本地缓存减少 gets 开销
避免大 valueCAS 适合小数据(如计数器)

带重试限制的 CAS

def cas_update(key, modify_fn, max_retries=5):
    for i in range(max_retries):
        result = client.gets(key)
        if not result:
            return False
        value, cas = result
        new_value = modify_fn(value)
        if client.cas(key, new_value, cas):
            return True
    raise Exception("CAS 重试失败")

9. 性能对比

方式并发安全性能
set最高
add/replace部分
CAS中等(多一次 gets

推荐:读多写少用 set,写并发用 CAS


10. 小结:CAS 流程图

START
  ↓
gets key → (value, cas_token)
  ↓
修改 value → new_value
  ↓
cas key ... cas_token new_value
  ↓
STORED? → YES → 结束
  ↓ NO
EXISTS? → 重新 gets(循环)

练习建议

  1. 用 telnet 模拟 两个客户端抢购 1 件商品
  2. 用 Python 实现 点赞计数器(支持 1000 并发)
  3. 实现 订单状态流转待支付 → 已支付(防并发)

需要 incr/decr 原子计数、delete 失效、多 key 事务替代方案?继续问我!

文章已创建 2481

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部