redis事务初探

redis事务初探

1.问题情景表述

背景:工作中需要维护一个tcc管理后台,主要用来人工干预tcc事务。tcc事务数据已哈希结构存储在redis服务器上,后台的管理任务也就落实于将当下的tcc事务数据从redis中捞出来,并予以展示,并且在管理界面上赋予“重置”“删除”“确认”等操作入口。操作入口对应的实现就是将指定tcc事务的存储于redis中的数据结构的某一个表示状态的字段设置为相应的状态表征,而问题就出在这个节骨眼上。

问题:当人工操作某条tcc事务数据时,该事务可能已经被业务系统正常消化调了,所以此时再操作“重置”,“删除”,“确认”,那么就相当于人工往redis中添加了一条tcc事务数据。要命的是,这条数据并不完整,因为只有状态字段。而这条不完整的数据,被业务系统处理的时候必然处理异常而报警(哎,相关业务系统为什么要主动处理这条数据呢?是tcc框架推过去的吗?不是tcc框架推过去的,是运行在相关业务中的tcc逻辑主动从redis中拉取得,拉取时对自己感兴趣的key做处理)。

问题说明:redis中是没有数据结构的概念的。我们平常的做法是将一个数据结构使用redis的hash结构来保存,然后redis是不会保证这个数据结构的整体性的:redis的数据操作没有getter,setter限制(其实数据库性质的工具都没有这种程序级别的逻辑限制的),而可以直接操作某个键的值,比如随便的设置字段Field的值,随意增加字段和删除字段。这些操作在数据存储级别当然是没有任何问题的,但是上升到代码级别,就会导致很多问题,比如字段为null(对应删除Filed),整体结构破坏(对应操作增加了Field,当然,高级点的序列化应该能兼容这个问题)。

2.redis事务

解决上面的问题,就是在操作的时候,做一个key值的存在检查:发现当前key还在(即该条tcc事务数据还没有被业务系统消化掉),再来设置hash结构中状态字段Field的值。而且“检查-设置”必须是原子性的,不能被打断。

“多条命令的原子执行”是中常见需求,redis提供了multi操作和pipeline就是用来这么做的。具体可以参见redis.io文档关于"MULTI",“WATCH”,“UNWATCH”,“EXEC”,“DISCARD”,等命令
关于redis事务,可以参见官方文档 redis-transaction

2.1 为什么不能使用redis-transaction

reason:然而java版本中比较流行的一个redis客户端jedis,其官网明确指出,jedis的transaction内部不能使用中间结果,原文如下:

 //Redis does not allow to use intermediate results of a transaction within that same transaction. This does not work:
 // this does not work! Intra-transaction dependencies are not supported by Redis!
jedis.watch(...);
Transaction t = jedis.multi();
if(t.get("key1").equals("something"))
   t.set("key2", "value2");
else 
   t.set("key", "value");

相关的解决策略:

  • watch实现key的监控,不适合key不存在的情况,而且也会遇到“使用中间状态”的问题。
  • redis原生命令,比如SETNX,HSETNX;其中HSETNX的语义是如果key不存在才设置哈希的某个字段。如果判断条件相反,这个命令再合适不过了。

2.2 最终的解决方案——lua脚本

其实redis事务这方面的发展:将逐渐弃用Multi五大工具,而使用lua。这一点在官网redis-transaction说的很清楚了。

redis在外部可以调用lua脚本,而lua脚本内部又可以调用redis命令,两者的数据类型一一对应。

第一个lua脚本实例:

if redis.call("get",KEYS[1]) == ARGV[1] then 
    return redis.call("del",KEYS[1])  
else  
    return 0  
end 

执行lua脚本:

redis-cli --eval singlelock.lua sdfssd , abcd

解释:用–eval可以执行lua脚本文件。注意,使用,来区分KEYS和ARGV这两个参数列表,其中,两边的空格不能省略。官网也有解释
当然,jedis中执行lua脚本就跟简单了,参见jedis源码。

总结

  1. redis存储数据结构的松散型而导致的结构破坏问题是很普遍的现象。
  2. redis的transaction的五大工具:WATCH,UNWATCH,MULTI,EXEC,DISCARD,没有pepiline。pepiline是jedis提供的工具。
  3. 关于redis事务的特点
    1. 多个命令打包发送,然后在服务端顺序执行而且不会被其他client发来的命令打断,因为redis是单线程的。
    2. 执行失败的命令不影响其他命令的执行,参见
    3. 有执行命令执行失败,整个multi不会回滚
    4. 不能使用中间状态
  4. 有网上说,学好redis,先学lua脚本,比较赞同。

继续学习

  1. redis官网文档:https://redis.io/documentation
  2. jedis wiki:https://github.com/xetorthio/jedis
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值