关于Redis 经常被问到的面试题

151 阅读4分钟

redis作为互联网技术应用十分广泛的中间件,有着非常卓越的性能。在当下redis已经成为后端开发工程师必不可少需要掌握的技能。

redis 可以用来做什么

  • 缓存中间件
  • 限流
  • 实现分布式锁
  • 消息队列
  • pub,sub发布订阅消息
  • 排行榜以及计数

redis 有哪些数据结构

    redis有着五种基本的数据结构包括 :字符串String, 字典Hash, 列表List, 集合Set, 有序集合SortedSet。

    除此之外redis还有着以下几种高级数据结构:HyperLogLog, Geo, Pub/Sub,布隆过滤器,Redis-ML

讲一下redis的分布式锁

    说起分布式锁,大家并不陌生,特别是分布式应用处理逻辑的遇到的并发问题,我们经常使用redis, zookeeper, apollo 等来实现分布式锁,下面主要讲讲redis是如何实现分布式锁的。

    分布式锁实质上是先要在redis中占坑。redis一般使用setnx 先占有锁资源,然后通过del释放锁

 // 伪代码
 if setnx locker_key true {    
     // todo somthing    
     del locker_key
 }

以上逻辑还是有问题,主要原因在于setnx 和 expire 两条指令不是一条原子操作,如果这两条指令可以同时一起执行就不会出现问题。

在redis2.8 set 执行增加了扩展参数,setnx和expire 指令可以合并在一起执行。

// 伪代码
if set key true ex 5 nx {    
    // todo somthing    
    del locker_key
}

但是还是有个问题需要解决就是分布式锁的超时问题,如果在加锁和释放锁之间,由于业务执行时间过长,导致锁超时,出现锁过期现象。解决思路如下,在释放锁的时候,每个锁持有者只能释放自己的锁,需要在释放锁的时候进行校验,在上锁的时候,key是唯一的,但是value可以不一样。所以可以将value作为每个持有锁者的身份标识。为了保证匹配value和del执行的原子性,使用lua脚本实现:

luaScript: if redis.call('get',KEYS[1])  == ARGV[1] then  return redis.call('del',KEYS[1])  else  return 0  end

redis持久化

    redis 数据全部都在内存中,如果服务突然宕机,那么数据就会丢失。redis的持久化就是为了保障数据不会因为故障丢失。

    redis持久化机制有两种,第一种是快照,第二种是AOF日志。快照是全量备份,数据是二进制序列化形式。AOF是增量备份,是对于redis内存数据修改的指令的合集。由此可知,时间越长AOF日志内容就会越大。

   快照是怎么实现的?

   redis实现快照主要利用的是操作系统多进程 写时复制(copy-on-write)来实现的

redis 为什么这么快?

    redis作为一个单线程模型的服务器,他所有的操作都是在内存中完成。查找和操作的时间复杂度都是O(1)

    使用 I/O 多路复用机制同时监听多个文件描述符的可读和可写状态,一旦收到网络请求就会在内存中快速处理,由于绝大多数的操作都是纯内存的,所以处理的速度会非常地快。

    采用单线程,避免线程切换上下文带来的CPU消耗,避免锁的问题。多线程虽然在某些方面能够表现良好,但是由于程序执行的顺序不确定性,多个线程访问共享变量带来的竞争问题,可能会导致难以预测的结果。加入多线程模型,同时需要引入并发控制来保证执行程序的顺序性。Redis4.0 引入了多线程,但是也只是在部分命令上面加入了多线程的处理,例如删除等一些非阻塞的操作。Redis 主程序继续使用单线程的,多线程带虽然能够充分利用CPU的性能来达到并发的效果,但是Redis 的性能瓶颈不在于CPU, 而是在网络传输,以及客户端等待过程中的时间消耗,也就是我们说的网络I/O。所以多线程带来的优势并不能进一步提升Redis性能。

坚持思考,方向比努力更重要。关注我领取更多关于Redis的面试题:技术那些事儿