关于Redis的相关问题

Redis的线程模型

1.Redis是线程安全的吗?

Redis的单线程程序,所以是线程安全的。

Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的。

但是,Redis 程序并不是单线程的,Redis 在启动的时候,是会启动后台线程(BIO)的:

关于Redis的相关问题_第1张图片

关闭文件AOF 刷盘释放内存这三个任务都有各自的任务队列:

  • BIO_CLOSE_FILE,关闭文件任务队列:当队列有任务后,后台线程会调用 close(fd) ,将文件关闭;
  • BIO_AOF_FSYNC,AOF刷盘任务队列:当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,
  • BIO_LAZY_FREE,lazy free 任务队列:当队列有任务后,后台线程会 free(obj) 释放对象 / free(dict) 删除数据库所有对象 / free(skiplist) 释放跳表对象;

2.Redis单线程为什么还能这么快?

  • Redis是基于内存的,内存的读写速度非常快;
  • Redis是单线程的,避免了不必要的上下文切换和竞争条件
  • Redis使用多路复用技术,可以处理并发的连接。非阻塞I/O内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。

3.Redis6.0之前为什么Redis是单线程的?

官网回答:因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。

4.Redis6.0为什么又改成多线程?

对于大多数公司来说,单线程的Redis达到近10w QPS已经足够用了。但现在动辄上亿的交易量,因此需要更大的QPS。

从Redis自身角度来说,因为读写网络的read/write系统调用占用了Redis执行期间大部分CPU时间,瓶颈主要在于网络的消耗,Redis支持多线程主要就是两个原因:

1.可以充分利用服务器的CPU资源
2.多线程任务可以分摊Redis同步IO读写负荷

5.Redis和Memcached有什么区别?

Redis 与 Memcached 共同点

  1. 都是基于内存的数据库,一般都用来当做缓存使用。
  2. 都有过期策略。
  3. 两者的性能都非常高。

Redis 与 Memcached 区别

  • Redis 支持的数据类型(String、Hash、List、Set、ZSet),而 Memcached 只支持最简单的 key-value 数据类型;
  • Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了;
  • Redis 原生支持集群模式,Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;
  • Redis 支持发布订阅模型Lua 脚本事务等功能,而 Memcached 不支持;

6.Redis集群脑裂导致数据丢失怎么办?

脑裂现象发生原因:

step1:在 Redis 主从架构中,部署方式一般是「一主多从」,主节点提供写操作,从节点提供读操作。 如果主节点A的网络突然发生问题,与所有的从节点都失联,但是此时的主节点和客户端的网络是正常的,这个客户端并不知道 Redis 内部已经出现了问题,还在照样的向这个失联的主节点写数据,此时这些数据被旧主节点缓存到了缓冲区里,因为主从节点之间的网络问题,这些数据都是无法同步给从节点的

step2:这时,哨兵也发现主节点失联,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了),于是哨兵就会在「从节点」中选举出一个 leeder 作为主节点,这时集群就有两个主节点了 —— 脑裂出现了

step3:然后,网络突然好了,哨兵因为之前已经选举出一个新主节点了,它就会把旧主节点降级为从节点(A),然后从节点(A)会向新主节点请求数据同步,因为第一次同步是全量同步的方式,此时的从节点(A)会清空掉自己本地的数据,然后再做全量同步。所以,之前客户端在过程 A 写入的数据就会丢失了,也就是集群产生脑裂数据丢失的问题。

解决方案

当主节点发现从节点下线或者通信超时总数量小于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。

7.Redis的过期键删除机制

惰性删除:不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。

  • 因为每次访问时,才会检查 key 是否过期,所以此策略只会使用很少的系统资源,因此,惰性删除策略对 CPU 时间最友好
  • 过期 key 一直没有被访问,它所占用的内存就不会释放,造成了一定的内存空间浪费

定期删除每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。

  • 通过限制删除操作执行的时长和频率,来减少删除操作对 CPU 的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用
  • 难以确定删除操作执行的时长和频率。如果执行的太频繁,就会对 CPU 不友好;如果执行的太少,那又和惰性删除一样了,过期 key 占用的内存不会及时得到释放。

Redis 的定期删除的流程:

  1. 从过期字典中随机抽取 20 个 key;
  2. 检查这 20 个 key 是否过期,并删除已过期的 key;
  3. 如果本轮检查的已过期 key 的数量,超过 5 个(20/4),也就是「已过期 key 的数量」占比「随机抽取 key 的数量」大于 25%,则继续重复步骤 1;如果已过期的 key 比例小于 25%,则停止继续删除过期 key,然后等待下一轮再检查。

8.Redis 内存满了,会发生什么?

在 Redis 的运行内存达到了某个阀值,就会触发内存淘汰机制,这个阀值就是我们设置的最大运行内存,此值在 Redis 的配置文件中可以找到,配置项为 maxmemory

9.Redis的大key危害

BigKey产生的场景

  • redis数据结构使用不恰当
  • 未及时清理垃圾数据;
  • 业务预估不准确
  • 明星、网红的粉丝列表、某条热点新闻的评论列表;

BigKey的危害

阻塞请求,在对BigKey进行读写时,需要耗费较长时间,容易阻塞后续请求处理。
内存增大,容易引发OOM(内存溢出),或达到redis最大内存设置引发重要key被逐出。
影响主从同步删除一个BigKey造成主库较长时间的阻塞,引发同步中断。

解决BigKey问题

对BigKey进行拆分
对BigKey进行清理,采用非阻塞的异步清零方式;
定期清理失效数据
压缩数据

10.Redis 大 Key 对持久化有什么影响?

在通过 fork() 函数创建子进程的时候,虽然不会复制父进程的物理内存,但是内核会把父进程的页表复制一份给子进程,如果页表很大,那么这个复制过程是会很耗时的,那么在执行 fork 函数的时候就会发生阻塞现象

如果创建完子进程后父进程对共享内存中的大 Key 进行了修改,那么内核就会发生写时复制会把物理内存复制一份,由于大 Key 占用的物理内存是比较大的,那么在复制物理内存这一过程中,父进程(主线程)就会发生阻塞。

11.如何使用Redis的bitMap实现签到功能?

你可能感兴趣的:(redis源码剖析,redis,数据库,缓存)