zoukankan      html  css  js  c++  java
  • Redis 缓存基础知识面试问题总结

    1、什么情况下使用redis?

         (1)热点数据,又是临时用一下,又想提高并发速度,吞吐量,那就可以考虑,如淘宝的节假日的销售活动。 

         (2)更新不频繁的数据

    2、redis 可以放哪几种数据?

         五种数据:字符串、hash、List、Set、Zset  

     

    3、Redis有哪些优缺点

    优点

    • 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。

    • 支持数据持久化,支持AOF和RDB两种持久化方式。

    • 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。

    • 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。

    • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

    缺点

    • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

    • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。

    • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。

    • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

    4、Redis为什么这么快?

           原因有以下几点:

           a.基于内存操作:Redis的所有数据都存在内存中,因此所有的运算都是内存级别的,所以它的性能比较高。

           b.数据结构简单:Redis的数据结构比较简单,是为Redis专门设计的,而这些简单的数据结构的查找和操作的时间复杂度都是O(1)。

           c.多路复用和非阻塞IO:Redis使用IO多路复用功能来监听多个socket连接的客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了IO阻塞操作,从而大大提高了Redis的性能。

           d.避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生。

         官方使用的基准测试结果表明,单线程的Redis可以达到10W/S的吞吐量。

    5、redis是单线程还是多线程,为什么?

        在Redis4.0之前,Redis是单线程运行的!

        原因是Redis是基于内存的,它的瓶颈在于机器的内存、网络带宽,而不是CPU,在CPU还没达到瓶颈时机器内存可能就满了、或者带宽达到瓶颈了。因此CPU不是主要原因,那么自然就采用单线程了,况且使用多线程比较麻烦。

        但是在Redis4.0的时候,已经开始支持多线程了,比如后台删除等功能。

        简单来说,Redis在4.0之前使用单线程的模式是因为以下三个原因:

             1.使用单线程模式的Redis,其开发和维护更简单,因为单线程模式方便开发和调试。

             2.即使使用单线程模型也能够并发地处理多客户端的请求,主要是因为Redis内部使用了基于epoll的多路复用。

             3.对于Redis来说,主要的性能瓶颈是内存或网络带宽,而非CPU。

    6.redis的IO多路复用是什么?

           当调用读取操作read方法时,缓冲区没有任何数据,那么这个线程会卡在这里,直到缓冲区有数据或者连接被关闭时,read方法才会返回,该线程才能继续处理其他业务。

           但这样显然就降低了程序的执行效率,而Redis使用的时非阻塞的IO,这就意味着IO的读写流程不再是阻塞的,读写方法都是瞬间完成并且返回的,也就是它会采用能读多少就读多少、能写多少就写多少的策略来执行IO操作,这显然更符合我们对性能的追求。

          但这种非阻塞的IO也面临一个问题,那就是当我们执行读取操作时,有可能只读取了一部分数据;同理写数据也是这种情况,当缓冲区满了,而我们的数据还没有写完,那么生效的数据何时写就成了一个问题。

          而IO的多路复用就是解决上面的这个问题的,使用IO多路复用最简单的方式就是使用select函数,此函数是操作系统提供给用户程序的API接口,用于监控多个文件描述符的可读和可写情况的,这样就可以监控到文件描述符的读写事件了。当监控到相应的时间之后就可以通知线程处理相应的业务了,这样就保证了Redis读写功能的正常执行。

    【不过现在的操作系统已经基本上不适用select函数了,改为调用epoll函数(Linux)了,macOS则是使用Kqueue(继承与Unix),因为select函数在文件描述符非常多的时候性能非常差。】

    7.Redis6.0中的多线程?

            Redis单线程的优点非常多,不但降低了Redis内部实现的负责性,也让所有操作都可以在无锁的情况下进行,并且不存在死锁和线程切换带来的性能以及时间上的消耗;

            但是其缺点也很明显,单线程机制导致Redis的QPS(Query Per Second,每秒查询数)很难得到有效的提高(虽然够快了,但人毕竟还是要有更高的追求的)。

            Redis在4.0版本中虽然引入了多线程,但是此版本的多线程只能用于大数据量的异步删除,对于非删除操作的意义并不是很大。

            如果我们使用Redis多线程就可以分摊Redis同步读写IO的压力,以及充分利用多核CPU资源,并且可以有效的提升Redis的QPS。在Redis中虽然使用了IO多路复用,并且是基于非阻塞的IO进行操作的,但是IO的读写本身是阻塞的。比如当socket中有数据时,Redis会先将数据从内核态空间拷贝到用户态空间,然后再进行相关操作,而这个拷贝过程是阻塞的,并且当数据量越大时拷贝所需要的的时间就越多,而这些操作都是基于单线程完成的。

            因此在Redis6.0中新增了多线程的功能来提高IO的读写性能,它的主要实现思路是将主线程的IO读写任务拆分给一组独立的线程去执行,这样就可以使用多个socket的读写并行化了,但Redis的命令依旧是主线程串行执行的。

           但是注意:Redis6.0是默认禁用多线程的,但可以通过配置文件redis.conf中的io-threads-do-reads 等于 true 来开启。但是还不够,除此之外我们还需要设置线程的数量才能正确地开启多线程的功能,同样是修改Redis的配置,例如设置 io-threads 4,表示开启4个线程。

    【关于线程数的设置,官方的建议是如果为4核CPU,那么设置线程数为2或3;如果为8核CPU,那么设置线程数为6.总之线程数一定要小于机器的CPU核数,线程数并不是越大越好。】

    8、Redis的应用场景

    • 计数器

    可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

    • 缓存

    将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。

    • 会话缓存

    可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

    • 查找表

    例如 DNS 记录就很适合使用 Redis 进行存储。查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。

    • 消息队列(发布/订阅功能)

    List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息。不过最好使用 Kafka、RabbitMQ 等消息中间件。

    • 分布式锁实现

    在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。

    • 其它

    Set 可以实现交集、并集等操作,从而实现共同好友等功能。ZSet 可以实现有序性操作,从而实现排行榜等功能。

    9、redis 如何做内存优化?

    1、缩减键值对象

      缩减键(key)和值(value)的长度,

    key长度:如在设计键时,在完整描述业务情况下,键值越短越好。

    value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等,下图是JAVA常见序列化工具空间压缩对比。

    2、共享对象池

      对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。

    3、字符串优化

    4、编码优化

    5、控制key的数量

  • 相关阅读:
    Codeforces Round #131 (Div. 2) E. Relay Race dp
    Codeforces Round #130 (Div. 2) C
    Codeforces Round #130 (Div. 2) A. Dubstep
    UVA 11858 Frosh Week 逆序对统计
    UVA 11149 Power of Matrix 快速幂
    UVA 12382 Grid of Lamps 贪心
    POJ 3614 Sunscreen 贪心
    Codeforces Round #238 (Div. 2) D. Toy Sum 暴搜
    Codeforces Round #227 (Div. 2) E. George and Cards 线段树+set
    Codeforces Round #327 (Div. 2) E. Three States
  • 原文地址:https://www.cnblogs.com/aitree/p/14417564.html
Copyright © 2011-2022 走看看