提到事务大家一定都不陌生,在关系型数据库MySql、Oracle中都存在事物,最常见的就是事物的提交(commit)和事物的回滚(rollback)。
但是Redis中并没有类似于关系型数据库的回滚(rollback)。Redis中是事务是一组命令的集合,事务同Redis中的其他命令一样也是最小执行单位。
MULTI命令
从MULTI命令开始,直到EXEC,之间的所有命令就是被事务管理的一组命令。
MULTI
SADD "user:1:following" 2
SADD "user:2:following" 1
EXEC
错误处理
- 语法错误:命令不存在或者命令参数个数不对
MULTI
SET key value
SET key
ERRORCOMMAND key
EXEC
这种情况下,Redis直接 返回错误错误,即使命令集合中有正确的命令也不会执行。
- 运行错误:运行错误是指命令在执行过程中出现的错误,如散列类型的命令操作集合类型的键,这种情况下命令集合中有命令执行出错,其他命令还会继续执行,包括错误命令之后的命令。
MULTI
HSET key field1 value
SADD key 2
HSET key field1 value2
EXEC
HGET key field1
WATCH命令
WATCH命令可以控制一个或多个键,一旦其中有一个键被修改或删除,之后的事务就不会执行。监控一直持续到EXEC命令(其实事务中的命令是在EXEC之后执行的,所以在MULTI之后可以修改WATCH监控的键值)。
SET key 1
WATCH key
SET key 2
MULTI
SET key 3#不会执行
EXEC
GET key
UNWATCH
:取消监控
设置过期时间
EXPIRE key seconds
:设置键的过期时间,秒为单位
当键不存在时返回-1
PEXPIRE key mseconds
:设置键的过期时间,毫秒为单位
TTL获取剩余过期时间
当键没有设置过期时间返回-1,键不存在时返回-2(2.6版本中以上两种情况都会返回-1,2.8版本及以后才分为以上两种情况返回)
PERSIST
:取消过期时间设置
成功返回1,失败返回0(键不存在或键本身就是永久的)
实现访问频率限制之一
每分钟同一个用户访问次数不能超过100
$isKeyExists = EXISTS rate.limiting:$IP
if $isKeyExists is 1
$times = INCR rate.limiting:$IP
if $times > 100
print 访问频率超过限制,请稍后再试
exit
else
INCR rate.limiting:$IP
EXPIRE $keyName, 60
上面这段代码存在一个不太明显的问题,就是当代码运行到倒数第二行后,由于某种原因中断运行了,那么就会来一个很严重的问题:对应IP的用户在管理员手动删除该键之前,最多只能访问该站100次!
为了保证键的建立和键设置过期时间一起执行,可以使用上面学习的事物功能,修改后的代码如下:
$isKeyExists = EXISTS rate.limiting:$IP
if $isKeyExists is 1
$times = INCR rate.limiting:$IP
if $times > 100
print 访问频率超过限制,请稍后再试
exit
else
NULTI
INCR rate.limiting:$IP
EXPIRE $keyName, 60
EXEC
实现访问控制之二
上面的做法确实能做到每分钟控制用户访问100次,但如果用户在上一分钟的第一秒访问1次,最后一秒访问99次,在下一分钟的第一秒访问100次,这样的话在两秒内就访问了199次,这与每分钟访问100次相差太大了!所以要想做到绝对的没分钟访问100次,就必须记录用户的访问历史,将访问历史纪录到list中,根据list的长度和最早一次访问的时间,控制访问频率:
$listLength = LLEN rate..limiting.$IP
if $listLength < 100
LPUSH rate.limiting:$IP,now()
else
$time = LINDEX rate.limiting:$IP,-1
if now() - $time < 60
print 访问频率超过限制,请稍后再试
else
LPUSH rate.limiting:$IP,now()
LTRIM rate.limiting:$IP,0,9
实现缓存
为了提高网站的负载能力,常常需要将一些访问频率较高但是对CPU或IO资源消耗较大的操作缓存起来,并希望缓存一段时间后自动过期。伪代码如下:
$rank = GET cache:rank
if not $rank
$rank = 计算排名...
MUITI
SET cache:rank,$rank
EXPIRE cache:rank,7200
EXEC
但实际使用场景中,缓存过期时间的设置是一大难题:设置过久保证不了数据的真实性,还会占用内存,设置短了会导致缓存命中率过低,而拜拜浪费了内存。
Reidis中可以限制其使用的最大内存:修改配置文件中的maxmemory参数,限制Redis最大使用的内存大小(单位是byte)。当占用内存超过限制最大内存时Redis会依据maxmemory-policy参数指定的策略来删除不必要的键直至Redis占用的内存小于指定最大占用内存。maxmemory-policy支持的规则如下表:
规则 | 说明 |
---|---|
volatile-lru | 使用LRU算法删除一个键(只删除设置了过期时间的键) |
allkeys-lru | 使用LRU算法删除一个键 |
volatile-random | 随机删除一个键(只删除设置了过期时间的键) |
allkeys-random | 随机删除一个键 |
volatile-ttl | 删除过期时间最近的一个键 |
noteviction | 不删除键,只返回错误 |
如当设置maxmomory-policy的值为volatile-lur,当redis占用内存超过最大内存时,redis就会不断删除设置了过期时间中,使用最近使用最少的键。