目录

一、NoSQL和SQL区别

二、redis特点

三、redis的数据结构

四、redis的通用命令

五、redis的数据类型

5.1 String类型

5.2 Hash类型

5.3 List类型 

 5.4 Set类型

5.5 SortedSet类型 

六、redis客户端--SpringDataRedis

七、缓存常见问题

 7.1 缓存更新

7.2 缓存穿透、缓存击穿和缓存雪崩 

八、redis的持久化

8.1 redis的RDB持久化

8.2 AOP持久化

 8.3 RDB和AOP区别

九、redis主从架构

十、哨兵模式 

十一、redis分片集群 

十二、redis底层数据结构

12.1 动态字符串SDS

12.2 IntSet

12.3 Dict 

12.4  ZipList

12.5  QuickList

12.6  SkipList

12.7  RedisObject结构

十三、网络模型 

13.1 用户空间和内核空间

 13.2 IO模型

 13.3 redis网络模型

13.4 redis的RESP协议

十四、Redis内存策略

十五 、淘汰策略

一、NoSQL和SQL区别

(1)SQL对数据有着强烈的结构化定义,而NoSQL对数据约束没有那么强烈。

(2)SQL数据之间有关联,而NoSQL没有,它通过json嵌套来实现数据的关联。

(3)SQL 查询数据的语法是固定的,相对应的NoSQL没有固定语法。

(4)SQL满足事务的ACID特性,而NoSQL不满足。

(5)SQL基于磁盘存储,NoSQL基于内存存储。

(6)SQL是垂直性扩展,NoSQL是水平扩展的。

(7)SQL适合数据结构固定,相关业务对数据安全性、一致性要求较高,NoSQL数据结构不固定,对一致性、安全性要求不高,对性能有要求。

二、redis特点

(1)键值(key-value)型,value支持多种不同数据结构,功能丰富。

(2)单线程,每个命令具备原子性

(3)低延迟,速度快(基于内存、IO多路复用和良好的编码)

(4)支持数据持久化

(5)支持主从集群、分片集群

(6)支持多语言客户端

三、redis的数据结构

redis是一个key-value的数据库,key一般是String类型,不过value的类型是多种多样的。

四、redis的通用命令

(1)KEYS:查看符合模板的所有key,可以使用*通配符,不建议在生产环境使用

(2)DEL:删除一个指定的key,也可以删多个,返回删除的数量

(3)EXISTS:判断一个KEY是否存在

(4)EXPIRE:给key设置有效时间,时间到期该key被自动删除

(5)TTL:查看一个KEY的剩余有效期

五、redis的数据类型

5.1 String类型

       String类型,其value是字符串,不过根据字符串的格式不同分为三类:string(普通字符串串)、int(整数类型)和float(浮动类型),后两种可以做自增和自减操作。底层都是采用字节数组存储的,只不过编码形式不同,数据类型的字符串将转换成二进制类型存储。字符串类型的上限不能超过512m

string常见命令

set:添加或修改一个字符串 

get:根据key获取String类型的value

mset:操作多个

mset:查询多个

incr:让key的value值自增1

incrby:让key的value值自增相应的步长

incrbyfloat:浮点数自增

setnx:添加key-value,前提key不存在

setex:添加key-value,设置key的过期时间

String的编码

(1)当存储字符串时,存储的SDS长度小于44字节,会采用EMBSTR编码,此时object head与SDS是一段连续空间。申请内存时只需要调用一次内存分配函数,效率更高。当存储的SDS长度超过44字节,会采用RAW编码。存储上限为521mb。

(2)当存储的字符串是整数,并且大小在LONG_MAX范围内,则会采用INT编码;直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS了

5.2 Hash类型

hash的value值是无序字典,类似于java中的hashMap结构

常见命令:

增加:HSET key field value   批量添加 HMSET

查询:HGET key field  批量查询 HMGET

查询所有 HGETALL  查询所有field HKEYS 查询所有的value HVALS

字段自增 HINCRBY 

HSETNX:判断hash的field是否存在,不存在则执行

hash底层数据存储结构

5.3 List类型 

list类型与java的LinkedList类似,是一个双向链表,支持正反检索。特点是有序、可重复、插入和删除快,查询慢

常见命令:

插入元素:LPUSH  RPUSH

获取并移除元素:LPOP RPOP   阻塞获取BLPOP BRPOP 

范围获取 LRANGE

List底层数据结构

 5.4 Set类型

redis的Set结构与java的hashSet类似,可以看做一个value为null的hashmap、因为也是一个hash表,和hashset类似 。特征有无序、元素不重复、查找快、支持交集、并集和差集等。

常见命令:

增加元素 SADD

移除元素 SREM

查询元素总数 SCARD   SMEMBERS 查询所有元素

SISMEMBER 判断一个元素是否存在于set中

SINTER 求key1和key2的交集

SDIFF 求key1和key2的差集

SUNION 求key1和key2的并集

set的底层数据结构

set是redis中的集合,set采用HT编码(Dict)。Dict中的key用来存储元素,value统一为null。当存储的所有数据都是整数时,并且元素数量不超过set-max-intset-entries,set会采用intSet编码,以节省内存。

5.5 SortedSet类型 

 SortedSet是一个可排序的集合,根据每个元素的score对元素排序,底层是一个跳表+hash表 sortedSet具有可排序、不重复和查询速度快

常见命令:

新增 zadd 删除 zrem 

zrank key member 获得排序,默认是升序  ZREVRANK 降序

ZCARD 获得set的总数

sortedset底层数据结构

zset底层数据结构必须满足键值存储、键必须唯一和可排序,因此底层采取的存储方式是SkipList和HT

六、redis客户端--SpringDataRedis

(1)需求引入spring-boot-starter-data-redis依赖和commons-pools依赖

(2)spring-boot-starter-data-redis默认引入的lettuce的客户端,如果使用jedis需要单独引入

(3)注入RedisTemplate

(4)SpringDataRedis默认使用jdk的序列化工具,使用时需要重新指定,对于key使用string序列化,value使用json序列化,这样会使得value多存储一个class的键值对,占据更多内存。

(5)Spring提供了一个StringRedisTemplate类,它的key和value的序列化方式默认是spring,这样需要手动序列化和反序列

七、缓存常见问题

 7.1 缓存更新

缓存更新有三种策略,分别是内存淘汰策略、超时剔除和主动更新

对于低一致性需求:使用内存淘汰策略,

对于高一致性需求:主动更新,并以超时剔除作为兜底方案

读操作: 缓存命中则直接返回,未命中则查询数据库,并写入缓存,设定超时时间

写操作:先写数据库,然后再删除缓存;要保证数据库和缓存操作的原子性

7.2 缓存穿透、缓存击穿和缓存雪崩 

 缓存穿透:用户查询的数据在缓存和数据库都不存在。解决方案使用缓存空对象加设置key的过期时间和布隆过滤器

缓存雪崩:同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来严重压力。解决方案包括设置key不同的TTL时间,搭建redis集群、降级限流和添加多级缓存。

缓存击穿:也叫热点key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。采用互斥锁和逻辑过期解决

八、redis的持久化

8.1 redis的RDB持久化

RDB(Redis Database Backup file)是redis数据快照,即将内存中的所有数据记录到磁盘用于故障恢复。使用命令save和bgsave,save会阻塞所有命令,bgsave使用子进程保存数据,效率高

RDB触发机制

(1)redis停机之前会先写RDB文件,之后停机

(2)在redis.conf配置 ,如下

 RDB持久化原理

bgsave开始会fork主进程到子进程,子进程共享主进程的内存数据,子进程写数据到新的RDB文件会替换旧的文件。fork子进程仅仅是复制主进程的页表,所以阻塞会非常小。同时,当主进程执行写操作时,会拷贝一份数据,执行写操作。

 RDB的缺点

(1)RDB执行间隔时间长,会存在丢失数据的风险

(2)fork子进程、压缩、写出RDB文件比较耗时

8.2 AOP持久化

AOP(Append Only File)追加文件,redis会将操作的每一个命令追加到文件里,可以看做一个命令的日志文件

AOP的开启

 AOP重写

为了减少aop文件的大小,使用bgrewriteaof命令重写aof文件,这个命令是开启一个独立线程执行的

触发机制

 8.3 RDB和AOP区别

九、redis主从架构

redis主从架构,实现读写分离

 开启主从模式

(1)永久开启:在redis.conf配置文件添加一行slaveof

(2)临时开启:redis-cli客户端连接redis服务器,在从节点执行slaveof命令,重启后失效

注意在redis5.0以后新增命令replicaof

主从同步原理

全量同步

(1)slave节点请求增量同步

(2)master节点判断replid,发现不一致,拒绝增量同步

(3)master将完整内存数据生成RDB,发送RDB到slave

(4)slave清空本地数据,加载master的RDB

(5)master将RDB期间的命令记录在repl_baklog,并持续将log命令发送到slave

(6)slave执行接收到的命令,保持与master之间的同步

增量同步

slave重启后采用增量同步,repl_baklog大小有上限,写满后覆盖最早的数据。如果slave断开太久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步。

优化redis主从同步

(1)在master配置repl-diskless-sync yes 启用无磁盘复制,避免全量复制

(2)控制redis单节点内存占用量,不要太大

(3)适当提高repl_baklog的大小,尽快恢复宕机的从节点

(4)限制一个master的slave节点数量,可以采用主-从-从链式结构,减少master的压力

十、哨兵模式 

redis的哨兵主要为了实现主从集群的自动故障恢复

哨兵的作用

(1)监控:哨兵会不断检查集群节点的健康状态

(2)自动故障恢复:若集群master节点宕机,哨兵会选择一个slave节点提升为master节点

(3)通知:当集群发生故障转移,哨兵会将最新信息推送给redis的客户端

哨兵监控

哨兵基于心跳机制每隔一秒向集群的每个实例发送ping命令,若某个哨兵在规定时间未收到响应,认为该实例主观下线;若超过指定数量(quorum)的哨兵节点都认为该实例主观下线,则该实例客观下线

选举规则

(1)首先判断slave节点与master节点断开时间长短,超过指定值(down-after-milliseconds  * 10)会排除slave节点

(2)判断slave的slave-priority值,越小优先级越高,如果是0则永不参与选举

(3)如果slave-priority越大,则判断slave节点的offset值,越大说明数据越新,优先级越高

(4)最后判断slave节点的运行id大小,越小优先级越高

故障转移

(1)哨兵给有资格的slave节点发送一个slaveof no one命令

(2)给其他节点广播发送salveof ip post,让其他从节点开始从新的master同步数据

(3)哨兵会将故障节点标记为slave节点,故障恢复后成为新master的从节点

哨兵集群搭建

(1)修改sentinel.conf文件

 (2)使用命令 redis-sentinel ./sentinel.con启动redis实例

十一、redis分片集群 

分片集群

(1)集群有多个master节点,每个master保存不同数据

(2)每个master都可以有多个slave节点

(3)master之间通过ping监测彼此健康状态

散列插槽

redis会把每一个master节点映射到0~16384个插槽上

查询时,数据key不是和节点绑定,而是和插槽绑定,redis会根据key的有序部分(key包含{}里的字符,没有{}当前key都是有效部分),利用CRC16算法得到一个hash值,然后对16384取余,得到结果为slot值。

注:将同一类数据固定保存到redis同一个实例上,可以在key添加相同的有效部分

集群伸缩

(1)创建集群:redis-cli --cluster create   --replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003.

(2)新增节点:redis-cli --cluster add-node new_host:new_port existing_host:existing_port  --cluster-slave --cluster-master-id

(3)redis-cli --cluster reshard ip:port 分配哈希槽

故障转移

(1)自动故障转移,类似于哨兵

(2)手动故障转移,通过执行cluster failover命令让集群某个master宕机,在slave节点执行该命令可以该节点升级为master节点

十二、redis底层数据结构

12.1 动态字符串SDS

      redis没有采用C语言的字符串,而是采用一种新的字符串结构,简称简单动态字符串(Simple Dynamic String),简称SDS。SDS本质是C语言的结构体。

      SDS支持动态扩容,有两种情况:(1)当新字符串小于1M,则新空间为扩展后字符串长度的两倍+1;(2)如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1。

12.2 IntSet

 IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。结构如下:

 为了查找方便(采用二分查找),Redis会将intset中所有的整数按照升序依次保存在contents数组中。

若当前编码无法满足当前数据存储,会动态升级编码,并倒序依次将数组中的元素拷贝到扩容后的正确位置,然后将待添加的元素放入数组末尾。

12.3 Dict 

redis的键值类型由Dict实现,Dict由三部分实现,分别是哈希表、哈希节点和字典

 当向Dict添加键值对时,redis首先根据key计算出hash值,然后利用h&sizemask来计算数组的索引下标。

Dict的扩容

负载因子LoadFactor=used/size

(1)当哈希表的LoadFactor >= 1,并且服务器没有执行BGSAVE或者BGREWRITEAOF等后台进程。

(2)当哈希表的LoadFactor > 5;

Dict的收缩

当删除元素时,也会对负载因子做检查,当LoadFactor < 0.1且数组长度大于4时,会做哈希表收缩。

Dict的移动元素

dict的rehash并不是一次性完成的,而是分多次、渐进式的完成,每次在执行增删改查都会重写哈希一个元素,直到所有元素迁移完成。

12.4  ZipList

ZipList是一种特殊的“双端链表”,由一系列特殊编码的连续内存组成。可以在任意一端进行压入/弹出操作,并且该操作的时间复杂度为O(1)。ZipList结构如下:

 ZipList的Entry结构如下

注意:ZipList中所有存储长度的数值均采用小端字节序,即低位字节在前,高位字节在后。例如数值0x1234,采用小端字节序后实际存储值为:0x3412

ZipList的Entry中的encoding编码分为字符串和整数

(1)当encoding是以“00”,“01”,或者“10”开头,对应的存储字节数分别为一字节、二字节和五字节,以这些标志开头代表是字符串。

(2)当encoding是以“11"开头,则是整数,且encoding固定只占用1个字节。

ZipList的连锁更新问题

当有N个,长度为250~253字节之间的entry时,此时entry的previous_entry_length属性采用一个字节存储,当插入一个比较大(超过254)的数据时,导致一个字节无法记录前一个entry长度,会导致多次的连续空间扩展,效率极低,发生概率极低。

12.5  QuickList

QuickList是一个双端链表,只不过链表中的每一个节点都是一个ZipList,这样做可以解决内存申请效率低的问题

QuickList会限制ZipList的大小

 QuickList压缩节点

12.6  SkipList

skiplist(跳表)是一个链表,其特点如下:

(1)元素按照升序排列存储

(2)节点可能包含多个指针,而且指针跨度不一样

 源代码如下:

12.7  RedisObject结构

 Redis中的任意数据类型的键和值都会被封装为一个RedisObject,也叫做Redis对象。

 

十三、网络模型 

13.1 用户空间和内核空间

为了避免用户应用崩溃导致内核崩溃,用户应用与内核是分离。

(1)进程的寻址空间分为内核空间和用户空间

(2)用户空间只能执行受限的命令,而且不能直接调用系统资源,必须通过内核提供的接口来访问

(3)内核空间可以执行特权命令,调用一切系统资源

 13.2 IO模型

(1)阻塞IO

(2)非阻塞IO

 (3)IO多路复用

 13.3 redis网络模型

redis通过IO多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库。

13.4 redis的RESP协议

redis的RESP协议规则如下:

(1)单行字符串:首字节是‘+’,后面跟上单行字符串,以CRLF("\r\n")结尾,例如返回“OK” “+OK\r\n”

(2)错误:首字节是‘-’,与单行字符串格式一样,只是字符串是异常信息,例如:“-Error message\r\n”

(3)数值:首字节是‘:’,后面跟上数字格式的字符串,以CRLF结尾。例如:“:10\r\n”

(4)多行字符串:首字节是‘$’,表示二进制安全的字符串,最大支持512MB;如果大小为0,则表示是空字符串:“$0\r\n\r\n”,如果大小为-1,则代表不存在:“$-1\r\n”

(5)数组:首字节是‘*’,后面跟上数组元素个数,在跟上元素,元素数据类型不限。

十四、Redis内存策略

内存过期策略

(1)惰性删除:过期key不会立即被删除,当访问时会判断当前key是否过期,过期后就删除。

(2)周期删除,通过一个定时任务,周期性的抽样部分过期的key,然后执行删除。

          SLOW模式:执行频率默认是10hz,每次执行不超过25ms    

          FAST模式:执行频率不固定,每次间隔不低于2ms,每次耗时不超过1ms

十五 、淘汰策略

内存淘汰:当redis内存使用达到设置的阈值时,redis主动挑选部分key删除以释放更多内存的流程

注意:LRU(Least Recently Used),最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。

           LFU(Least Frequently Used),最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。

文章来源

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。