查看“Redis:事务(transaction)”的源代码
←
Redis:事务(transaction)
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Redis]] == 什么是事务? == 事务:是指一系列操作步骤,要么完全地执行,要么完全地不执行。 <br/> Redis 中的事务(transaction)是一组命令的集合,至少是两个或两个以上的命令,redis 事务保证这些命令被执行时中间不会被任何其他操作打断。<br/> 示例:微博中,A 用户关注了 B 用户,那么 A 的关注人列表里面就会有 B 用户,B 的粉丝列表里面就会有 A 用户。 : 这个关注与被关注的过程是由一系列操作步骤构成: # A 用户添加到 B 的粉丝列表里面; # B 用户添加到 A 的关注列表里面; 这两个步骤必须全部执行成功,整个逻辑才是正确的,否则就会产生数据的错误,比如A用户的关注列表有B用户,但B的粉丝列表里没有A用户; == Redis事务控制 == Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证: # 批量操作在发送 EXEC 命令前被放入队列缓存。 # 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。 # 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。 一个事务从开始到执行会经历以下三个阶段: # 开始事务。 # 命令入队。 # 执行事务。 === 事务命令 === {| class="wikitable" ! 命令 !! 描述 |- | MULTI || 标记一个事务块的开始。 |- | EXEC || 执行所有事务块内的命令。 |- | DISCARD || 取消事务,放弃执行事务块内的所有命令。 |- | WATCH key [key ...] || 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |- | UNWATCH || 取消 WATCH 命令对所有 key 的监视。 |} === 事务示例 === 以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令: <syntaxhighlight lang="java" highlight=""> redis 127.0.0.1:6379> MULTI OK redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days" QUEUED redis 127.0.0.1:6379> GET book-name QUEUED redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series" QUEUED redis 127.0.0.1:6379> SMEMBERS tag QUEUED redis 127.0.0.1:6379> EXEC 1) OK 2) "Mastering C++ in 21 days" 3) (integer) 3 4) 1) "Mastering Series" 2) "C++" 3) "Programming" </syntaxhighlight> 单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 '''Redis 事务的执行并不是原子性的'''。 '''事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做'''。 如下: <syntaxhighlight lang="java" highlight=""> 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 </syntaxhighlight> 如果在 set b bbb 处失败,set a 已成功不会回滚,set c 还会继续执行。 === 事务情况【???】 === Redis 事务不支持回滚,'''如果遇到问题,会继续执行余下的命令'''。 * '''只有语法错误,Redis 才会执行失败'''。 正常情况: # MULTI:用MULTI命令告诉Redis,接下来要执行的命令你先不要执行,而是把它们暂时存起来 【开启事务】 # SADD "user1" 2:第一条命令进入等待队列【命令入队】 # SADD "user2" 1:第二条命令进入等待队列【命令入队】 # EXEC:告知redis执行前面发送的两条命令【提交事务】 异常情况:【???】 # MULTI:正常命令 # SET key value:正常命令 # SET key:命令语法错误 # EXEC:无法执行事务,那么第一条正确的命令也不会执行,所以key的值不会设置成功 例外情况: # MULTI:正常命令 # SET key v1:正常命令 # INCR key:此命令错误,字符串不能自增 # EXEC: ## 事务依然提交了,key的值被设置为v1,自增操作执行失败,但整个事务没有回滚 ## 可以放弃事务:discard 放弃情况: # MULTI:开启事务 # SET age 25:命令入队 # SET age 30:命令入队 # DISCARD:放弃事务,则命令队列不会被执行 == Redis事务复杂情况实现 == === 悲观锁 === 悲观锁(Pessimistic Lock):每次去拿数据的时候都认为别人会修改该数据,所以每次在拿数据的时候都会先上锁,这样别人想拿这个数据就会 block 阻塞直到它拿到锁。 * 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,让别人无法操作该数据。 === 乐观锁 === 乐观锁(Optimistic Lock):每次去取数据的时候都认为别人不会修改该数据,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这条数据,一般使用'''版本号机制'''(类比于MySQL的“MVCC”)进行判断。 * 乐观锁适用于多读的应用类型,这样可以提高吞吐量。 * 乐观锁大多数情况是基于数据版本号(version)的机制实现的:为数据增加一个版本标识。 *: 在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个“version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加 1。此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据,不予更新。 乐观锁实现举例: : [[File:乐观锁实现举例.png|600px]] === '''watch 机制'''实现乐观锁 === 监视一个(或多个)key ,'''如果在事务exec执行之前这个(或这些)key 被其他命令所改动,那么事务将被打断'''。 示例: # set k1 1:设置 k1 值为 1; # watch k1:监视 k1;【开始监视 k1,则其他客户端或事务不能修改 k1 的值】 # set k1 2:设置 k1 值为 2; # multi:开始事务; # set k1 3:修改 k1 值为 3; # exec:提交事务; 如上,k1 的值仍然是 2,而不会被修改为 3,因为在事务开启之前 k1 的值被修改了;
返回至“
Redis:事务(transaction)
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息