面试准备-redis(持续更新)

1、Redis用过哪些数据类型,以及Redis底层怎么实现

Redis支持五种数据类型:string(字符串,get,set),hash(哈希,hmget,hmset),list(列表,lpush),set(集合,sadd)及zset(sorted set:有序集合,zadd)。
http://www.cnblogs.com/binyue/p/5342281.html
https://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html

/*
 * Redis 对象
 */
typedef struct redisObject {
    // 类型
    unsigned type:4;
    // 对齐位
    unsigned notused:2;
    // 编码方式
    unsigned encoding:4;
    // LRU 时间(相对于 server.lruclock)
    unsigned lru:22;
    // 引用计数
    int refcount;
    // 指向对象的值
    void *ptr;
} robj;
type:redisObject的类型,字符串、列表、集合、有序集、哈希表
encoding:底层实现结构,字符串、整数、跳跃表、压缩列表等
ptr:实际指向保存值的数据结构

2、Redis缓存穿透,缓存雪崩

https://blog.csdn.net/zeb_perfect/article/details/54135506

3、如何使用Redis来实现分布式锁

jedis.set(lockKey, requestId, “NX”, “PX”, expireTime);
第一个为key,我们使用key来当锁,因为key是唯一的。
第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
第五个为time,与第四个参数相呼应,代表key的过期时间。
http://www.importnew.com/27477.html

@param nxxx nxxx的值只能取NX或者XX,如果取NX,则只有当key不存在是才进行set,如果取XX,则只有当key已经存在时才进行set
@param expx expx的值只能取EX或者PX,代表数据过期时间的单位,EX代表秒,PX代表毫秒。

4、Redis的并发竞争问题如何解决

redis本身是不会存在并发问题的,因为他是单进程的,再多的command都是one by one执行的。我们使用的时候,可能会出现并发问题
1)使用watch命令,使用乐观锁方式,类似于CAS
2)redis客户端进行操作的时候,获取锁,类似synchronized和lock
https://www.cnblogs.com/shamo89/p/8385390.html

5、Redis持久化的几种方式,优缺点是什么,怎么实现的

https://segmentfault.com/a/1190000002906345
rdb持久化:rdb是保存一份数据快照,是二进制文件,可以定时出发或者手动触发。SAVE命令会使用同步的方式生成RDB快照文件,这意味着在这个过程中会阻塞所有其他客户端的请求。BGSAVE命令使用后台的方式保存RDB文件

过程:Redis调用fork(),产生一个子进程。子进程把数据写到一个临时的RDB文件。当子进程写完新的RDB文件后,把旧的RDB文件替换掉。

优点:RDB文件是一个很简洁的单文件,它保存了某个时间点的Redis数据,很适合用于做备份。你可以设定一个时间点对RDB文件进行归档,这样就能在需要的时候很轻易的把数据恢复到不同的版本。比起AOF,在数据量比较大的情况下,RDB的启动速度更快。

缺点:RDB容易造成数据的丢失。假设每5分钟保存一次快照,如果Redis因为某些原因不能正常工作,那么从上次产生快照到Redis出现问题这段时间的数据就会丢失了。RDB使用fork()产生子进程进行数据的持久化,如果数据比较大的话可能就会花费点时间,造成Redis停止服务几毫秒。如果数据量很大且CPU性能不是很好的时候,停止服务的时间甚至会到1秒。

aof持久化:每当Redis接受到会修改数据集的命令时,就会把命令追加到AOF文件里,当你重启Redis时,AOF里的命令会被重新执行一次,重建数据。

优点:比RDB可靠。你可以制定不同的fsync策略:不进行fsync、每秒fsync一次和每次查询进行fsync。默认是每秒fsync一次。这意味着你最多丢失一秒钟的数据。
缺点:在相同的数据集下,AOF文件的大小一般会比RDB文件大。

AOF重写:新文件上会写入能重建当前数据集的最小操作命令的集合。所谓的最小数据集操作不是对旧的AOF文件进行读取分析,而是当前时刻的数据快照,和rdb的二进制文件不同,aof文件是命令的集合。

过程:Redis调用fork(),产生一个子进程。子进程把新的AOF写到一个临时文件里。主进程持续把新的变动写到内存里的buffer,同时也会把这些新的变动写到旧的AOF里,这样即使重写失败也能保证数据的安全。当子进程完成文件的重写后,主进程会获得一个信号,然后把内存里的buffer追加到子进程生成的那个新AOF里,新的AOF文件替换旧的AOF文件

两种方式可以同时开启,重建时,aof的优先级更高

6、Redis的缓存失效策略

定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

7、Redis的数据淘汰策略

当客户端申请内存无法满足时触发数据淘汰策略,通过配置redis.conf中的maxmemory这个值
LRU:最近最久未使用
主键空间和设置了过期时间的键空间,举个例子,假设我们有一批键存储在Redis中,则有那么一个哈希表用于存储这批键及其值,如果这批键中有一部分设置了过期时间,那么这批键还会被存储到另外一个哈希表中,这个哈希表中的值对应的是键被设置的过期时间。设置了过期时间的键空间为主键空间的子集。
noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
allkeys-lru:在主键空间中,优先移除最近未使用的key。
volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
allkeys-random:在主键空间中,随机移除某个key。
volatile-random:在设置了过期时间的键空间中,随机移除某个key。
volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
http://blog.jobbole.com/105335/

8、Redis缓存分片

分片路由:
1)客户端分片路由,客户端直接访问分片的实例,服务端节点变化对客户端有影响
2)服务端分片路由:客户端访问任意节点,如果数据不在该分片上,返回客户端真正的节点重新访问
3)代理中间件分片路由:客户端访问代理,由代理访问真实的redis节点。

分片策略:
预分片:一般的分片是因为数据量比较大了,所以做分片,分开存储。预分片就是先提前做好分片,每个分片的存储空间随着数据量增加做扩展,这样不影响分片的路由规则。

一致性哈希:hash分片的时候一般是hashcode%n,n表示分片数目,当某一个分片节点GG的时候,n会发生变化,导致部分其实存在的数据被路由到其他节点上,缓存等于不生效了。一致性hash就是n不变,2^32,同时将节点形成虚拟的hash环形桶,对n取模后的节点如果不存在,顺时针找到接下来存在的第一个节点,从那个节点上获取缓存,更新数据等操作,方便进行扩容缩容,将数据影响降低到相邻节点。缺点就是下一个节点会完全承受上一个节点的压力,有可能造成连环宕机;或者是数据倾斜,大部分常用数据在少部分节点上,分布式分散压力的作用不生效。

虚拟节点:在环形hash桶上,一个节点拆分成多个虚拟节点间隔起来,n个节点,虚拟成3n个节点,虚拟1(1)-虚拟11(4)-虚拟4(2)-虚拟7(3),类似这样的环形顺序,其他和一致性哈希一样,因为是间隔分部,所以当一个节点gg,该节点的数据请求压力被所有节点共同分担,减少了连环宕机的风险

Redis Cluster中,分片采用slot(槽)的概念,一共分成16384个槽,这有点儿类似前面讲的pre sharding思路。对于每个进入Redis的键值对,根据key进行散列,分配到这16384个slot中的某一个中。使用的hash算法也比较简单,就是CRC16后16384取模。Redis集群中的每个node(节点)负责分摊这16384个slot中的一部分,也就是说,每个slot都对应一个node负责处理。当动态添加或减少node节点时(node之间互相通信确定节点状态),需要将16384个槽做个再分配,槽中的键值也要迁移。(感觉和虚拟节点的意思差不多,就是一个是虚拟位置,一个是真的有这么多槽)

9、Redis集群架构
那些年用过的Redis集群架构

灾备:每个实例可以存在备份节点,形成master-slave形式(redis cluster通过node间互相通信,客观下线/主观下线,自动故障转移。redis sharding可以通过哨兵进行主节点监控以及故障转移)
主从复制:第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,复制节点bgsave的数据,加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

10、Redis缓存与数据库双写一致性
这里的一致性是指:数据库数据发生更新后,缓存中的数据要和数据库保持一致
在刷新缓存的时候一般有两种方式:1是更新缓存,2是删除缓存,相对来说删除操作更简单,速度更快,所以一般选择删除缓存

更新数据库和删除缓存数据的先后顺序

  • 先删除缓存,再更新数据库
    在高并发下容易出问题,在原子性被破坏时表现优异
  • 先更新数据库,再删除缓存(Cache Aside Pattern设计模式)
    在高并发下不容易出问题,在原子性被破坏时表现不如意

详细分析说明请参考:面试前必须要知道的Redis面试题

你可能感兴趣的:(面试,中间件)