redis

作者: 蜡笔没了小新_e8c0 | 来源:发表于2019-11-23 12:10 被阅读0次

    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

          本文链接:https://www.haomeiwen.com/subject/jyersctx.html