Redis事务的分析及改进

Redis事务的分析及改进

Redis的事务特性

数据ACID特性满足了几条?
为了保持简单,redis事务保证了其中的一致性和隔离性;
不满足原子性和持久性;

原子性

redis事务在执行的中途遇到错误,不会回滚,而是继续执行后续命令;(违反原子性)

事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作;
中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做;
比如:

redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK

如果在set b bbb处失败,set a已成功不会回滚,set c还会继续执行;

持久性

事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,所以事务的持久性由 Redis 所使用的持久化模式决定:

  • 在单纯的内存模式下,事务肯定是不持久的。
  • 在 RDB 模式下,服务器可能在事务执行之后、RDB 文件更新之前的这段时间失败,所以 RDB 模式下的 Redis 事务也是不持久的。
  • 在 AOF 的“总是 SYNC ”模式下,事务的每条命令在执行成功之后,都会立即调用 fsync 或 fdatasync 将事务数据写入到 AOF 文件。但是,这种保存是由后台线程进行的,主线程不会阻塞直到保存成功,所以从命令执行成功到数据保存到硬盘之间,还是有一段非常小的间隔,所以这种模式下的事务也是不持久的。
  • 其他 AOF 模式也和“总是 SYNC ”模式类似,所以它们都是不持久的。

隔离性和一致性

redis事务在执行的过程中,不会处理其它命令,而是等所有命令都执行完后,再处理其它命令(满足隔离性)
redis事务在执行过程中发生错误或进程被终结,都能保证数据的一致性;(详见参考资料1)

redis事务的缺陷

除了不保证原子性和持久性,在实际使用中还有以下问题:

1) 遇到有查询的情况穿插在事务中间,不会返回结果;

设置事务开始标志后,所有的命令都是queued,即使是查询指令;

如果后续的更新操作需要依赖于前面的查询指令,那redis事务就无法有效的完成任务;

例如:

redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> get b
QUEUED
业务逻辑...
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) bbb
3) OK

第二步 get a 返回的是queued,并不是a的查询结果,
如果后续的set操作依赖于get的结果(存在依赖业务逻辑),就不能将get操作放在事务操作中;

2) 事务中的每条命令都与redis服务器进行了一次网络交互;

redis 事务指定开始后,执行一个事务返回的都是queued,那这个入队操作是在客户端实现,还是在服务器端实现的?

查看源码,很容易发现是在服务器端实现;

在Redis.c中有这么一段:

int processCommand(redisClient *c) {
...
    /* Exec the command */
    if (c->flags & REDIS_MULTI &&
        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
    {
        queueMultiCommand(c); // 将事务中的命令都放入到队列中,然后返回"QUEUED"
        addReply(c,shared.queued);
    } else {
        if (server.vm_enabled && server.vm_max_threads > 0 &&
            blockClientOnSwappedKeys(c)) return REDIS_ERR;

        //调用该命令函数来处理命令
        call(c);
    }
    return REDIS_OK;
}

这里就涉及到客户端与服务器端的多次交互,明明是需要一次批量执行的n条命令,还需要通过多次网络交互,有些浪费;

更新操作中的查询实现

如果有这样的需求:在事务开始后,中间穿插有查询逻辑;
那么使用redis事务(单库),无法满足这个要求;

可能的解决方案:

  1. 可以考虑使用多个库,读写分离,查询库只用来查询,更新库用来开事务做写操作;
  2. 不再使用redis的事务指令,自己在客户端将待执行的命令批量打包,决定是否回滚还是全部执行;这样可以在更新的间隙执行查询逻辑;而不需要将查询逻辑提前到事务指令multi之前;
  3. 将查询业务逻辑提前;严格规范代码编写要求,所有的redis查询逻辑都放在事务之外:
     redis 127.0.0.1:7000> get b
     bbb
     业务逻辑...
     redis 127.0.0.1:7000> multi
     OK
     redis 127.0.0.1:7000> set a aaa
     QUEUED
     redis 127.0.0.1:7000> set c ccc
     QUEUED
     redis 127.0.0.1:7000> exec
     1) OK
     2) OK

优化网络特性

将多个命令打包批量发送到redis服务器执行,减少网络交互,优化性能,可能的解决方案:

  1. 对于所有的get/set操作,可使用现有的mget/mset指令;
  2. 对于各种不同类型的更新操作,可使用lua脚本将命令打包后,发送到服务器端一次执行;

参考

http://redisbook.readthedocs.org/en/latest/feature/transaction.html

Posted by: 大CC | 10MAR,2015
博客:blog.me115.com [订阅]

微博:新浪微博

时间: 2024-11-05 16:26:26

Redis事务的分析及改进的相关文章

redis源码分析之事务Transaction(下)

接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需要先对上面文章有所了解: redis源码分析之事务Transaction(上) 一.redis事务核心命令简介 redis事务操作核心命令: //用于开启事务 {"multi",multiCommand,1,"sF",0,NULL,0,0,0,0,0}, //用来执行事

redis源码分析之事务Transaction(上)

这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易阅读.因此把事务这个模块整理成上下两篇文章进行总结. 原文地址:http://www.jianshu.com/p/acb97d620ad7 这篇文章我们重点分析一下redis事务命令中的两个辅助命令:watch跟unwatch. 一.redis事务辅助命令简介 依然从server.c文件的命令表中找

Redis 源码学习之 Redis 事务Nosql

Redis事务提供了一种将多个命令请求打包,然后一次性.按照顺序地执行多个命令的机制,并且在事务执行的期间,服务器不会中断事务而去执行其他不在事务中的命令请求,它会把事务中所有的命令都执行完毕才会去执行其他的命令. How Redis中提供了multi.discard.exec.watch.unwatch这几个命令来实现事务的功能. Redis的事务始于multi命令,之后跟着要在事务中执行的命令,终于exec命令或者discard命令.加入事务中的所有命令会原子的执行,中间不会穿插执行其他没有

Redis系列六 Redis事务

Redis事务 1.介绍 在Redis事务中可以一次执行多个命令,本质是一组命令的集合.一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞. 2.事务的作用 一个队列中,一次性.顺序性.排他性的执行一系列命令. 3.事物执行五中情况 case1:正常执行 执行exec全部成功 Case2:放弃事务 执行Discard Case3:全体连坐 在向事物队列中添加命令的时候报错,然后执行Exec会全部失败. Case4:冤头债主 在向事物队列中添加命令的时候没有报错,但在

redis源码分析4---结构体---跳跃表

redis源码分析4---结构体---跳跃表 跳跃表是一种有序的数据结构,他通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的: 跳跃表支持平均O(logN),最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点.性能上和平衡树媲美,因为事先简单,常用来代替平衡树. 在redis中,只在两个地方使用了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构. 1 跳跃表节点 1.1 层 层的数量越多,访问其他节点的速度越快: 1.2 前进指针 遍历举例

redis源码分析3---结构体---字典

redis源码分析3---结构体---字典 字典,简单来说就是一种用于保存键值对的抽象数据结构: 注意,字典中每个键都是独一无二的:在redis中,内部的redis的数据库就是使用字典作为底层实现的: 1 字典的实现 在redis中,字典是使用哈希表作为底层实现的,一个hash表里面可以有多个hash表节点,而每个hash表节点就保存了字典中的一个键值对: hash表定义 table属性是一个数组,数组中的每个元素都是一个指向dictEntry结构的指针,每个dictEntry结构保存着一个键值

单线程你别阻塞,Redis时延问题分析及应对

单线程你别阻塞,Redis时延问题分析及应对 Redis的事件循环在一个线程中处理,作为一个单线程程序,重要的是要保证事件处理的时延短,这样,事件循环中的后续任务才不会阻塞: 当redis的数据量达到一定级别后(比如20G),阻塞操作对性能的影响尤为严重: 下面我们总结下在redis中有哪些耗时的场景及应对方法: 耗时长的命令造成阻塞 keys.sort等命令 keys命令用于查找所有符合给定模式 pattern 的 key,时间复杂度为O(N), N 为数据库中 key 的数量.当数据库中的个

redis事务(转载)

原文地址:http://blog.csdn.net/hechurui/article/details/49508749 Redis事务 首先,Redis本身是单线程的. redis中的事务(transaction)是一组命令的集合.事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行.Redis事务的实现需要用到 MULTI 和 EXEC 两个命令,事务开始的时候先向Redis服务器发送 MULTI 命令,然后依次发送需要在本次事务中处理的命令,最后再发送 EXE

Redis 事务

Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证: 事务是一个单独的隔离操作:事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中,不会被其他客户端发送来的命令请求所打断. 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行. 一个事务从开始到执行会经历以下三个阶段: 开始事务. 命令入队. 执行事务. 实例 以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令