redis使用基础(三)
——Redis事务与过期时间
(转载请附上本文链接——linhxx)
一、事务(Transaction)
1、概述
事务的定义和关系型数据库一样,保证各个步骤操作的原子性。另外,也保证这几个步骤之间不会插入其他的步骤。但是,redis的事务没有回退的功能。
redis事务开始和结束的命令分别是MULTI和EXEC,在这两个命令之间的其他命令,redis都会先存在队列中,待接收到EXEC后一起执行。会返回一串的内容,返回值的顺序和语句顺序一致。
当输入MULTI,尚未输入EXEC时,如果redis和客户端断线,则multi后面的命令全部不执行,并且redis会清空刚刚还没执行的队列;但是如果已经输入EXEC,则即使后面发生断网,中间的命令也会执行,因为命令已经存储在redis的队列中。
另外,multi和exec之间的命令会同步执行,一起返回结果,所以不能在事务里面先获取上一个命令操作的值进行下一个命令的操作。
2、错误处理
1)语法错误
如果在multi和exec之间有语法错误,则所有的命令都不会执行,包括正确的命令。
2)运行错误
运行错误时,除了错误的命令,其他正确的命令都会被执行。
3、WATCH命令
watch类似锁的功能,当watch某个key后,果在事务之前修改了此key,则在事务中无法操作此key。watch只针对一次的事务,即当匹配到第一个exec后,就不再监听事务。
因此,执行一次exec后,就会取消对所有key的watch。
如果要提前取消watch,则可以在事务命令multi之前,发送一个UNWATCH命令,则取消所有的watch。
但是,如果键是在过期时间后被自动删除,并不会被watch认为键被改变。
4、取消事务
DISCARD命令,使用后会放弃执行事务的所有命令。如果遇到watch,则此时除了放弃事务,还会自动再执行一个unwatch命令,取消监视。
二、过期时间
redis的过期时间使用场景很广泛,当需要设置缓存、令某个值仅在一段时间内有效(如优惠券等)、设置最短访问间隔(防止爬虫太多导致服务器宕机),则都需要设置过期时间。
1、命令
1)设置失效时间
1. EXPIRE key seconds,seconds是一个数字,即设置key在seconds秒之后失效,second要求是整数,即最少是1秒。当键不存在或者设置失败会返回0,否则返回1。
2. PEXPIRE key millsecond,相当于设置毫秒,PEXPIRE key 1000 等效于EXPIRE key 1。
3. EXPIREAT key unixtime,将key的过期时间以unix时间进行设置,单位是秒。
4. PEXPIREAT key unixmilltime,与3的区别是这个是设置毫秒的。
2)查看还有多久有效
TTL key,返回值是剩余的时间,单位是秒。如果不存在,则返回-2;如果没有设置过期时间,则返回-1。(这是redis2.8版的,2.6版则不存在和没有设置过期都是-1)
PTTL key,返回剩余的毫秒数。
3)取消时间限制
1. PERSIST key,则此时再对key进行TTL命令查看,会发现返回-1。
2. 重新SET key value,则此时会清除原来的时间限制。因此如果对值进行重新设置,需要重新设置失效时间。
3. 重新expire key seconds,则会重置key的失效时间。
4. 除了set,其他只对键值进行操作的命令,如lpush、hset、incr等均不会影响过期时间。
2、业务场景
1)缓存
redis是将数据存在内存中,因此可以避免I/O的操作,以加快速度。
通常,在读数据库的时候,会先读缓存,如果有的话则直接返回,如果没有的话会去数据库拉数据,并把数据设置在redis上,再返回。此时,需要对redis的键名进行考量,通常要设置类似:id:title等作为键,把值序列化或者json后进行存储。存储的数据类型也需要考虑,是放于set、hash还是list,或者特殊情况下用sorted set。
但是,也不是所有的操作放入缓存,只有大容量(图片、长评论、文章等)或者频繁改动(如访问量)等放于缓存。相应的失效时间也需要考虑好。
对于新增,通常使用懒加载的配置,即新增数据不更新缓存,会等到查询的时候再更新缓存;但是修改、删除数据的时候,就有很多的解决方案了。
对于修改、删除数据,可以用类unix系统的方式进行处理,称为Write Behind Caching Pattern,又称Write Back。即当更新、删除数据时,会先查缓存,如果没有查到,则直接操作数据库。如果查到有缓存,则更新缓存,并给缓存增加一个已经被改动的标记;当要删除操作时,则对缓存加一个删除标记。
系统有独立的地方记录上述每一处缓存的改动,当程序执行到结束时,会统一查看缓存的改动情况,并统一对数据库进行操作。
此方法在高并发时,在命中缓存的情况下,必然数据一致性:
当更新了数据,此时如果有其他并发的再来查询,会查询到最新的数据(因为命中缓存,直接从缓存查看结果);如果要更新操作时,由于被加了已经更新的标签,不允许再次更新,则后面的更新会返回错误码;如果要删除,则可以删除,并增加删除标签(也可以根据业务需要,配置成不允许删除)。
当删除了数据,由于数据已经被加上删除标签,则此时再次增删改查,会发现此删除标签,则会返回空结果,不从数据库查询。
程序执行结束后,会根据标签,有修改标签的对数据库相应字段进行update,有删除标签的删除数据库相应数据。
2)设置优惠券
优惠券的存储可以用优惠券:优惠券id:优惠券类型的方式作为键,值可以设置1,并根据优惠券的使用时间定制expire second。当查询优惠券有效期或者使用优惠券时,使用ttl命令查询键,如果过期则该键不存在,此时则返回-2,否则返回优惠券的有效期。
3)最短访问间隔
此场景设置ip:ip值:业务场景为键,值可以设置1,并且设置expire second。当某个ip访问时,在redis中进行查询,如果键存在,则说明在最短访问间隔内,则不允许访问;如果键不存在,则说明不在最短访问间隔,则设置一个值,并且本次允许访问。
4)时间段内访问总数
如果要设置用户某个时间段(如一分钟内)可以访问的页面总数,则可以用列表的方式进行存储。当用户进行访问时,先用llen判断加上这个是否超过10个,如果不是则lpush,并且允许此次访问;如果超过,则要判断这个和最右边的第一个(lrange -1,1)的时间差是否在1分钟内,如果在一分钟内,则不改变此list,并且不允许用户的此次访问;如果超过一分钟,则lpush此次的时间,并且rpop第一个时间,允许用户此次访问。
3、redis用作缓存的时间设置
缓存的时间设置太长,会导致redis占用大量的内存;但是设置的太短,又会使得redis的作用减少。因此,可以通过修改配置文件的maxmemory参数,设置redis的最大占用内存,并且设置maxmemory-policy设置内存超出时的策略。
内存超出时会自动删除数据,删除的规则通常采用LRU(Least Recently Used),最近最少使用原则。另外也可以设置其他规则,如不同的参数值确定是否删除未设置过期时间的键,或随机删除一个键,或删除过期时间最近的一个键,或者不删除仅返回错误。
——written by linhxx
更多最新文章,欢迎关注微信公众号“决胜机器学习”,或扫描右边二维码。