Redis因为是基于缓存的,它的处理能力远高于数据库。我们将程序中用到的一些热点数据存放到redis中,来减轻数据库的压力。Redis还可以用来保存访问量、实时排行榜等信息,也可以用来做session共享。
redis IO多路复用线程模型 - 简书 (jianshu.com)
单线程中如果一个网络请求进来后阻塞在某个地方时,会导致其他操作被阻塞。Redis使用Linux的IO多路复用机制实现。该机制允许内核中存在多个套接字(网络请求),内核一直监听是否由数据或请求到达。数据到达后统一进入处理队列
Redis同时监听多个socket,请求到来时将socket放入队列中排队等待处理
主要使用的方式有两种:先删缓存再改数据库->延时双删解决脏数据(仍然存在脏数据的可能)
先改数据库再删除缓存->消息队列异步删除、canel基于binlog同步
分布式锁实现方案最全解读 - 知乎 (zhihu.com)
基于setnx实现分布式锁
https://blog.csdn.net/lisheng19870305/article/details/122464924
redis持久化主要有两种方式:AOF和RDB
AOF是将操作指令一条一条记录下来,AOF是写后日志。每一操作结束后进行记录确保了指令是正确的
AOF的日志写入方式有三种:每条指令进行记录、每秒记录一次、以及不进行主动记录(操作系统触发)
AOF日志持续记录可能会导致日志文件过大的问题,redis会主动对日志进行重写例如对某一个list进行了多次操作,重写之后只保存最终态的指令
RDB是将某一刻redis当中的数据以快照的方式保存
redis在生成RDB快照时默认会开辟一个子线程执行,redis采用了写时复制的方式来避免主线程阻塞
RDB的执行策略为t秒执行一次
Redis是一个使用C语言开发的一个基于内存的键值对数据库,并且可以通过日志和快照实现持久化。
Redis中使用哈希表来保存所有键值对,其结构类似于Java中的HashMap。因为Redis中值的数据类型不同,哈希表中保存的是所有值的指针。这个表称为全局哈希表。使用哈希表来保存键值对的好处就是,不论哈希表中元素个数,每次访问的时间复杂度都是O(1)。
但是于HashMap相同,当表中元素过多时,写入数据时可能会变慢。其原因就是过多元素带来的hash冲突和rehash带来的操作阻塞。
Redis和Memcached的区别和共同点
区别
共同点
Redis中的用于存放键值对的哈希表实际上有两个,即哈希表1、哈希表2
一开始插入数据时redis只使用哈希表1,此时的哈希表2没有被分配空间。当哈希表中的元素触发扩容时。
**问题:**Redis的哈希表扩容流程大致如此。但这种方式存在一定问题—在迁移元素时元素过多会导致请求阻塞。此时Redis无法快速访问数据
**解决:**Redis采用渐进式rehash方式
Redis不会将客户端请求阻塞来进行拷贝,在第二步拷贝期间,Redis正常处理请求,每次请求结束就按顺序将哈希表1中一个桶上的元素散列到哈希表2中,下一次请求结束后继续将下一个顺序位置上的元素散列到哈希表2中。这样就可以将拷贝元素耗费的时间分摊到每次请求耗时上,避免出现一次请求被长时间阻塞的问题
(108条消息) Redis五种基本数据类型底层实现_AmyZheng_的博客-CSDN博客
https://baijiahao.baidu.com/s?id=1731524214636496899&wfr=spider&for=pc
redis中主要有5种数据结构:string hash list set zset
String
简单动态字符串,类似于Java中的ArrayList,字符串的内容是可以修改的。
字符串有多种数据类型:int、embstr、raw
能用使用数字编码时采用int
字符串小于44个字节时使用embstr
字符串大于44字节时使用raw
Hash
hash有两种数据结构:ziplist、hashtable
ziplist:当hash表的元素很少时,会直接使用压缩列表来实现,查找元素时直接通过遍历查找
hashtable:
数组加链表,类似于Java中的Hashtable,区别是redis中的hash在扩容时采用的是延迟扩容,客户端请求时将一个元素转移到新Hashtable上,这样做的好处是将扩容耗时均摊到了每次请求上。如果长时间没有请求,Redis也会执行扩容。
List
list的底层数据结构是(3.2之后改用快表->压缩列表+双向链表)
基于压缩列表+双向链表,即多个压缩列表由前后指针链接成链表。
Set
底层数据结构:intset 和 hashtable
Zset
底层数据结构:压缩列表和跳表
压缩列表和跳表
Redis的持久化主要有两大机制,AOF日志和RDB快照
AOF日志是写后日志(Redis先执行操作,再写日志)
AOF中记录的是Redis收到的每条命令,并以文本形式保存
为什么采用写后日志
写后日志可以确保记录的命令正确性,只有正确执行后的命令才会被记录下来
写后日志不会阻塞命令的执行
因为Redis在记录日志时并不会对语法正确性进行检查,所以如果是写前日志就没有办法保证语法正确性
潜在风险
三种写回策略
AOF机制提供了三种可配置的写回策略
Always:同步写回,每个写命令执行完,立马同步将日志写回磁盘
**Everysec:**每秒写回,每个写命令执行完后,将AOF日志放入内存缓冲,每隔一秒将日志存入磁盘
**No:**操作系统控制写回,每个命令执行完后,将AOF日志放入内存缓冲,由操作系统决定合适写回磁盘
AOF日志过大问题
因为AOF是以文件形式记录,随着接收的命令越来越多,AOF文件会变得越来越大,这时又会对性能产生一定影响。
解决办法:AOF重写机制,多条指令变一条指令
日志重写是Redis开新的线程来执行的,并不会影响操作
使用AOF日志的方法存在一些问题,比如每次重启时都需要将操作执行一次,如果日志非常多就会造成Redis恢复缓慢的问题。
Redis还有另外一种持久化方法:内存快照RDB
所谓内存快照就是在某一时刻将Redis内存中的数据记录下来并保存成文件。这种文件就被称为RDB文件。
RDB与AOF的区别
给哪些内存数据做快照
Redis为了保证数据的可靠性,生成快照时使用的全量快照即每次都会将当前内存中所有的数据全部放入快照中。
Redis中有两种快照生成方式
生成快照时Redis的正常操作会被阻塞吗
Redis使用基于操作系统提供的写时复制技术(Copy-On-Write,COW),实现在生成快照时也能继续写操作。其原理是:
由于bgsave子进程是由主线程fork生成的,可以共享主线程的所有内存数据,子进程在写入快照时,如果主线程对某个键值对需要修改,则将该键值对进行复制,并提供给用户修改。这样就能保证生成快照的同时能进行修改。
生成快照的时机
假设每隔t秒生成开始执行一次快照生成,t的大小不能过于小,如果太小可能会造成资源消耗过大,甚至出现上一个子进程没有执行结束下一个子进程开始执行的情况。如果间隔太大间隔时间内修改的数据就没有办法被保存。
总结
Redis使用AOF日志和RDB快照混合使用的方法。即主要RDB方式生成快照,但两次快照间隔使用AOF日志方式保存操作记录。
大量请求的key在redis以及数据库中都不存在,导致后端频繁查询数据库。
这种情况一般是黑客恶意攻击造成的。
解决办法:
布隆过滤器
布隆过滤器可以判断要请求的数据是否存在与数据库和缓存中,判断key是否合法。请求到达后布隆过滤器判断是否合法,key值不合法时直接返回。
布隆过滤器是一个bit数组,元素加入过滤器是会计算出一个hash值,然后将对应下标上的值置为1。
判断时只需要计算出要查询的key的哈希值,确认对应下标是否为1,如果为0说明数据不存在,为1时说明数据可能存在。因为计算哈希值时可能出现哈希冲突。所以布隆过滤器可能出现误判,但不影响它的正确性。
redis中缓存的一个热点数据失效后,导致大量请求打到数据库上,给数据库造成很大压力。
解决办法:
redis中缓存的数据在短时间内同时失效,造成大量请求打到数据库上。
解决办法
缓存击穿和雪崩类似,但击穿主要指的是一个值过期后大量请求打到数据库上造成的数据库压力。
雪崩是指缓存中大量的值在短时间内同时过期造成的数据库压力。
redis中有一个过期字典,一个哈希表。其中的键对应数据的键,值对应的是该键值对的过期时间
redis中过期删除策略主要有两种 惰性删除、定期删除
**惰性删除:**redis在使用这个键值对时才会判断该键值对是否过期。这种删除策略可能会出现有键值对已经过期了但可能有大量数据过期但没有被删除的情况。惰性删除对CPU比较友好
**定期删除:**redis定期的判断哪些键值对过期需要删除。定期删除对内存比较友好
redis采用两种混合的方式,但还是可能会造成内存溢出的情况,这时候就需要redis中的内存淘汰机制了。
redis的内存如果不足时会触发redis的内存淘汰机制。
redis中一共有6种淘汰机制
Redis内存满了之后会发生什么
redis的读会正常执行,但写操作会返回错误信息。
为什么说延时双删很扯淡 - idea偶买噶 - 博客园 (cnblogs.com)
保证缓存和数据数据一致有几种方法,这里只讨论主要使用的两种
存在的问题
这种策略存在一定问题,例如:A再删除缓存后,改数据库之前,B同时也进来读取数据,B会发现缓存中没有数据于是去查数据库,由于此时A并没有修改数据库中的数据所以B会读取到旧的数据并且重新设置到缓存中,此时缓存中的数据即位脏数据。
如何解决
所以可以在A修改完数据库后延时等待一定时间再次删除缓存,防止修改数据库期间有其他线程将脏数据写到缓存中。但是等待的时间根据具体的业务进行修改所以仍然可能会出现脏数据。
使用场景
这种方式适合使用在能容忍脏数据的业务场景,并且最好是对缓存数据设置了过期值的场景
下面是延时双删的流程图
延时双删就是在数据更新后,等待一段时间等其他拿到旧数据的事务更新到数据库中后再删除缓存。
但整体来说延时双删在第二次等待期间,其他线程拿到的都是旧数据。(解决办法:等待期间不让过多的线程请求进来,并且尽量缩短第一次删除缓存和更新数据库的时间,就可以减少获取到旧数据的线程数量)
存在问题
请求A查询获取到旧值,请求B修改数据库并删除缓存,请求A将旧值同步到缓存中导致出现脏数据
如何解决
可以考虑使用消息队列异步删除,或者binlog同步删除(canel这种基于binlog日志的同步中间件)
Redis在主从复制模式下,master结点如果出现故障就需要人工来选举新的master点。Redis通过哨兵模式来实现结点动态选举新的master结点。
哨兵进程监控集群中master结点是否正常工作
当哨兵进程检测到master结点故障时,会自动将slave结点切换为master结点,并且通知其他slave结点修改自己的配置文件来切换master结点
Redis中的哨兵不止一个,为了防止单个哨兵出现故障而无法进行动态选举。一般采用多哨兵模式。
当一个哨兵检测到master不可用时,这一个哨兵会主观认为它不可用(称为主观下线)只有当多个哨兵都认为master不可用时才会开始进行新结点的选举。这时原来的master结点被称为(客观下线)
哨兵进程工作流程