`
1028826685
  • 浏览: 938646 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类

Redis事务介绍

 
阅读更多

概述

相信学过MySQL等其他数据库的同学对事务这个词都不陌生,事务表示的是一组动作,这组动作要么全部执行,要么全部不执行。为什么会有这样的需求呢?看看下面的场景:

  • 微博是一个弱关系型社交网络,用户之间有关注和被关注两种关系,比如两个用户A和B,如果A关注B,则B的粉丝中就应该有A。关注这个动作需要两个步骤完成:在A的关注者中添加B;在B的粉丝中添加A。 这两个动作要么都执行成功,要么都不执行。否则就可能会出现A关注了B,但是B的粉丝中没有A的不可容忍的情况。
  • 转账汇款,假设现在有两个账户A和B,现在需要将A中的一万块大洋转到B的账户中,这个动作也需要两个步骤完成:从A的账户中划走一万块;在B的账户中增加一万块。这两个动作要么全部执行成功,要么全部不执行,否则自会有人问候你的!!!

Redis作为一种高效的分布式数据库,同样支持事务。

Redis事务

Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行。Redis事务的实现需要用到 MULTI  EXEC 两个命令,事务开始的时候先向Redis服务器发送 MULTI 命令,然后依次发送需要在本次事务中处理的命令,最后再发送 EXEC 命令表示事务命令结束。

举个例子,使用redis-cli连接redis,然后在命令行工具中输入如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set url http://qifuguang.me
QUEUED
127.0.0.1:6379> set title winwill2012
QUEUED
127.0.0.1:6379> set desc java
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
127.0.0.1:6379>
127.0.0.1:6379> get url
"http://qifuguang.me"
127.0.0.1:6379> get title
"winwill2012"
127.0.0.1:6379> get desc
"java"
127.0.0.1:6379>

从输出中可以看到,当输入MULTI命令后,服务器返回OK表示事务开始成功,然后依次输入需要在本次事务中执行的所有命令,每次输入一个命令服务器并不会马上执行,而是返回”QUEUED”,这表示命令已经被服务器接受并且暂时保存起来,最后输入EXEC命令后,本次事务中的所有命令才会被依次执行,可以看到最后服务器一次性返回了三个OK,这里返回的结果与发送的命令是按顺序一一对应的,这说明这次事务中的命令全都执行成功了。

再举个例子,在命令行工具中输入如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a a
QUEUED
127.0.0.1:6379> sett b b
(error) ERR unknown command 'sett'
127.0.0.1:6379> set c c
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get a
(nil)
127.0.0.1:6379> get b
(nil)
127.0.0.1:6379> get c
(nil)
127.0.0.1:6379>

和前面的例子一样,先输入MULTI最后输入EXEC表示中间的命令属于一个事务,不同的是中间输入的命令有一个错误(set写成了sett),这样因为有一个错误的命令导致事务中的其他命令都不执行了(通过后续的get命令可以验证),可见事务中的所有命令式同呼吸共命运的。

如果客户端在发送EXEC命令之前断线了,则服务器会清空事务队列,事务中的所有命令都不会被执行。而一旦客户端发送了EXEC命令之后,事务中的所有命令都会被执行,即使此后客户端断线也没关系,因为服务器已经保存了事务中的所有命令。

除了保证事务中的所有命令要么全执行要么全不执行外,Redis的事务还能保证一个事务中的命令依次执行而不会被其他命令插入。试想一个客户端A需要执行几条命令,同时客户端B发送了几条命令,如果不使用事务,则客户端B的命令有可能会插入到客户端A的几条命令中,如果想避免这种情况发生,也可以使用事务。

Redis事务错误处理

如果一个事务中的某个命令执行出错,Redis会怎样处理呢?要回答这个问题,首先要搞清楚是什么原因导致命令执行出错:

  1. 语法错误 就像上面的例子一样,语法错误表示命令不存在或者参数错误
    这种情况需要区分Redis的版本,Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行,就比如上面的例子(使用的Redis版本是2.8的)

  2. 运行错误 运行错误表示命令在执行过程中出现错误,比如用GET命令获取一个散列表类型的键值。
    这种错误在命令执行之前Redis是无法发现的,所以在事务里这样的命令会被Redis接受并执行。如果食物里有一条命令执行错误,其他命令依旧会执行(包括出错之后的命令)。比如下例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set key 1
    QUEUED
    127.0.0.1:6379> SADD key 2
    QUEUED
    127.0.0.1:6379> set key 3
    QUEUED
    127.0.0.1:6379> EXEC
    1) OK
    2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    3) OK
    127.0.0.1:6379> get key
    "3"
    

    Redis中的事务并没有关系型数据库中的事务回滚(rollback)功能,因此使用者必须自己收拾剩下的烂摊子。不过由于Redis不支持事务回滚功能,这也使得Redis的事务简洁快速。

回顾上面两种类型的错误,语法错误完全可以在开发的时候发现并作出处理,另外如果能很好地规划Redis数据的键的使用,也是不会出现命令和键不匹配的问题的。

WATCH命令

从上面的例子我们可以看到,事务中的命令要全部执行完之后才能获取每个命令的结果,但是如果一个事务中的命令B依赖于他上一个命令A的结果的话该怎么办呢?就比如说实现类似Java中的i++的功能,先要获取当前值,才能在当前值的基础上做加一操作。这种场合仅仅使用上面介绍的MULTI和EXEC是不能实现的,因为MULTI和EXEC中的命令是一起执行的,并不能将其中一条命令的执行结果作为另一条命令的执行参数,所以这个时候就需要引进Redis事务家族中的另一成员:WATCH命令

换个角度思考上面说到的实现i++的方法,可以这样实现:

  1. 监控i的值,保证i的值不被修改
  2. 获取i的原值
  3. 如果过程中i的值没有被修改,则将当前的i值+1,否则不执行

这样就能够避免竞态条件,保证i++能够正确执行。

WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,EXEC命令执行完之后被监控的键会自动被UNWATCH)

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> set mykey 1
OK
127.0.0.1:6379> WATCH mykey
OK
127.0.0.1:6379> set mykey 2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set mykey 3
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> get mykey
"2"
127.0.0.1:6379>

上面的例子中,首先设置mykey的键值为1,然后使用WATCH命令监控mykey,随后更改mykey的值为2,然后进入事务,事务中设置mykey的值为3,然后执行EXEC运行事务中的命令,最后使用get命令查看mykey的值,发现mykey的值还是2,也就是说事务中的命令根本没有执行(因为WATCH监控mykey的过程中,mykey被修改了,所以随后的事务便会被取消)。

有了WATCH命令,我们就可以自己实现i++功能了,伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
def incr($key):
    WATCH $key
    $value = GET $key
    if not $value
        $value = 0
    $value = $value + 1

    MULTI
    SET $key $value
        result = EXEC
    return result[0]

因为EXEC返回的是多行字符串,使用result[0]表示返回值的第一个字符串。

注意:由于WATCH命令的作用只是当被监控的键被修改后取消之后的事务,并不能保证其他客户端不修改监控的值,所以当EXEC命令执行失败之后需要手动重新执行整个事务。

 

执行EXEC命令之后会取消监控使用WATCH命令监控的键,如果不想执行事务中的命令,也可以使用UNWATCH命令来取消监控。

分享到:
评论

相关推荐

    springboot-redis事务

    本文将深入探讨“SpringBoot-Redis事务”这一主题,旨在帮助开发者理解如何在SpringBoot项目中利用Redis进行事务操作。 首先,SpringBoot是Spring框架的一种快速启动方式,它简化了配置,提供了自动配置的功能,...

    redis事务与关系型数据库事务比较

    【Redis 事务与关系型数据库事务的比较】 Redis 和关系型数据库(如 MySQL)在事务处理上有显著的差异。在Redis中,事务提供了一种批量执行命令的方式,以确保原子性,但其机制与传统的ACID(原子性、一致性、隔离...

    redis事务回滚工具

    基于SpringBoot的轻量级redis事务回滚机制,使用栈和ThreadLocal记录业务链的redis操作,发生异常进行回滚,参考了阿里巴巴Seata AT模式的db回滚策略:补偿回滚,记录前镜像与当前操作语句,反向解析生成补偿动作。...

    redis事务常用操作详解

    Redis 事务是数据库系统中的一种特性,允许用户一次性提交...例如,Redis事务不支持回滚操作,如果事务中的某条命令失败,后续命令仍会执行。因此,在设计使用Redis的事务时,需要考虑到这些限制并进行适当的错误处理。

    redis事务_动力节点Java学院整理

    Redis 事务(Transactions)是 Redis 数据库提供的一种批量执行命令的功能,尽管它的事务机制相比于传统的关系型数据库事务较为简化,但仍能满足一些基本的事务需求。Redis 事务的主要特点在于其简单、快速和高效,...

    cpp-Redis分布式事务

    标题中的“cpp-Redis分布式事务”表明我们讨论的是如何在C++编程环境下利用Redis来实现分布式事务处理。Redis是一款开源的、基于键值对的数据存储系统,常用于缓存、消息队列以及分布式场景中。在大型游戏中,由于...

    记录redisson实现redis分布式事务锁

    本篇文章将详细探讨如何使用Redisson实现Redis分布式事务锁,以及在Spring Boot环境中如何进行集成。 首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多...

    redis常用命令,redis配置文件,redis持久化,redis事务,redis主从复制,jedis的使用

    下面是对 Redis 的常用命令、配置文件、持久化、事务、主从复制、Jedis 使用的详细讲解。 Redis 常用命令 Redis 提供了很多有用的命令来管理和操作数据。下面是一些常用的 Redis 命令: * SET key value:设置...

    redis事务1

    Redis 事务是 Redis 数据库提供的一种批量执行命令的机制,旨在提高数据操作的效率和原子性。虽然在传统的数据库系统中,事务具有ACID(原子性、一致性、隔离性和持久性)特性,但 Redis 事务并不完全具备这些特性。...

    Redis事务的使用

    一、Redis事务简介 Redis事务可以一次执行多个命令,一个事务的所有命令都会序列化并按顺序地串行化执行,而不会被其他客户端提交的命令请求插入到事务执行命令序列中。 二、Redis事务命令 下面的表格节选自:...

    第7单元 Redis事务与持久化1

    【Redis事务】 Redis事务是Redis数据库中提供的一种确保数据操作原子性的机制,它允许用户在一个操作序列中执行多条命令,这些命令会被串行化并按顺序执行,以保证在并发环境下的数据一致性。事务的开启是通过`MULTI...

    redis事务处理:(error) EXECABORT

    然而,事务处理并非总是顺利进行,"redis事务处理:(error) EXECABORT" 是一个常见的错误提示,意味着在执行事务时遇到了问题。本文将深入探讨这个错误的原因以及如何解决。 一、Redis 事务基础 Redis 的事务机制...

    Redis之Redis事务

    本文主要介绍了Redis事务的概念、Redis不保证原子性、Redis事务的三个阶段、Redis事务相关命令以及Redis事务使用案例。 本文来自于博客园,由火龙果软件Anna编辑、推荐。Redis事务的本质是一组命令的集合。事务支持...

    08-Redis HyperLogLog与事务和Redis 7.0那点事-ev.rar

    08-Redis HyperLogLog与事务和Redis 7.0那点事_ev.rar08-Redis HyperLogLog与事务和Redis 7.0那点事_ev.rar08-Redis HyperLogLog与事务和Redis 7.0那点事_ev.rar08-Redis HyperLogLog与事务和Redis 7.0那点事_ev.rar...

    Redis 事务(上).flv

    Redis 事务(上).flv

    Redis学习笔记—Redis事务

    Redis事务可以一次执行多个命令(按顺序地串行执行,执行中不会被其他命令插入,不许加塞) 1.简介 Redis事务可以一次执行多个命令(允许在一次单独的步骤中执行一组命令)。 特征: [1]批量操作在发送EXEC命令前被放入...

    剖析!Redis事务实现原理

    在Redis中实现事务主要依靠以下几个命令来实现:Redis事务从开始到结束通常会通过三个阶段:1.事务开始2.命令入队3.事务执行以下是一个最简单的Redis事务流程:第一步跟其他的关系型数据库类似,也是需要开启一个事务...

Global site tag (gtag.js) - Google Analytics