- 前言
- 一、Redis的五种基础数据结构及其底层实现
- 二、Redis的扩容机制
- 三、Redis是单线程还是多线程
- 四、Redis为啥这么快
- 五、跳表的实现原理
- 六、Redis主从复制
- 七、缓存击穿、缓存穿透、缓存雪崩
- 八、Redis里的数据落地(持久化)机制
- 九、Redis分布式锁
- 十、Redis过期删除策略
- 十一、Redis分布式锁的宕机失效
- 总结
前言
注:我的笔记风格,可能不会特别官方,不会晦涩难懂,而是以一个初学者能看懂的方式把知识呈现出来,用最简单的语言把抽象的概念表达出来~
Redis是最常用的NoSQL数据库,也是后端开发最常用的技术之一,缓存、分布式锁等场景都会用到Redis。本文用通俗易懂的语言把Redis在面试中和工作中常用到的知识做一个小结。
一起开始吧【长文警告!!!】~
一、Redis的五种基础数据结构及其底层实现
这里补充一点:Redis里面所有的操作都是原子操作哦~
Redis有五种基础的数据结构:
(1)string
动态字符串;
底层:动态字符串(类似cpp里面的vector),有capacity(大于size),如果扩容,扩容两倍。
(2)list
双向链表;
底层:使用quicklist,数据少的时候用连续的内存(ziplist,压缩链表),如果数据多了,就用多个ziplist连接到一起,形成quicklist;
(3)set
底层:set(哈希set)
(4)zset
有序集合,用跳表实现
底层:数据少用ziplist,value和scores挨着存储,数据多用hash(map
(5)hash
底层,当数据少的时候,用ziplist,数据多,用hashtable
二、Redis的扩容机制Redis的扩容机制:
- 首先redis内部,都是k-v(key-value结构),用一个类似哈希的结构存储的,所谓扩容,就是指哈希的扩容;
- 哈希就是,输入一个k-v,根据key和散列函数,得到k的索引,然后插入到哈希表中;
- 如果两个k对应一个索引,就会产生冲突,哈希冲突,解决哈希冲突的方法有很多,比如映射到的索引值+1,比如用链表,而redis底层就是用链表来实现的,由于没有指向链表尾部的指针,所以每次插入都是插在链表头部;
- 会维护一个负载因子,负载因子等于当前存的节点数÷容量;
- 扩容的时候就是rehash,重新散列的过程;
- 只有负载因子大于1或者5的时候,需要扩容,或者小于0.1的时候,需要收缩,扩容和收缩都是rehash。
rehash的过程:
- 为新的哈希表申请内存,申请的大小为之前的几倍或者几分之一;
- 把原来的数据,重新做哈希映射,重新计算散列函数的值和索引值,插入到新表中;
- 插入完成后,释放原来的哈希表,把新的改为原来的哈希表的名字。
(1)Redis主体是单线程+io多路复用的哒,io多路复用就是为了提高并发,一个线程多个tcp连接;
(2)执行指令是单线程的,可以避免多线程的开销(创建、释放、上下文切换、锁机制等),但是对于一些模块,采用多线程。
四、Redis为啥这么快(1)Redis都是存储在内存中的,没有磁盘io带来的开销;
(2)Redis对命令的执行和解析是单线程的,没有多线程的开销(线程的创建和销毁,线程上下文切换);
(3)redis使用io多路复用技术,提高并发效率;
(4)redis不需要解析sql语句,基于哈希存储,哈希的效率很高。
五、跳表的实现原理为啥要用跳表?
对于一个单链表,要想去查找一个节点,需要O(n),因此可以类比二分查找的思想,提出跳表的概念,Redis里面的zset也是用跳表+哈希map实现的,lc1206就是实现跳表的题目~
1)原理?
先把单链表排序,从小到大,作为最底层;
然后每隔两个节点,设置一个索引节点(保存值,next节点的指针,down下一层节点的指针),这是第二层;
以此类推,可以设置多层,这样就形成了一个跳表~
2)查询、删除、插入?
查询:先从最上层查,对比,如果目标在两个节点之间,那么就寻找左边节点的down,下一层,以此类推,时间复杂度O(logn);
删除:要找到前一个节点才能删除,寻找节点O(logn),删除节点O(1);
插入:同删除,先查询再删除。
六、Redis主从复制1.目的:
为了实现读写分离,可以支持高并发的读数据,分担读数据压力;
高可用性,主数据库宕机了,从可以变成主。
2.原理:
一个master,多个从,从初始化的时候,全量复制master的数据(此时可以从从数据库读数据),后面,master写数据的时候,从随之跟着修改,实现增量复制。
3.缺点:
有延迟,主和从复制有延迟。
七、缓存击穿、缓存穿透、缓存雪崩这三个都是Redis用作缓存的时候经常出现的问题~
模型:请求--缓存--数据库
(1)缓存击穿
问题:如果同一时刻有大量的key过期,那么访问这些key的请求,缓存不会命中,就直接打到数据库上,造成击穿。
解决:活跃key设置永不过期,常用的延长过期时间;同一批key设置不同的过期时间。
(2)缓存雪崩
问题:缓存宕机,或者大量key失效,请求会直接访问数据库,造成系统崩溃。
解决:设置redis集群作缓存;用队列来控制请求流量。
(3)缓存穿透
问题:请求在缓存中没有,因此访问数据库,数据库中也没有,因此返回null,然后缓存中不存储null,有大量该请求。
解决:缓存中存储该对象,并设置过期时间;布隆过滤器。
八、Redis里的数据落地(持久化)机制Redis是存在内存中的,有两种持久化机制,所谓持久化,就是从内存拷贝到磁盘中!
(1)RDB
1)定时将数据库数据进行备份,dump到dump.rdb文件中,每次启动的时候从rdb文件恢复;
2)在4379.conf中可以修改,比如多少个key变动,进行备份。
优点:适合大规模数据的备份,效率高。
缺点:可用性不行,如果在备份前宕机,那么会丢失数据。
(2)AOF
1)将数据库的写和删除操作,以日志的形式追加到文件中,不记录读操作。
2)有三种备份方式:每秒,每写(always),永不。
优点:可用性高,宕机也能恢复之前的。
缺点:效率低。
九、Redis分布式锁(1)实现方式
setnx:只能对没创建的数据加锁,可以配置过期时间。
redlock:设置五个独立的redis主机,记录一个时间戳,然后分别对这五个加锁,如果超过三个加锁成功,那就记录下一个时间戳,加锁成功,否则失败。
(2)分布式锁的问题
1.取到锁的客户端长期阻塞,导致锁过期,其他客户端也可以取到锁。
十、Redis过期删除策略(1)设置过期时间
expire
(2)过期删除策略
1.立刻删除:
设置过期时间的时候,设置一个删除器,过期时间一到,立刻删除
cpu压力大,内存压力小
2.惰性删除:
不删除,直到读写该数据的时候,判断是否过期,过期则删除
cpu压力小,内存压力大
3.定期删除:
定期扫描数据,删除过期的数据
(3)redis使用的过期策略
惰性删除+定期删除(随机)
十一、Redis分布式锁的宕机失效宕机失效也是传统的使用setnx实现分布式锁的一个缺点,也就是,假如一个1主3从的结构,主节点有个锁,从节点变成主节点的时候,主节点宕机了,此时这个锁没有及时同步过来,就发生了锁失效的问题。
此时可以用redis提供的redlock机制来解决这个问题。
redlock大致的原理:
1)获取时间戳
2)同时创建独立的五个master节点
3)如果要获取锁,分别对五个节点同时获取锁,如果在一定时间内获取到了3把或者更多,就可以获取锁
4)如果没有获取到一定数目的锁,就失败
总结本文介绍了Redis的常见知识点和面试题,希望可以给大家提供帮助!
本系列为基础知识分享,日更,有任何问题可以私聊或评论博主哦!
希望给各位找工作和工作中的小伙伴提供一点微博的帮助,如果觉得有帮助,欢迎点赞评论收藏关注~



