RedisTemplate的increment()返回Long类型,key不存在时自动初始化为0再自增并返回结果;若value非数字字符串则抛InvalidDataAccessResourceUsageException。

increment() 返回值类型容易踩坑
redisTemplate.opsForValue().increment() 默认返回 Long,但如果你的 Redis key 初始不存在,它会从 0 开始自增并返回 1;如果 key 存在但存的是非数字字符串(比如 "abc"),会直接抛 org.springframework.dao.InvalidDataAccessResourceUsageException,底层是 ERR value is not an integer or out of range。
实操建议:
- 确保 key 对应的 value 是纯数字字符串(Redis 本身不区分 int/string,但
INCR命令要求可解析为 64 位有符号整数) - 首次生成序列号前,不要手动
set("seq:order", "0"),而应直接调用increment()—— 它会自动初始化为 0 再加 1 - 若需支持大数(超
Long.MAX_VALUE),得换StringRedisTemplate+ 手动解析,RedisTemplate的increment()不支持BigInteger
并发场景下 increment() 是原子的,但业务逻辑未必
increment() 底层调用 Redis 的 INCR 命令,天然具备原子性,多个服务实例同时调用不会产生重复值。但很多人忽略后续步骤:比如把自增结果拼成订单号 "ORD-" + seq,再存进 MySQL —— 这个“读-拼-写”过程不是原子的。
常见错误现象:两个请求几乎同时拿到相同 seq,生成一样订单号,导致数据库唯一键冲突。
实操建议:
- 序列号生成和业务实体落库必须放在同一事务中(如用
@Transactional),且 MySQL 表主键/唯一索引要基于该序列号 - 避免“先取号 → 再查 DB 是否存在 → 再插入”的三步检查,这会引入竞态条件
- 如果需要前缀+时间戳+序列号复合结构(如
ORD_20240520_00001),建议用 Lua 脚本封装整个生成逻辑,保证 Redis 端原子
RedisTemplate 类型匹配必须一致
如果你的 RedisTemplate 配置了 StringRedisSerializer 作为 value 序列化器,那 increment() 就能正常工作;但如果误配成 Jackson2JsonRedisSerializer 或 GenericJackson2JsonRedisSerializer,哪怕 key 存的是 "123",反序列化后可能变成 Integer 对象或 JSON 字符串,increment() 会静默失败或抛 ClassCastException。
实操建议:
- 专门用于序列号的
RedisTemplate,应独立配置,value 序列化器固定为StringRedisSerializer - 不要复用业务缓存用的通用
RedisTemplate,尤其当它默认序列化 Java 对象时 - 可通过
redis-cli直接get seq:order检查实际存储值是否为纯数字字符串,而不是"123"或{"@class":"java.lang.Integer","val":123}
过期时间不能直接用 increment() 设置
increment() 本身不支持设置 TTL,你无法在一个原子操作里完成“自增 + 设过期”。常见错误是先 increment(),再 expire(),但这俩命令之间存在时间窗口,可能刚自增完服务宕机,key 就永久残留。
实操建议:
- 如果需要带过期的计数器(如限流、临时 ID),改用
redisTemplate.opsForValue().getAndIncrement()配合redisTemplate.expire(),并在调用前确保 key 不存在(可用setIfAbsent()初始化) - 更稳妥的做法:用 Lua 脚本封装
INCR和EXPIRE,例如EVAL "local v = redis.call('INCR', KEYS[1]); redis.call('EXPIRE', KEYS[1], ARGV[1]); return v" 1 seq:order 3600 - 注意:Redis 7.0+ 支持
INCR EX 3600语法,但 Spring Data Redis 当前(6.2.x)尚未封装该特性,仍需脚本或原生命令
真正麻烦的从来不是调用 increment() 这一行代码,而是它背后隐含的状态一致性假设 —— Redis 的原子性只管自己,不管你的 MySQL、MQ 或下游 HTTP 调用。


















