最近遇到好多redis的面试题,今天就来好好总结一下redis的使用。关于redis API的使用这里就多说了,有兴趣的能够看下官网:Redis官网html
在redis中当键到了过时的时间,就会立马被删除掉吗?面试
咱们了解一下删除策略的知识,删除策略可分为三种redis
定时删除
(对内存友好,对CPU不友好)惰性删除
(对CPU极度友好,对内存极度不友好)按期删除
(折中)惰性删除是指每次从键空间取键的时候,判断一下该键是否过时了,若是过时了就删除。数据库
Redis采用的是惰性删除+按期删除两种策略,因此说,在Redis里边若是键到了过时的时间了,未必被立马删除的!segmentfault
内存淘汰机制
若是按期删除漏掉了不少过时key,也没及时去查(没走惰性删除),大量过时key堆积在内存里,致使redis内存块耗尽了,咋整?数组
咱们能够设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。缓存
Redis的内存淘汰机制有如下几种:网络
volatile-lru:从已设置过时时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过时时间的数据集中挑选将要过时的数据淘汰。
volatile-random:从已设置过时时间的数据集中任意选择数据淘汰
allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
allkeys-random:从数据集中任意选择数据淘汰
no-enviction:当内存达到限制的时候,不淘汰任何数据
和不少关系型数据库同样, Redis 也提供了慢查询日志记录,Redis会把执行比较慢的命令放到内部的一个list列表中。数据结构
注意:慢查询记录的只是命令的执行时间,不包括网络传输和排队时间。
关于 Redis 慢查询的配置有两个,分别是 slowlog-log-slower-than
和 slowlog-max-len
slowlog-log-slower-than
用来控制慢查询的阈值,全部执行时间超过该值的命令都会被记录下来。该值的单位为微秒
,默认值为 10000,若是设置为 0,那么全部的记录都会被记录下来,若是设置为小于 0 的值,那么对于任何命令都不会记录,即关闭了慢查询。能够经过在配置文件中设置,或者用 config set 命令来设置:dom
config set slowlog-log-slower-than 10000
slowlog-max-len
用来设置存储慢查询记录列表的大小,默认值为 128,当该列表满了时,若是有新的记录进来,那么 Redis 会把队最旧的记录清理掉,而后存储新的记录。在生产环境咱们能够适当调大,好比调成 1000,这样就能够缓冲更多的记录,方便故障的排查。配置方法和 slowlog-log-slower-than 相似,能够在配置文件中指定,也能够在命令行执行 config set 来设置:
config set slowlog-max-len 1000
Redis 专门提供了一组命令来查询慢查询日志:
SLOWLOG GET
能够看到这里查到了两条慢查询记录,分别是ZINCRRBY和ZREVRANGE命令
那么记录的中的1)2)3)4)分别表示什么呢?
1)表示日志惟一标识符uid
2)命令执行时系统的时间戳
3)命令执行的时长,以微妙来计算
4)命令和命令的参数
SLOWLOG LEN
SLOWLOG RESET
pipeline
命令用来批量操做redis命令
因为redis是单线程
的,下一次请求必须等待上一次请求执行完成后才能继续执行。执行N次命令须要N次网络时间+执行时间
,然而使用Pipeline模式,客户端能够一次性的发送多个命令,也就是1次网络时间+N次执行时间
,这样就大大的减小了网络往返时间,提升了系统性能。
下面看下伪代码:
Jedis redis = new Jedis("127.0.0.1", 6379); Pipeline pipe = redis.pipelined(); for (int i = 0; i < 10000; i++) { pipe.hmset("key_" + i, data); //将值封装到PIPE对象,此时并未执行,还停留在客户端 } pipe.sync(); //将封装后的PIPE一次性发给redis jedis.close;
那么问题来了,在什么样的情景下适合使用pipeline呢?
有些系统可能对可靠性要求很高,每次操做都须要立马知道此次操做是否成功,是否数据已经写进redis了,那这种场景就不适合。
还有的系统,多是批量的将数据写入redis,容许必定比例的写入失败,那么这种场景就可使用了,好比10000条一下进入redis,可能失败了2条无所谓,后期有补偿机制就好了,好比短信群发这种场景,若是一下群发10000条,按照第一种模式去实现,那这个请求过来,要好久才能给客户端响应,这个延迟就太长了,若是客户端请求设置了超时时间5秒,那确定就抛出异常了,并且自己群发短信要求实时性也没那么高,这时候用pipeline最好了。
Bitmap
是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset)
,在bitmap上可执行AND,OR,XOR以及其它位操做。
SETBIT
SETBIT KEY OFFSET VALUE
该命令用于对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。时间复杂度O(1)
在redis中,存储的字符串都是以二进制
的形式存在的。
如何经过SETBIT命令将'a'变成'b'呢?即将 01100001 变成 01100010(b的ASCII码是98),其实就是将'a'中的offset 6从0变成1,将offset 7从1变成0。
每次SETBIT完毕以后,会返回该位置原先的bit值。
BITCOUNT
BITCOUNT KEY [START END]
该命令统计字符串中指定范围里bit被设置为1的数量
GETBIT
GETBIT KEY OFFSET
返回key对应的string在offset处的bit值
BITOP
BITOP operation destkey key [key...]
能够对多个二进制进行交集、并集等操做,并将结果保存到 destkey 上
一个简单的例子:日活跃用户
为了统计今日登陆的用户数,咱们创建了一个bitmap,每一位标识一个用户ID。当某个用户访问咱们的网页或执行了某个操做,就在bitmap中把标识此用户的位置为1。这样的话咱们就能够经过BITCOUNT
命令获得当日活跃用户数。
注意:使用BitMap统计流量适用于亿级流量系统,若是系统用户只有10w的话使用set数据结构会更好。
GEO
功能在Redis3.2版本提供,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。GEO的数据类型为zset
。
geoadd
geoadd cityGeo 116.405285 39.904989 "北京" geoadd cityGeo 121.472644 31.231706 "上海"
geopos
127.0.0.1:6379> geopos cityGeo 北京 1) "116.40528291463851929" 2) "39.9049884229125027"
geodist
127.0.0.1:6379> geodist cityGeo 北京 上海 "1067597.9668" 127.0.0.1:6379> geodist cityGeo 北京 上海 km "1067.5980"
georadius
georadius cityGeo 116.405285 39.904989 100 km WITHDIST WITHCOORD ASC COUNT 5
能够指定WITHDIST返回距离,WITHCOORD返回经纬度,WITHHASH返回geohash值
能够指定ASC或DESC,根据距离来排序
能够指定COUNT限定返回的记录数
georadiusbymember
georadiusbymember cityGeo 北京 100 km WITHDIST WITHCOORD ASC COUNT 5
zrem
zrem cityGeo 北京
本质上布隆过滤器是一种数据结构,比较巧妙的几率型数据结构,特色是高效地插入和查询,能够用来告诉你 “某样东西必定不存在或者可能存在”。
相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,可是缺点是其返回的结果是几率性的,而不是确切的。
原理
其本质就是一个只包含0和1的bit数组。具体操做当一个元素被加入到集合里面后,该元素经过K个Hash函数运算获得K个hash后的值,而后将K个值映射到这个位数组对应的位置,把对应位置的值设置为1。查询是否存在时,咱们就看对应的映射点位置若是全是1,他就极可能存在(跟hash函数的个数和hash函数的设计有关),若是有一个位置是0,那这个元素就必定不存在。
若是咱们要映射一个值到布隆过滤器中,咱们须要使用多个不一样的哈希函数生成多个哈希值,并对每一个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不一样的哈希函数分别生成了哈希值 一、四、7,则上图转变为
Ok,咱们如今再存一个值 “tencent”,若是哈希函数返回 三、四、8 的话
值得注意的是,4 这个 bit 位因为两个值的哈希函数都返回了这个 bit 位,所以它被覆盖了。如今咱们若是想查询 “dianping” 这个值是否存在,哈希函数返回了 一、五、8三个值,结果咱们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,所以咱们能够很肯定地说 “dianping” 这个值不存在。而当咱们须要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 一、四、7,而后咱们检查发现这三个 bit 位上的值均为 1,那么咱们能够说 “baidu” 存在了么?答案是不能够,只能是 “baidu” 这个值可能存在。
这是为何呢?答案跟简单,由于随着增长的值愈来愈多,被置为 1 的 bit 位也会愈来愈多,这样某个值 “taobao” 即便没有被存储过,可是万一哈希函数返回的三个 bit 位都被其余值置位了 1 ,那么程序仍是会判断 “taobao” 这个值存在。
应用场景
常见的适用应用场景有,利用布隆过滤器减小磁盘 IO 或者网络请求,由于一旦一个值一定不存在的话,咱们能够不用进行后续昂贵的查询请求,好比能够用来解决缓存穿透
的问题。
今天先对redis有个初步的了解,下一篇咱们来深刻探讨Redis:Redis进阶