下面小编给大家带来Redis研究(十)―Redis事务和生存时间(共含7篇),希望能帮助到大家!同时,但愿您也能像本文投稿人“Rukawa”一样,积极向本站投稿分享好文章。
一、事务概述
Redis事务是一组命令的集合,事务同命令一样是Redis的最小执行单位,要么执行,要么不执行。
事务的原理是先将属于一个事务的命令发送给Redis,然后在让Redis依次执行这些命令。
Redis在事务中没有立即执行sadd操作,而是返回QUEUED表示这两条命令已经进入等待执行的事务队列中了。vcD4KPHA+yrnTw0VYRUPD/MHuuObL31JlZGlzvau1yLT91rTQ0LXEysLO8bbTwdDW0LXEy/nT0MP8we6wtNXVt6LLzcuz0PLSwLTO1rTQ0KGjPC9wPgo8cD5FWEVDt7W72CYjMjA1NDA7vs3Kx9Xi0KnD/MHutcS3tbvYJiMyMDU0MDvX6bPJtcTB0LHto6y3tbvYJiMyMDU0MDvLs9Dyus3D/MHutcTLs9Dyz+DNrKGjPC9wPgoKPHA+UmVkaXOxo9ak0ru49srCzvHW0LXEy/nT0MP8we7SqsO0trzWtNDQo6zSqsO0tryyu9a00NCho8jnufvU2reiy81FWEVDw/zB7sewv827p7bLts/P38HLo6zU8lJlZGlzu+HH5b/VysLO8bbTwdCjrMrCzvHW0LXEy/nT0MP8we62vLK7u+HWtNDQoaO2+NK7tam/zbuntsu3osvNwctFWEVDw/zB7qOsy/nT0LXEw/zB7r7Ntry74da00NCjrLy0yrm0y7rzv827p7bLts/P39Kyw7vT0LnYz7WjrNLyzqpSZWRpc9bQ0tG+rbzHwrzBy8v509DSqta00NC1xMP8we6hozwvcD4KCjxwPlJlZGlztcTKws7xu7nE3LGj1qTSu7j2ysLO8cTatcTD/MHu0sC0zta00NC2+LK7sbvG5Mv7w/zB7rLlyOuhozwvcD4KPHA+PHN0cm9uZz48YnI+Cjwvc3Ryb25nPjwvcD4KPHA+PHN0cm9uZz62/qGitO3O87SmwO08L3N0cm9uZz48L3A+CjxwPsrCzvHW0LXExLO49sP8we7WtNDQs/a07aOs1PXDtLDso788L3A+CjxwPqOoMaOp0++3qLTtzvOjrNa4w/zB7rK7tObU2rvy1d/D/MHuss7K/bXEuPbK/bK7ttShozwvcD4KPHA+PGltZyBzcmM9”www.2cto.com/uploadfile/Collfiles/0119/2015011909522880.png“ alt=”“>
3个命令,一个正确,两个错误,而只要有一个命令有语法错误,执行EXEC命令后Redis就会直接返回错误,连语法正确的命令也不会执行。
(2)运行错误,指命令执行时出现的错误,比如使用散列类型的命令操作集合类型的键。在执行以前Redis无法发现,所以在事务中这样的命令会被接受并执行,
一条命令出现了运行错误,事务里其他的命令依然会继续执行。
虽然sadd key 2出现了错误,但是set key 3依然执行了,
Redis事务没有回滚功能。
Redis不支持回滚功能,也使得Redis在事务上可以保持简洁和快速。两种错误,语法错误和运行错误完全可以在开发时找出并解决。
三、watch命令
事务中每个命令的执行结果都是最后一期返回的,无法将前一条命令的结果作为下一条命令的参数。
比如说get值,+1,再set
为了解决这个问题,在get获得键值后保证该键值不被其他客户端修改,直到函数执行完成后才允许其他客户端修改该键值。可以防止竞态条件。
执行watch命令后,事务执行前修改了key的值,set key 2,所以最后事务中的命令set key 3没有执行,EXEC命令返回空结果。
由于watch命令的作用只是当被监控的键值被修改后阻止之后一个事务的执行,而不能保证其他客户端不修改这一键值,所以我们需要在EXEC执行失败后重新执行整个函数。
执行EXEC后会取消所有键的监控,也可以用unwatch命令来取消监控。
四、生存时间
使用expire命令设置一个键的生存时间,到时候后redis会自动删除它。
如果想让session:29e3d键在15分钟后被删除
返回1表示成功,0表示键不存在或者设置失败。
一个键还有多久时间被删除,可以用ttl命令,返回值是键的剩余时间(单位s)
当键不存在时ttl返回-1.同样会返回-1是没有为键设置生存时间(永久存在)。
想取消生存时间设置,即恢复永久,用persist命令。成功1,失败0
除次之外,还可以set或getset命令为键赋值也会同时清除键的生存时间。
使用expire会重新设置键的生存时间。
其他只对键值进行操作的命令(incr、lpush、hset、zrem)都不会影响键的生存时间。
expire命令时间参数必须是整数,s。更精确,使用pexpire,单位是毫秒。对应可以用pttl返回剩余时间。
如果watch命令监测了一个拥有生存时间的键,该键时间到期后自动删除并不会被watch命令认为该键被改变。
另外两个命令
expireat和pexpireat,与前面差别使用unix时间作为时间参数。expireat单位是秒,pexpireat单位是毫秒。
#在Shell命令行下执行Redis的客户端工具。 /> redis-cli #在当前连接上启动一个新的事务。 redis 127.0.0.1:6379> multi OK #执行事务中的第一条命令,从该命令的返回结果可以看出,该命令并没有立即执行,而是存于事务的命令队列。 redis 127.0.0.1:6379> incr t1 QUEUED #又执行一个新的命令,从结果可以看出,该命令也被存于事务的命令队列。 redis 127.0.0.1:6379> incr t2 QUEUED #执行事务命令队列中的所有命令,从结果可以看出,队列中命令的结果得到返回。 redis 127.0.0.1:6379> exec 1) (integer) 1 2) (integer) 1
#为键t2设置一个事务执行前的值。 redis 127.0.0.1:6379> set t2 tt OK #开启一个事务。 redis 127.0.0.1:6379> multi OK #在事务内为该键设置一个新值。 redis 127.0.0.1:6379> set t2 ttnew QUEUED #放弃事务。 redis 127.0.0.1:6379> discard OK #查看键t2的值,从结果中可以看出该键的值仍为事务开始之前的值。 redis 127.0.0.1:6379> get t2 ”tt“
#开启一个新的事务。 redis 127.0.0.1:6379> multi OK #设置键a的值为string类型的3。 redis 127.0.0.1:6379> set a 3 QUEUED #从键a所关联的值的头部弹出元素,由于该值是字符串类型,而lpop命令仅能用于List类型,因此在执行exec命令时,该命令将会失败。 redis 127.0.0.1:6379> lpop a QUEUED #再次设置键a的值为字符串4。 redis 127.0.0.1:6379> set a 4 QUEUED #获取键a的值,以便确认该值是否被事务中的第二个set命令设置成功。 redis 127.0.0.1:6379> get a QUEUED #从结果中可以看出,事务中的第二条命令lpop执行失败,而其后的set和get命令均执行成功,这一点是Redis的事务与关系型数据库中的事务之间最为重要的差别。 redis 127.0.0.1:6379> exec 1) OK 2) (error) ERR Operation against a key holding the wrong kind of value 3) OK 4) ”4“
这篇文章主要介绍了Redis教程(八):事务详解,本文讲解了,本文讲解了事务概述、相关命令列表、命令使用示例、WATCH命令和基于CAS的乐观锁等内容,需要的朋友可以参考下
一、概述:
和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制,在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出Redis中事务的实现特征:
1). 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
2). 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
3). 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为”BEGIN TRANSACTION“语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
4). 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。
5). 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。
二、相关命令列表:
命令原型时间复杂度命令描述返回值MULTI用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行。始终返回OKEXEC执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了WATCH命令,那么只有当WATCH所监控的Keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。原子性的返回事务中各条命令的返回结果。如果在事务中使用了WATCH,一旦事务被放弃,EXEC将返回NULL-multi-bulk回复。DISCARD回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将UNWATCH所有的Keys。始终返回OK。WATCHkey [key ...]O(1)在MULTI命令执行之前,可以指定待监控的Keys,然而在执行EXEC之前,如果被监控的Keys发生修改,EXEC将放弃执行该事务队列中的所有命令。始终返回OK。UNWATCHO(1)取消当前事务中指定监控的Keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有被监控的Keys都将自动取消。始终返回OK。
三、命令示例:
1. 事务被正常执行:
代码如下:
#在Shell命令行下执行Redis的客户端工具。
/> redis-cli
#在当前连接上启动一个新的事务。
redis 127.0.0.1:6379> multi
OK
#执行事务中的第一条命令,从该命令的返回结果可以看出,该命令并没有立即执行,而是存于事务的命令队列。
redis 127.0.0.1:6379> incr t1
QUEUED
#又执行一个新的命令,从结果可以看出,该命令也被存于事务的命令队列。
redis 127.0.0.1:6379> incr t2
QUEUED
#执行事务命令队列中的所有命令,从结果可以看出,队列中命令的结果得到返回。
redis 127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
2. 事务中存在失败的命令:
代码如下:
#开启一个新的事务,
redis 127.0.0.1:6379> multi
OK
#设置键a的值为string类型的3。
redis 127.0.0.1:6379> set a 3
QUEUED
#从键a所关联的值的头部弹出元素,由于该值是字符串类型,而lpop命令仅能用于List类型,因此在执行exec命令时,该命令将会失败。
redis 127.0.0.1:6379> lpop a
QUEUED
#再次设置键a的值为字符串4。
redis 127.0.0.1:6379> set a 4
QUEUED
#获取键a的值,以便确认该值是否被事务中的第二个set命令设置成功。
redis 127.0.0.1:6379> get a
QUEUED
#从结果中可以看出,事务中的第二条命令lpop执行失败,而其后的set和get命令均执行成功,这一点是Redis的事务与关系型数据库中的事务之间最为重要的差别。
redis 127.0.0.1:6379> exec
1) OK
2) (error) ERR Operation against a key holding the wrong kind of value
3) OK
4) ”4“
3. 回滚事务:
代码如下:
#为键t2设置一个事务执行前的值。
redis 127.0.0.1:6379> set t2 tt
OK
#开启一个事务。
redis 127.0.0.1:6379> multi
OK
#在事务内为该键设置一个新值。
redis 127.0.0.1:6379> set t2 ttnew
QUEUED
#放弃事务。
redis 127.0.0.1:6379> discard
OK
#查看键t2的值,从结果中可以看出该键的值仍为事务开始之前的值。
redis 127.0.0.1:6379> get t2
”tt“
四、WATCH命令和基于CAS的乐观锁:
在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:
代码如下:
val = GET mykey
val = val + 1
SET mykey $val
以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景--竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:
代码如下:
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。
一、介绍
一个集合类型(set)键可以存储至多2^32-1个字符串,
集合类型在Redis内部是使用值为空的散列表(hash table)实现的,所以操作的复杂度为O(1)。多个集合类型键之间还可以进行并集、交集和差集运算。
二、命令
1.增加/删除元素vcD4KPHA+PHByZSBjbGFzcz0=“brush:sql;”>sadd key membersrem key member
sadd用来向集合中增加一个或者多个元素,如果键不存在则会自动创建。一个集合中不能有相同的元素,所以如果要加入的元素已经存在于集合中就会忽略这个元素。本命令的返回值是成功加入的元素数量。
第二条sadd命令返回2,因为a已经存在,实际上只加入两个元素。
srem用来从集合中删除一个或者多个元素,并返回删除成功的个数,
由于d在集合中不存在,所以只删除了一个元素,返回值为1.
2.获得集合中的所有元素
smembers key
返回集合所有元素
3.判断元素是否在集合中
sismember key member
时间复杂度O(1),无论有多少个元素。
4.集合之间运算
sdiff keysinter keysunion key
(1)sdiff差集运算,A-B,属于A但不属于B
sdiff支持同时传入多个键
顺序先计算setA-setB,再计算结果与setC的差集,
(2)sinter交集运算
sinter同样支持同时传入多个键。
(3)sunion并集运算。
sunion同样支持同时传入多个键。
三、命令拾遗
1.获得集合中元素个数
scard key
2.进行集合运算并将结果存储
sdiffstore destination keysinterstore destination keysunionstore destination key
和sdiff功能一样,唯一区别前者不会直接返回运算结果,而是将结果存储在destination键中。
3.随机获得集合中的元素
srandmember key [count]
count参数一次随机获得多个元素。
(1)count正数,获得count个不重复的元素,当大于集合元素个数,返回集合全部元素
(2)count负数,绝对值个元素,可能相同。
4.从集合中弹出一个元素
spop key
从集合中随机选择一个元素弹出。
一、介绍
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获取列表的一个片段,
列表类型内部是使用双向链表实现的, 所以向列表两端添加元素的时间复杂度为O(1).获取越接近两端的元素速度越快。
所以不管原来有多少数据(无关),获取头部或者尾部的10条记录也是很快的。
不过使用链表的代价是通过索引访问元素比较慢。
列表类型适合用来记录日志,可以保证加入新日志的速度不会受到已有日志数量的影响。
一个列表类型键最多能容纳2^32-1个元素。
二、命令
1.向列表两端增加元素
lpush key value1 value2...左边增加rpush key value1 value2...右边增加
上面三条命令执行后的列表情况依次为
【1】
【3 2 1】
【3 2 1 0 -1】
2.从列表两端弹出元素
lpop key 左边弹出一个元素,1.将左边弹出的元素从列表删除,2,。返回被移除的元素值rpop key
IDChvzwvcD4KCjxwPjxzdHJvbmc+tO7F5Mq508NscHVzaLrNbHBvcCAgILvy1d8gcnB1c2i6zXJwb3Cw0cHQse21sdf21bs8L3N0cm9uZz48L3A+CjxwPjxzdHJvbmc+tO7F5Mq508NscHVzaLrNcnBvcCAgu/LV3yBycHVzaLrNbHBvcLDRwdCx7bWx1/a208HQPC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPjxicj4KPC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPjMuu/HIocHQse3UqsvYuPbK/Twvc3Ryb25nPjwvcD4KPHA+PHByZSBjbGFzcz0=“brush:sql;”>llen key
复杂度O(1),不会像sql语句一样,需要遍历一遍数据表来统计条目数量,Redis会直接读取现成的值。
4.获得列表片段
lrange key start stop
支持负索引,-1表示最右边的第一个元素
lrange numbers 0 -1可以获取列表中的所有元素
(1)start > stop ,返回空
(2)stop大于实际范围,返回到列表最右边的元素(“到”字很重要)
5.删除列表中指定的值
lrem key count value删除列表中钱count个值为value的元素
count > 0 ,从左开始删除count个值为value的元素
count < 0 ,从右边开始删除|count|个值为value的元素
count = 0 ,删除所有值为value的元素,
6.获得,设置指定索引的元素值
lindex key index 返回指定索引的元素lset key index value 改变指定索引的值
7.只保留列表指定片段
ltrim key start end删除索引范围 之外 的所有元素
8.向列表插入元素
linsert key before|after pivot value从左到右查找值为pivot的元素,根据 before|after插入到前面还是后面
9.将元素从一个列表转到另一个列表
rpoplpush source destination从source列表类型键的右边弹出一个元素,然后将其加入到destination列表类型键的左边,并返回这个元素的值,这个过程是原子的
当吧列表类型作为队列使用时,rpoplpush命令可以很直观的在多个队列中传递数据。
★ linux中配置安装redis方法介绍linux操作系统
★ 作文和时间赛跑
★ 和时间赛跑日记