Redis初识

背景

最近遇到好多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-thanslowlog-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

慢查询redis.PNG

能够看到这里查到了两条慢查询记录,分别是ZINCRRBY和ZREVRANGE命令
那么记录的中的1)2)3)4)分别表示什么呢?
1)表示日志惟一标识符uid
2)命令执行时系统的时间戳
3)命令执行的时长,以微妙来计算
4)命令和命令的参数

获取当前慢查询日志记录数

SLOWLOG LEN

慢查询日志重置

SLOWLOG RESET

Pipeline

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

Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR以及其它位操做。

API

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

GEO功能在Redis3.2版本提供,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。GEO的数据类型为zset

geoadd

  • 添加经纬度信息
geoadd cityGeo 116.405285 39.904989 "北京"
geoadd cityGeo 121.472644 31.231706 "上海"

geopos

  • 查找指定key的经纬度信息,能够指定多个key,批量返回
127.0.0.1:6379> geopos cityGeo 北京
1) "116.40528291463851929"
2) "39.9049884229125027"

geodist

  • 返回两个地方的距离,能够指定单位,好比米m,公里km,英里mi,英尺ft
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

  • 和georadius同样,不过中心点是由给定的位置元素决定的,而不是像georadius那样,使用输入的经度和纬度来决定中心点。
georadiusbymember cityGeo 北京 100 km WITHDIST WITHCOORD ASC COUNT 5

zrem

  • GEO没有提供删除成员的命令,可是由于GEO的底层实现是zset,因此能够借用zrem命令实现对地理位置信息的删除.
zrem cityGeo 北京

布隆过滤器

本质上布隆过滤器是一种数据结构,比较巧妙的几率型数据结构,特色是高效地插入和查询,能够用来告诉你 “某样东西必定不存在或者可能存在”。

相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,可是缺点是其返回的结果是几率性的,而不是确切的。

原理

其本质就是一个只包含0和1的bit数组。具体操做当一个元素被加入到集合里面后,该元素经过K个Hash函数运算获得K个hash后的值,而后将K个值映射到这个位数组对应的位置,把对应位置的值设置为1。查询是否存在时,咱们就看对应的映射点位置若是全是1,他就极可能存在(跟hash函数的个数和hash函数的设计有关),若是有一个位置是0,那这个元素就必定不存在。

若是咱们要映射一个值到布隆过滤器中,咱们须要使用多个不一样的哈希函数生成多个哈希值,并对每一个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不一样的哈希函数分别生成了哈希值 一、四、7,则上图转变为
image

Ok,咱们如今再存一个值 “tencent”,若是哈希函数返回 三、四、8 的话

image

值得注意的是,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进阶

参考

Redis单排
详解布隆过滤器

相关文章
相关标签/搜索