上一篇redis只是对于redis做一个比较全面的介绍,这节对redis的集群、持久化、分布式锁、事务、缓存穿透、缓存击穿、缓存雪崩、布隆过滤器做一个详细的解析。
https://blog.csdn.net/hguisu/article/details/90748695
一、基本概述
1、什么是redis?
2、场景应用场景:也可以看我写的一个redis文章:Redis的应用实战场景
3、redis的数据结构
我们存的值查找:要通过redisobj的*ptr去指针寻址找到对应的值,而redis中会对44byte大小以内的数据存储到redisobj之后(缓存行是64byte,而redisobj的描述信息用了20byte,所以44byte的数据会直接跟在redisobj的内存位置后面,这样一个缓存行即可将redisobj和存储数据一起拿到了,此时就不用进行二次寻址找数据了),这时redis的一个优化。
key在redis中始终是以String类型存在,不管你插入的是String、int、还是folat。
4、String类型
当key、value都是String类型时,它对外说是String类型,那就是吗?其实并不是的,redis底层是使用的另一套数据类型描述
以sdshdr5(SDS_TYPE_5)为例:
但这种数据类型存在一个缺点,存储长度固定了,无法动态存储数据(所有都不会使用这个类型),所以一般会转换为Type_8这种可扩容的数据结构,下面这种就可以进行动态存储
当使用Type_8的类型时,一个redisObj占用了20byte的大小。linux内存的缓存行一般是64byte。如果你是在linux中,redisobj的描述信息用了20byte,所以44byte的数据会直接跟在redisobj的内存位置后面,这样一个缓存行即可将redisobj和存储数据一起拿到了,此时就不用进行二次寻址找数据了。
5、List
quicklist即我们常见的双端链表,对于ziplist又是什么?为什么有了双向链表还要用ziplist?(单纯双向链表存在查询慢、指针等信息占用资源等弊端),而ziplist在内存中是以连续存储方式的。
这种ziplist的存储是非常紧凑的,在大量数据插入时,单纯使用ziplist也会出现问题(大量数据,需要根据空间对数据进行拆分,再进行插入不同ziplist中,查找时也要进行多次寻址查找(因为被拆为多个ZipList,没办法进行定位),这个过程是非常消耗资源的)。所以redis就使用了quickList+zipList进行整合使用来实现redis的list类型(利用quickList来进行拆分、定位到是哪个zipList,ziplist进行真实数据的存储(每个小的zipList都是连续存储的,提高效率))
参数设置:
当list-compress-depth为2时:表示前两个+后两个不压缩,其他压缩。一次类推3、4、5、6等等
6、Hash
而当数据量比较大时,则回归到字典的类型(hash)存储(无序存储)‘
7、set
对于字典类型可以看:https://blog.csdn.net/amnesia_h/article/details/80055805
例如:d={"中国":"北京","美国":"华盛顿","法国":"巴黎"} key为d,多个value存在{}中
8、zset
下面的两个设置代表:当一个ziplist内的数据超过128个则会将ziplist存储转换为字典+跳表,当ziplist总个数超过64个也会转换为字典+跳表进行存储
那什么是跳表?
跳表在底层是使用链表的形式存储的:
而如果我们在这层链表的基础上加上一层索引,每隔一个节点存储一个索引。此时再去查找先根据索引层找到大概的一个范围,然后根据这个范围再去数据层查找具体的数据,此时是不是就会发现查找效率提高了。
那如果我们再加一层索引层:
这时我再加一层上去:
这样下来,会不会发现这个类似于二分查找?但你要注意,这也不是叠越多层就越好,当层数太高时,它的效率有时反而会变低
这种类型就是跳表。
当redis中有相同key值时,set可以覆盖旧的value,setnx则不能覆盖旧的值。redis中的更新操作都是去重新set覆盖旧值。
二、核心数据结构精讲
常见的五种数据类型:
1、String
常用操作
使用场景:
2、Hash
使用场景:
优缺点:
3、List
4、Set
5、Zset
6、redis的更多功能
7、总结:
String最常用,List可以用来做排行榜等(可以实现栈、队列等功能),hash可以类比平时用的map配合符合场景进行使用,set有交并集、去重、计数等功能,Zset有set有的功能且是有序的(score值排序)。
8、其他高级命令
keys:全量遍历键,用来列出所有满足特定正则字符串规则的key,当redis数据量比较大时, 性能比较差,要避免使用
scan:渐进式遍历键
SCAN cursor [MATCH pattern] [COUNT count]
scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三个是一次遍 历的key的数量,并不是符合条件的结果数量。第一次遍历时,cursor 值为 0,然后将返回结果中第 一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。
Info:查看redis服务运行信息,分为 9 大块,每个块都有非常多的参数,这 9 个块分别是:
Server 服务器运行的环境参数
Clients 客户端相关信息
Memory 服务器运行内存统计数据
Persistence 持久化信息
Stats 通用统计数据
Replication 主从复制相关信息
CPU CPU 使用情况
Cluster 集群信息
KeySpace 键值对统计数量信息
三、redis的核心原理
1、Redis 单线程为什么还能这么快?(6.0会支持多线程,这个后面补充)
因为它所有的数据都在内存中,所有的运算都是内存级别的运算,而且单线程避免了多线程的切换性 能损耗问题。正因为 Redis 是单线程,所以要小心使用 Redis 指令,对于那些耗时的指令(比如 keys),一定要谨慎使用,一不小心就可能会导致 Redis 卡顿。
2、Redis 单线程如何处理那么多的并发客户端连接?
Redis的IO多路复用:redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到 文件事件分派器,事件分派器将事件分发给事件处理器。
(Nginx也是采用IO多路复用原理解决C10K问题)
关于什么是IO多路复用,这个会在我写的netty文章中说明。
3、Redis持久化
3.1、RDB方式
3.2、AOF方式
其中AOF存在一个重写机制:AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件(即根据某种情况AOF会根据内存的最新数据代替调AOF文件中的指令,例如:我对key1 进行原子incr了6次,本来是要存6个incr操作的(incr、incr、incr......),如果在第5次的时候发生了重写,则会将(5、一次incr)存到aof文件中),这样aof是不是就减少了存储空间,而且如果数据恢复也会变快很多了。
(为了减小aof文件的体量,可以手动发送“bgrewriteaof”指令,通过子进程生成更小体积的aof,然后替换掉旧的、大体量的aof文件。)
- 使用子进程来进行AOF重写时会遇到的问题,可以看:https://www.cnblogs.com/chichung/p/12687101.html
3.3、混合方式
混合就是类似aof的重写,用一个文件存储rdb+部分aof数据(利用aof重写),恢复时先恢复快照,再追加aof数据
aa
四、主从、哨兵及集群架构实战
redis的高可用方案主要有三种:1、主从 2、哨兵 3、集群(常用)
1、主从:
主从复制(全量复制)流程图:
主从复制(部分复制)流程图:
2、Redis哨兵高可用架构
3、集群模式
Redis集群原理分析:
Redis集群节点间的通信机制 (默认使用的是gossip)
redis cluster节点间采取gossip协议进行通信 维护集群的元数据有两种方式:集中式和gossip
集中式: 优点在于元数据的更新和读取,时效性非常好,一旦元数据出现变更立即就会更新到集中式的存储中,其他节点读取的 时候立即就可以立即感知到;不足在于所有的元数据的更新压力全部集中在一个地方,可能导致元数据的存储压力。
gossip:
五、分布式锁实现+lua脚本
aaaa
六、缓存设计与性能优化
aaaaaaaa





























































