redis vs memcached。
redis与memcached对比,redis不仅适合做缓存,而且可以做存储,这就有点数据库的影子了。说到数据库,事务是一个很重要的一个方面。
数据库事务
(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:
1.为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
2.当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
事务约束
当事务被提交给了DBMS(数据库管理系统),则DBMS(数据库管理系统)需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。
通过事务的约束条件,可以总结出四个特点
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
有兴趣的更深了解事务相关,可以关注下事务传播属性,事务隔离级别。
本文主要演练下redis对事务的支持效果。
1 相关命令
命令 | 作用 | 可用版本 | 时间复杂度 | 返回值 |
WATCH key [key ...] | 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 | >= 2.2.0 | O(1) | 总是返回 |
MULTI | 标记一个事务块的开始 | >= 1.2.0 | O(1) | 总是返回 OK |
EXEC | 执行所有事务块内的命令。 假如某个(或某些) key 正处于 WATCH 命令的监视之下,且事务块中有和这个(或这些) key 相关的命令,那么 EXEC 命令只在这个(或这些) key 没有被其他命令所改动的情况下执行并生效,否则该事务被打断(abort)。 | >= 1.2.0 | 事务块内所有命令的时间复杂度的总和 | 事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。 |
DISCARD | 取消事务,放弃执行事务块内的所有命令。 如果正在使用 WATCH 命令监视某个(或某些) key,那么取消所有监视,等同于执行命令 UNWATCH 。 | >= 2.0.0 | O(1) | 总是返回 OK 。 |
UNWATCH | 取消 WATCH 命令对所有 key 的监视。 如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。 因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。 | >= 2.2.0 | O(1) | 总是 OK 。 |
上面,自己仅仅进行了整理,没什么好说的。重点是演练
2.命令演练
2.1MULTI ,起声明事务的意思。表示“下面的命令,打个包做个整体”但不绝对。
127.0.0.1:6379> incr bar
(integer) 1
127.0.0.1:6379> incr foo
(integer) 1
#事务开始
127.0.0.1:6379> multi
OK
#发出的命令,直接返回QUEUED,放入队列,不立即执行
127.0.0.1:6379> incr bar
QUEUED
127.0.0.1:6379> incr foo
QUEUED
#执行所有事务块内的命令
127.0.0.1:6379> exec
1) (integer) 2
2) (integer) 2
#演示语法出错情况,直接停止事务
127.0.0.1:6379> incr bar
QUEUED
127.0.0.1:6379> incrbar
(error) ERR unknown command 'incrbar'
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
#事务不允许连续声明
127.0.0.1:6379> multi
OK
127.0.0.1:6379> multi
(error) ERR MULTI calls can not be nested
#演示命令参数不匹配,部分成功
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr bar
QUEUED
127.0.0.1:6379> lpush bar "value2"
QUEUED
127.0.0.1:6379> exec
1) (integer) 2
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
结论
multi:不支持嵌套
redis对事务的支持,不严谨。不太符合ACID中的原子性。
Redis 在事务失败时不进行回滚,而是继续执行余下的命令”
如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。
以下是这种做法的优点:
Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
2.discard,"丢弃”意思。表示“上面的命令,统统都不要了”有点rollback的影子,但又不是。discard的意思,是把队列中的命令舍弃掉,这时候还没有发出命令。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> discard
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr bar
QUEUED
127.0.0.1:6379> incr foo
QUEUED
127.0.0.1:6379> discard
OK
2.1 WATCH :含义“监视”。表示“这是我的菜,其他人靠边站”。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
#watch命令不能在事务里出现,否则ERR
127.0.0.1:6379> watch bar
(error) ERR WATCH inside MULTI is not allowed
#监控bar,bar不再是随便的key了,不允许随便修改了。
127.0.0.1:6379> watch bar
OK
#事务之前,没有修改bar,事务成功
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr bar
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
#exec,将watch 打断,重新watch
127.0.0.1:6379> watch bar
OK
##事务之前,修改bar,事务失败
127.0.0.1:6379> incr bar
(integer) 2
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr bar
QUEUED
127.0.0.1:6379> exec
(nil)