资讯专栏INFORMATION COLUMN

Redis事务的分析及改进

tracymac7 / 3079人阅读

摘要:在模式下,服务器可能在事务执行之后文件更新之前的这段时间失败,所以模式下的事务也是不持久的。在的总是模式下,事务的每条命令在执行成功之后,都会立即调用或将事务数据写入到文件。

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事务(单库),无法满足这个要求;

可能的解决方案:

可以考虑使用多个库,读写分离,查询库只用来查询,更新库用来开事务做写操作;

不再使用redis的事务指令,自己在客户端将待执行的命令批量打包,决定是否回滚还是全部执行;这样可以在更新的间隙执行查询逻辑;而不需要将查询逻辑提前到事务指令multi之前;

将查询业务逻辑提前;严格规范代码编写要求,所有的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服务器执行,减少网络交互,优化性能,可能的解决方案:

对于所有的get/set操作,可使用现有的mget/mset指令;

对于各种不同类型的更新操作,可使用lua脚本将命令打包后,发送到服务器端一次执行;

参考

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/36084.html

相关文章

  • 数据库收集 - 收藏集 - 掘金

    摘要:前言在使用加载数据数据库常见的优化操作后端掘金一索引将放第一位,不用说,这种优化方式我们一直都在悄悄使用,那便是主键索引。 Redis 内存压缩实战 - 后端 - 掘金在讨论Redis内存压缩的时候,我们需要了解一下几个Redis的相关知识。 压缩列表 ziplist Redis的ziplist是用一段连续的内存来存储列表数据的一个数据结构,它的结构示例如下图 zlbytes: 记录整...

    muddyway 评论0 收藏0
  • 数据库收集 - 收藏集 - 掘金

    摘要:前言在使用加载数据数据库常见的优化操作后端掘金一索引将放第一位,不用说,这种优化方式我们一直都在悄悄使用,那便是主键索引。 Redis 内存压缩实战 - 后端 - 掘金在讨论Redis内存压缩的时候,我们需要了解一下几个Redis的相关知识。 压缩列表 ziplist Redis的ziplist是用一段连续的内存来存储列表数据的一个数据结构,它的结构示例如下图 zlbytes: 记录整...

    Little_XM 评论0 收藏0
  • 数据库收集 - 收藏集 - 掘金

    摘要:前言在使用加载数据数据库常见的优化操作后端掘金一索引将放第一位,不用说,这种优化方式我们一直都在悄悄使用,那便是主键索引。 Redis 内存压缩实战 - 后端 - 掘金在讨论Redis内存压缩的时候,我们需要了解一下几个Redis的相关知识。 压缩列表 ziplist Redis的ziplist是用一段连续的内存来存储列表数据的一个数据结构,它的结构示例如下图 zlbytes: 记录整...

    XUI 评论0 收藏0
  • Java面试通关要点汇总集

    摘要:本文会以引出问题为主,后面有时间的话,笔者陆续会抽些重要的知识点进行详细的剖析与解答。敬请关注服务端思维微信公众号,获取最新文章。 原文地址:梁桂钊的博客博客地址:http://blog.720ui.com 这里,笔者结合自己过往的面试经验,整理了一些核心的知识清单,帮助读者更好地回顾与复习 Java 服务端核心技术。本文会以引出问题为主,后面有时间的话,笔者陆续会抽些重要的知识点进...

    gougoujiang 评论0 收藏0
  • PHP面试总结

    摘要:而在面试过程中,也是经常会遇到的,所以,无论是面试准备还是日常开发,我们都应该关注这方面的东西。二分法的基本做法是确定要查找的区间。区间内选取二分点。根据二分点的值,综合左右区间情况以及求解的目的,舍去一半无用的区间。 showImg(https://images.pexels.com/photos/935977/pexels-photo-935977.jpeg); 前言 面试是你进入...

    alin 评论0 收藏0

发表评论

0条评论

tracymac7

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<