redis

1.为什么说redis快?

  • 完全基于内存
  • 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,也不存在加锁和释放锁的操作。
  • 使用多路I/O复用模型,非阻塞IO。
  • 使用底层模型不同。Redis自己有VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

2.多路I/O复用模型

“多路”指的是多个网络连接,“复用”指的是复用同一个线程。
利用select、poll、epoll可以同时监察多个流的I/O事件的能力,在空闲的时候,会把当前阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态唤醒,于是程序就会轮询一遍所有的流(epoll只轮询那些真正发出了事件的流),并且只依次顺序处理就绪的流。

2.1 select

select(int nfds, fd_set *r, fd_set *w,fd_set *e, struct timeval *timeout)

  • nfds:r、w、e集合中标识符最大值加一。
  • r:读集合
  • w:写集合
  • e:异常集合
  • timeout:表示等待时间。如果设置为null,表示如果没有事件时,可以一直处于阻塞状态;否则,会根据指定的时间进行阻塞。

问题:

  • 大小限制(FD_SETSIZE,1024)
  • 在每次返回时都会被内核修改,需要重新设置
  • 在读写事件比较少的时候,依然需要对所有的集合进行扫描

2.2 poll

poll(struct pollfd *fds, int nfds, inttimeout)

 

struct pollfd {

    int fd;

    short events;

    short revents;

}

3.redis实现乐观锁?

  • watch
  • multi
  • exec

4.哨兵机制

  • 每个哨兵进程以每秒钟依次的频率向整个集群的Master主服务器,Slave从服务器以及其他Sentinl进程发送依次PING命令。
  • 如果一个实例距离最后一次有效回复PING命令的时间超过down-after-milliseconds选项设定的值,则这个实例会被哨兵进程标记为主观下线(SDOWN)。
  • 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有哨兵

5.缓存穿透和缓存雪崩

  • 缓存穿透:指查询大量不在缓存中的数据,会直接请求数据库。

解决方法:
1.采用布隆过滤器,拦截肯定不存在的数据
2.为不存在的数据设置空值

  • 缓存雪崩:大量数据缓存同时过期。

解决方法:
1.加锁排队
2.在原来过期时间添加一个随机时间

6.sorted set 底层实现?

  • 当数据较少时,sorted set是由一个ziplist来实现的。
  • 当数据较多的时候,sorted set是由一个dict + 一个skiplist来实现的。dict用来查询数据到分数的对应关系,而skiplist用来根据分数查询数据。

** skiplist和平衡树、哈希表的比较**

  • skiplist和平衡树的元素是有序排列的,而哈希表不是。
  • skiplist的范围查询要优于平衡树。平衡树需要先找到范围的最小值,然后通过中序遍历的方式继续寻找;而skiplist在找到最小值后,只需要对最低层的链表进行遍历。
  • 平衡树的插入和删除操作可能会引起子树的调整,而skiplist只需要修改相邻节点的指针,相对简单。
  • 查找key的时候,skiplist和平衡树的时间复杂度都是O(logn),而哈希表是O(1)。

7.redis持久化

RDB和AOF

由于Redis是内存数据库,它将自己的数据库状态存储在内存中,如果不想办法将存储在内存中的数据库状态保存到磁盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。

RDB

  • SAVE:会阻塞Redis服务器进程,直到RDB文件创建完成为止,在服务器进程阻塞期间,服务器不能处理任何命令请求。
  • BGSAVE:会派生出一个子进程,然后由子进程负责创建RDB文件,父进程进行处理命令请求。

一旦BGSAVE开始执行了,SAVE命令和BGSAVE命令都会被服务器拒绝,避免产生竞争条件。如果BGSAVE命令正在执行,那么客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行;如果BGREWRITEAOF命令正在执行,那么客户端发送的BGSAVE命令会被服务器拒绝。避免产生大量的磁盘写入操作。

将某段时间内的所有数据持久化到磁盘中,类似快照的行为。当在进行持久化的过程时有数据的更新,会把这些记录保存到备份文件中,最后会把备份文件拿来替换原文件。

缺点:如果机器发生故障,容易丢失某个时间段内的数据。

AOF

将所有写命令保存到AOF缓冲区中,根据appendfsync的值(默认为everysec)来对AOF文件同步,由于大量的写入命令会导致AOF文件过大,后台就会开启一个子进程对原AOF文件进行重写(合并命令),对文件进行压缩。如果在重写期间执行了写入命令,会将写入命令保存到AOF重写缓冲区中,等到AOF重写结束,再将AOF重写缓冲区中的内容追加到新AOF文件中,此时会对父进程阻塞,最后用新AOF文件替换原AOF文件。

  • 每秒fsync一次(默认)
  • 每次有新命令追加到AOF文件是就执行一次fsync
  • 从不

缺点:由于恢复要进行的操作较多,可能会导致主线程阻塞。

8. redis zset底层为啥用skiplist而不用跳表?

跳表:采用多层链表结构,查询、删除、插入时间复杂度都是O(logn)。

在操作时间复杂度差不多的情况下,skiplist的实现简单。

9. redis 查询某一前缀的key

方法一:利用keys查找(不推荐)

keys pattern:查找所有符合给定模式pattern的key

通过这种方式查询可能会导致查询时间过长,导致redis其他服务卡顿。

方法二:利用scan查找(推荐)

scan cursor [MATCH pattern] [COUNT count]

  • 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程;
  • 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历;
  • 不保证每次执行都返回某个给定数量的元素,支持模糊查询;
  • 一次返回的数量不可控,只能是大概率符合count参数。

keys命令的时间复杂度为O(n),而scan命令会将遍历操作分解成m次时间复杂度为O(1)的操作来执行,从而解决使用keys命令遍历大量数据而导致服务器阻塞的情况。

10. redis过期删除策略

  • 定时删除:在设置键的过期时间的时候创建一个定时器,当过期时间到的时候立马执行删除操作。对CPU不是很友好。
  • 惰性删除:惰性删除不会在键过期的时候立马删除,而是当外部命令获取这个键的时候才会主动删除。过程为:接收get执行、判断是否过期、执行删除操作、返回nil。
  • 定期删除:每隔一段时间检测是否有过期键,如果有执行删除操作。

11. 一致性hash算法

hash算法依赖于现有的机器数目,导致所有的位置都要发生变动。一致性hash算法是采用与2^32取模,形成一个虚拟的圆环,当机器数目发生变动的时候,只有少量的位置变动。由于可能存在数据倾斜问题,引入虚拟节点来使数据分布均匀,只是多了虚拟节点到实际节点的映射。

12. 使用场景?

  • 缓存
  • 计数(String)
  • 消息队列(list)
  • 求共同部分(set)
  • 估算访问量(hyperloglog)
  • 过滤请求(bitmap)
  • 排行榜(sorted set)
  • session服务器

你可能感兴趣的:(redis)