Redis事务(乐观锁)

一、什么是事务
事务是指一系列操作步骤,这一系列的操作步骤,要么完全地执行,要么完全地不执行。

比如微博中:A用户关注了B用户,那么A的关注人列表里面就会有B用户,B的粉丝列表里面就会有A用户。
这个关注与被关注的过程是由一系列操作步骤构成:
(1)A用户添加到B的粉丝列表里面
(2)B用户添加到A的关注列表里面;
这两个步骤必须全部执行成功,整个逻辑才是正确的,否则就会产生数据的错误,比如A用户的关注列表有B用户,但B的粉丝列表里没有A用户;
要保证一系列的操作都完全成功,提出了事务控制的概念。

二、事务的ACID原则
当事务处理系统创建事务时,将确保事务有某些特性。组件的开发者们假设事务的特性应该是一些不需要他们亲自管理的特性。这些特性称为ACID特性。

ACID就是:原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily)。

  1. 原子性
    原子性属性用于标识事务是否完全地完成,一个事务的任何更新要在系统上完全完成,如果由于某种原因出错,事务不能完成它的全部任务,系统将返回到事务开始前的状态。

让我们看一下银行转帐的例子。如果在转帐的过程中出现错误,整个事务将会回滚。只有当事务中的所有部分都成功执行了,才将事务写入磁盘并使变化永久化。

为了提供回滚或者撤消未提交的变化的能力,许多数据源采用日志机制。例如,SQL Server使用一个预写事务日志,在将数据应用于(或提交到)实际数据页面前,先写在事务日志上。但是,其他一些数据源不是关系型数据库管理系统 (RDBMS),它们管理未提交事务的方式完全不同。只要事务回滚时,数据源可以撤消所有未提交的改变,那么这种技术应该可用于管理事务。

  1. 一致性
    事务在系统完整性中实施一致性,这通过保证系统的任何事务最后都处于有效状态来实现。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。因为事务开始时系统处于一致状态,所以现在系统仍然处于一致状态。

再让我们回头看一下银行转帐的例子,在帐户转换和资金转移前,帐户处于有效状态。如果事务成功地完成,并且提交事务,则帐户处于新的有效的状态。如果事务出错,终止后,帐户返回到原先的有效状态。

记住,事务不负责实施数据完整性,而仅仅负责在事务提交或终止以后确保数据返回到一致状态。理解数据完整性规则并写代码实现完整性的重任通常落在开发者肩上,他们根据业务要求进行设计。

当许多用户同时使用和修改同样的数据时,事务必须保持其数据的完整性和一致性。因此我们进一步研究A C I D特性中的下一个特性:隔离性。

  1. 隔离性
    在隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。

这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。

重要的是,在隔离状态执行事务,系统的状态有可能是不一致的,在结束事务前,应确保系统处于一致状态。但是在每个单独的事务中,系统的状态可能会发生变化。如果事务不是在隔离状态运行,它就可能从系统中访问数据,而系统可能处于不一致状态。通过提供事务隔离,可以阻止这类事件的发生。

在银行的示例中,这意味着在这个系统内,其他过程和事务在我们的事务完成前看不到我们的事务引起的任何变化,这对于终止的情况非常重要。如果有另一个过程根据帐户余额进行相应处理,而它在我们的事务完成前就能看到它造成的变化,那么这个过程的决策可能建立在错误的数据之上,因为我们的事务可能终止。这就是说明了为什么事务产生的变化,直到事务完成,才对系统的其他部分可见。

隔离性不仅仅保证多个事务不能同时修改相同数据,而且能够保证事务操作产生的变化直到变化被提交或终止时才能对另一个事务可见,并发的事务彼此之 间毫无影 响。这就意味着所有要求修改或读取的数据已经被锁定在事务中,直到事务完成才能释放。大多数数据库,例如SQL Server以及其他的RDBMS,通过使用锁定来实现隔离,事务中涉及的各个数据项或数据集使用锁定来防止并发访问。

  1. 持久性
    持久性意味着一旦事务执行成功,在系统中产生的所有变化将是永久的。应该存在一些检查点防止在系统失败时丢失信息。甚至硬件本身失败,系统的状态仍能通过在日志中记录事务完成的任务进行重建。持久性的概念允许开发者认为不管系统以后发生了什么变化,完成的事务是系统永久的部分。

在银行的例子中,资金的转移是永久的,一直保持在系统中。这听起来似乎简单,但这,依赖于将数据写入磁盘,特别需要指出的是,在事务完全完成并提交后才写入磁盘的。

所有这些事务特性,不管其内部如何关联,仅仅是保证从事务开始到事务完成,不管事务成功与否,都能正确地管理事务涉及的数据 当事务处理系统创建事务 时,将确保事务有某些特性。组件的开发者们假设事务的特性应该是一些不需要他们亲自管理的特性。这些特性称为ACID特性。

三、redis事务
Redis中的事务(transaction)是一组命令的集合,至少是两个或两个以上的命令,redis事务保证这些命令被执行时中间不会被任何其他操作打断。

redis对事务控制的实现

1、正常情况
一般为三步:

开启事务(MULTI)==> 写操作 ==> 执行事务(EXEC)

开启事务(MULTI):用MULTI命令告诉Redis,接下来要执行的命令你先不要执行,而是把它们暂时存起来 (开启事务)

写操作:N条命令进入等待队列(命令入队)

执行事务(EXEC):告知redis执行前面发送的两条命令(提交事务)

127.0.0.1:6379> MULTI  #开启事务
OK
127.0.0.1:6379> sadd user1 1   #写操作
QUEUED
127.0.0.1:6379> SADD user2 2   #写操作
QUEUED
127.0.0.1:6379> EXEC   #执行操作
1) (integer) 1
2) (integer) 1
 
127.0.0.1:6379> keys *  #检查是否操作执行
1) "user1"
2) "user2"

2、异常情况
如果操作语句错误


127.0.0.1:6379> MULTI  #开启事务
OK
 
127.0.0.1:6379> set key value #第一条操作
QUEUED
 
127.0.0.1:6379> set key  #错误操作
(error) ERR wrong number of arguments for 'set' command
 
127.0.0.1:6379> EXEC  #无法执行事务,那么第一条正确的命令也不会执行,所以key的值不会设置成功
(error) EXECABORT Transaction discarded because of previous errors.
 
127.0.0.1:6379> keys *  #查看key并没有生成第一条操作的key
1) "runtime" 
2) "caches"

3、例外情况

127.0.0.1:6379> MULTI #开启事务
OK
 
127.0.0.1:6379> set key v1  #操作语句
QUEUED
 
127.0.0.1:6379> INCR key  #此命令错误,字符串不能自增
QUEUED                    #但是入队成功
 
127.0.0.1:6379> EXEC   #事务依然提交了,key的值被设置为v1,自增操作执行失败,但整个事务没有回滚
1) OK  #语句一成功
2) (error) ERR value is not an integer or out of range  #语句二报错

4、放弃情况
当我们事务写到一半,出现了错误,或者不想执行此事务,可以使用discard放弃事务

127.0.0.1:6379> MULTI   #开启事务
OK
 
127.0.0.1:6379> set age 25  #命令入队
QUEUED
 
127.0.0.1:6379> set age 30  #命令入队
QUEUED
 
127.0.0.1:6379> DISCARD  #放弃事务,则命令队列不会被执行
OK
 
127.0.0.1:6379> keys *  #查看已有key
1) "runtime"
2) "key"
3) "caches"

5、复杂情况
5.1 悲观锁
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改该数据,所以每次在拿数据的时候都会先上锁,这样别人想拿这个数据就会block阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,让别人无法操作该数据。

5.2 乐观锁(防止并发操作)
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改该数据,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这条数据,一般使用版本号机制进行判断。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

乐观锁大多数情况是基于数据版本号(version)的机制实现的。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个“version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据,不予更新。

乐观锁实现举例:

5.3 Redis的watch机制实现乐观锁
监视一个(或多个) key ,如果在事务exec执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断

下面这个例子:我们在观察的时候k1值为1,但是事务执行的时候k1值为2,k1被修改了所以不能执行事务。

127.0.0.1:6379> set k1 1 #设置k1值为1
OK
127.0.0.1:6379> WATCH k1 #监视k1 (当已经开始监控k1,则其他客户端不能修改k1的值)
OK
127.0.0.1:6379> set k1 2 #设置k1值为2,此操作可能由其他客户端执行
OK
127.0.0.1:6379> MULTI #开始事务
OK
127.0.0.1:6379> set k1 3 #修改k值为3
QUEUED 
127.0.0.1:6379> EXEC #提交事务,但k1值不会被修改为3,k1的值仍然是2,因为在事务开启之前k1的值被修改了
(nil)
127.0.0.1:6379> get k1
"2"

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354