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 [订阅]

微博:新浪微博

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

  1. Redis事务原理分析

    Redis事务原理分析 基本应用 在Redis的事务里面,采用的是乐观锁,主要是为了提高性能,减少客户端的等待.由几个命令构成:WATCH, UNWATCH, MULTI, EXEC, DISCARD ...

  2. 一、Redis事务原理分析

    一.Redis事务原理分析 在Redis的事务里面,采用的是乐观锁,主要是为了提高性能,减少客户端的等待.由几个命令构成:WATCH, UNWATCH, MULTI, EXEC, DISCARD.通过 ...

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

    这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...

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

    接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需 ...

  5. MongoDB、Hbase、Redis等NoSQL分析

    NoSQL的四大种类 NoSQL数据库在整个数据库领域的江湖地位已经不言而喻.在大数据时代,虽然RDBMS很优秀,但是面对快速增长的数据规模和日渐复杂的数据模型,RDBMS渐渐力不从心,无法应对很多数 ...

  6. redis 事务 事务机制详解 MULTI、EXEC、DISCARD、WATCH

    1. Redis服务端是个单线程的架构,不同的Client虽然看似可以同时保持连接,但发出去的命令是序列化执行的,这在通常的数据库理论下是最高级别的隔离2. 用MULTI/EXEC 来把多个命令组装成 ...

  7. 探索Redis设计与实现14:Redis事务浅析与ACID特性介绍

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  8. Redis 源码学习之 Redis 事务Nosql

    Redis事务提供了一种将多个命令请求打包,然后一次性.按照顺序地执行多个命令的机制,并且在事务执行的期间,服务器不会中断事务而去执行其他不在事务中的命令请求,它会把事务中所有的命令都执行完毕才会去执 ...

  9. Redis事务实现原理

    一:简介 Redis事务通常会使用MULTI,EXEC,WATCH等命令来完成,redis实现事务实现的机制与常见的关系型数据库有很大的却别,比如redis的事务不支持回滚,事务执行时会阻塞其它客户端 ...

随机推荐

  1. :parent 匹配含有子元素或者文本的元素

    描述: 查找所有含有子元素或者文本的 td 元素 HTML 代码: <table> <tr><td>Value 1</td><td>< ...

  2. 【secureCRT】会话保存的路径

  3. 【jQuery UI 1.8 The User Interface Library for jQuery】.学习笔记.6.Dialog控件

    习惯上,我们播放一条简短的信息,或向浏览者询问一个问题,都会用到dialog. 创建一个基本的dialog 使用dialog 选项 形式 启用内置动画 给dialog添加按钮 使用dialog回调函数 ...

  4. Can't create/write to file '/tmp/#sql_887d_0.MYD' (Errcode: 17)

    lsof |grep "#sql_887d_0.MYD" 如果没有被占用就可以删掉 . https://wordpress.org/support/topic/cant-creat ...

  5. Oracle 行迁移和行链接

    一.行迁移 1.1.行迁移概念 当一个行上的更新操作(原来的数据存在且没有减少)导致当前的数据不能在容纳在当前块,我们需要进行行迁移.一个行迁移意味着整行数据将会移动,仅仅保留的是一个转移地址.因此整 ...

  6. php如何支持实现多线程并发

    <?php if(function_exists('date_default_timezone_set')) { date_default_timezone_set('PRC'); } func ...

  7. PHP多表取数据的代码优化

    <?php header("Content-type: text/html; charset=utf-8"); //假设这里的$goods_arr  和 $shop_arr  ...

  8. VS 6.00 工程项目文件详解

    *.dsp(DeveloperStudio Project):是VC++的工程配置文件,比如说你的工程包含哪个文件,你的编译选项是什么等等,编译的时候是按照.dsp的配置来的.*.dsw(Develo ...

  9. Android Studio解决unspecified on project app resolves to an APK archive which is not supported

    出现该问题unspecified on project app resolves to an APK archive which is not supported as a compilation d ...

  10. HDU 自动刷题机 Auto AC (轻轻松松进入HDU首页)

    前言: 在写这篇文章之前,首先感谢给我思路以及帮助过我的学长们 以下4篇博客都是学长原创,其中有很多有用的,值得学习的东西,希望能够帮到大家! 1.手把手教你用C++ 写ACM自动刷题神器(冲入HDU ...