Memcached incr 与 decr 命令(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在分布式系统与高性能应用开发中,Memcached incr 与 decr 命令是处理数值型数据增减的核心工具。它们以极低的延迟和高并发性能,解决了传统数据库在计数场景下的锁竞争问题。无论是统计网页点击量、管理购物车库存,还是实现分布式锁,这两个命令都能提供简洁高效的解决方案。本文将从基础概念、工作原理到实战案例,逐步解析其应用场景与技术细节。


一、基础概念:什么是 incr 和 decr?

Memcached incr 命令用于将存储的数值键值递增一个指定步长,默认步长为 1;decr 命令则相反,将数值键值递减一个步长。其核心特性是原子性,即操作在单个服务器内是不可分割的,避免了多线程/多进程下的数据竞争问题。

形象比喻

可以把 incr/decr 想象为“智能计数器”:

  • 每次调用 incr,就像按下电梯楼层按钮,系统内部直接更新当前楼层,无需先读取再写入;
  • 而传统读-改-写流程,则像多人同时抢按电梯按钮,可能导致楼层跳变或重复。

语法与参数

incr key [step]  
decr key [step]  
  • key:要操作的键名
  • step(可选):递增/递减的步长,默认为 1

注意:这两个命令仅对数值型存储有效。若键不存在或值非数字,操作会失败并返回 0。


二、工作原理:原子操作与内存机制

1. 内存存储结构

Memcached 将键值对存储为内存中的slab 对象。当执行 incr/decr 时:

  1. 直接定位到键对应的内存地址;
  2. 通过CAS(Compare and Swap)指令快速验证并更新值;
  3. 无需序列化/反序列化,操作时间复杂度为 O(1)。

2. 原子性保障

原子性通过 CPU 的锁总线机制实现:

  • 操作期间,其他线程无法修改同一内存区域;
  • 即使百万级并发请求,也能保证计数结果的准确性。

对比传统数据库

场景Memcached incr/decr数据库 UPDATE 操作
延迟(典型值)<1ms几十到几百毫秒
并发能力(QPS)10万+几千至万级
数据一致性强保证需显式加锁

三、核心使用场景与案例

场景 1:实时计数(如网页 PV 统计)

需求:记录某文章的浏览量,要求每秒支持 1 万次请求。

import memcache  

mc = memcache.Client(['127.0.0.1:11211'])  
key = "article:123:view_count"  

mc.add(key, 0, time=3600)  

current = mc.incr(key)  
print(f"当前浏览量:{current}")  

优势:无需数据库交互,避免了频繁写入导致的锁表问题。

场景 2:库存扣减(电商秒杀场景)

挑战:商品库存为 100 件,需保证并发扣减时不超卖。

mc.add("product:456:stock", 100)  

while True:  
    stock = mc.decr("product:456:stock", 1)  
    if stock >= 0:  
        # 扣减成功,执行下单操作  
        break  
    else:  
        # 库存不足,回滚事务  
        mc.incr("product:456:stock", 1)  
        print("库存不足")  
        break  

注意:需结合业务逻辑处理负值情况,此处仅为简单示例。

场景 3:分布式限流器

需求:限制某个接口每秒最多 100 次请求。

def check_rate_limit(client_ip):  
    key = f"rate_limit:{client_ip}"  
    now = int(time.time())  
    # 每秒重置计数器  
    if not mc.add(key, 1, time=1):  
        current = mc.incr(key)  
        if current > 100:  
            return False  # 超限  
    return True  

通过time参数自动过期,实现基于滑动时间窗口的限流。


四、关键注意事项与错误处理

1. 数据类型约束

incr/decr 仅支持数值型存储。若键值非数字(如字符串),操作会失败并返回 0:

set key "hello"  
incr key → 返回 0,且键值不变  

2. 键不存在的处理

若键未初始化,incr/decr 默认返回 0。建议先用add命令设置初始值:

add counter 0 3600 1  
1  
END  
incr counter → 成功返回 2  

3. 负数溢出问题

当执行decr导致值小于 0 时:

  • Memcached 1.4.14+ 版本允许负数;
  • 旧版本会停止减法并返回 0。
mc.set("balance", 50)  
mc.decr("balance", 100) → 返回 -50(新版本)或 0(旧版本)  

4. 多服务器集群场景

若使用多节点集群:

  • 键的分布遵循一致性哈希,同一键始终落在固定节点;
  • 需确保所有操作访问同一节点,避免跨节点竞争。

五、进阶技巧与最佳实践

技巧 1:组合使用 add 和 incr

if not mc.add(key, initial_value):  
    mc.incr(key)  

技巧 2:批量操作优化

Memcached 不支持批量 incr/decr,但可通过客户端库合并请求:

pipeline = mc.pipeline()  
pipeline.incr("counter1")  
pipeline.decr("counter2", 5)  
results = pipeline.execute()  

技巧 3:与 get 命令配合

current = mc.gets(key)  
mc.cas(key, current + 1)  # 需配合 gets/cas 实现  

结论

Memcached incr 与 decr 命令凭借其原子性和高性能,在计数场景中展现了独特优势。通过本文的案例解析与代码示例,开发者可以掌握其核心用法,并结合业务需求设计出高并发、低延迟的解决方案。无论是实时统计、库存管理还是分布式限流,合理运用这两个命令都能显著提升系统性能与可靠性。

提示:在生产环境中,建议搭配监控工具(如 Nagios 或 Prometheus)跟踪 incr/decr 的 QPS 和错误率,及时发现潜在问题。

最新发布