redis管道和事务
关于管道和事务的使用时机
背景
有这样一段代码
1 | |
这里同时应用了管道和事务,但是我认为有些多余。
管道
管道的作用是在于把多个命令合并成一次网络请求,交给redis执行,可以有效减少请求时间。
我做过测试,同样次数的请求,一条一条发送,需要35秒左右。5条合并发送一次,就只需要1.5秒左右。
由于redis响应请求是单线程的,所以管道内的请求不会被其他请求插队,可以保证其原子性
事务
redis事务很简单,使用multi开启,使用exec 提交,同时不会阻塞请求。按照书中的说法,其保证了当前执行的事务有着不被其他事务打断的权利。
关于redis事务是否具有原子性的问题上,我看到各个地方说法不太一致
- 《redis深度历险:核心原理和应用实践》中通过一个例子证明事务没有原子性
- 《redis使用手册》直接说redis具有ACID,没有佐证
- 菜鸟教程的说法又体现了其没有原子性
我使用docker的redis:6.2.3-alpine 测试以下脚本
1 | |
结果是
(error) EXECABORT Transaction discarded because of previous errors.
再执行
1 | |
没有对应的值,说明至少6.2.3这个版本的事务是有原子性的
在4.0.14-alpine中执行以下脚本
1 | |
exec结果如下
1 | |
get 能够拿到值,可见这个版本不具有原子性
为了控制变量,我又把这个命令在6.2.3上执行了一下,结果两个结果居然一样,key2 同样是有值的
这。。。我就不知道该说什么了。
只好求助网络了。
网上给了一个体现原子性的例子,就是在事务中执行一个不存在的命令(随便输入点什么)。
这样exec就是报错,而不执行。
这和我的理解不太一样,我原本理解是,redis事务命令入队时会检测命令是否合法,合法则入队,也就是返回QUEUED
现在看可能是命令不合法,会将事务置为出错,入不入队都不重要了,exec时会直接提示错误。
而我在4.x中执行的错误是一种使用错误,必须执行才能得知的。redis认为这种错误是程序员的错,不会为此大费周章,去做回滚之类的操作。这就是一些人说redis没有原子性的原因
再测试一下其他特性,按以下流程执行命令
| clientA | clientB |
|---|---|
| set key1 1 | |
| multi | |
| get key1 | |
| set key1 2 | |
| get key1 | |
| set key1 3 | |
| get key1 | |
| exec | |
| get key1 |
结果如下

可以看出,事务的隔离级别类似于未提交读,
问题
如果按照以上说法,管道同样拥有不会被别的请求打断的功能,那事务还有必要吗?
回答
后来我在实现限流时,尝试在管道close之前,获取某条命令的执行结果作判断,结果报了异常。
细想一下,管道只会在close之后才发送请求,那么肯定是无法获取命令结果的。这就是管道的局限性
所以后来我使用脚本实现,就能保证操作的原子性,同时不过分浪费网络请求
当然也可以使用事务实现
还是没看出来事务有多大意义
顺便提一句,网上好多关于redis的博客其实就是把官方文档给翻译了一遍,还搞得跟自己研究出来的一样