Redis知识点总结

redis脑图

1、项目中的应用场景

1.1、使用场景

2、在应用中使用的是缓存,保存cookie、session等
3、服务的无状态,延申思考:根据项目中的数据结构,单机锁和分布式锁,可以抽出来放入redis。

1.2、存储数据结构

1、redis的五大value类型:String、list(列表)、hash(哈希)、set(集合)、zset(有序集合)。
a、string --》int、raw(长度<39个字符、embstr(长度>39个字符);
b、list --》ziplist(512个内长度小于64字节)、双向链表;
c、hash --》 ziplist(512个内长度小于64字节)、哈希表;
d、set --》 inset(长度是512个内的整数)、哈希表;
e、zset --》 ziplist(128个内长度小于64字节)、跳表。

2、redis是单线程还是多线程

1、redis主要工作线程是单线程
2、6.x高版本出现了IO多线程
3、理解面向IO模型的编程的时候,会涉及到内核模式,线程负责网络IO、计算、过期问题。内核中有epoll(多路复用),使用多线程进行读写,工作线程进行计算,计算的时候还是串行执行,满足redis的串行原子性。多路复用后,可以节约时间、更大效率的使用到了资源。

注意: 事务执行的时候是原子性的,执行失败不会回滚,不会影响其他的事物。

3、redis存在线程安全的问题吗

	不存在,因为在redis服务内部是单线程的。
	业务层面的话,尽量保障业务的顺序执行。

4、redis的缓存雪崩、缓存穿透、缓存击穿

请求 --》 缓存 --》数据库

4.1、缓存穿透

关键:在缓存层和数据库层都没有找到符合条件的数据。

也就是说,在缓存层和数据库层都没有命中数据,那么,这种情况就叫作缓存穿透。

造成缓存穿透的主要原因就是:查询某个 Key 对应的数据,Redis 缓存中没有相应的数据,则直接到数据库中查询。数据库中也不存在要查询的数据,则数据库会返回空,而 Redis 也不会缓存这个空结果。这就造成每次通过这样的 Key 去查询数据都会直接到数据库中查询,Redis 不会缓存空结果。这就造成了缓存穿透的问题。

如何解决缓存穿透问题?

既然我们知道了造成缓存穿透的主要原因就是缓存中不存在相应的数据,直接到数据库查询,数据库返回空结果,缓存中不存储空结果。

第一种解决方案:就是把空对象缓存起来。当第一次从数据库中查询出来的结果为空时,我们就将这个空对象加载到缓存,并设置合理的过期时间(尽量短一点,避免正常后也不可用),这样,就能够在一定程度上保障后端数据库的安全。

第二种解决方案:就是使用布隆过滤器,布隆过滤器可以针对大数据量的、有规律的键值进行处理。一条记录是不是存在,本质上是一个 Bool 值,只需要使用 1bit 就可以存储。我们可以使用布隆过滤器将这种表示是、否等操作,压缩到一个数据结构中。比如,我们最熟悉的用户性别这种数据,就非常适合使用布隆过滤器来处理。

第三种解决方案:在业务层、请求接口说进行过过滤拦截,拦截不合理的请求参数。

4.2、缓存击穿

关键:缓存没有值,数据库有值,热key同时失效,大量并发。同时读缓存没读到数据,又同时去数据库去取数据,造成的压力。

如果我们为缓存中的大部分数据设置了相同的过期时间,则到了某一时刻,缓存中的数据就会批量过期,又导致大部分用户的请求在这一时刻都会直接落在数据库上,这种现象就叫作缓存击穿。

如何解决缓存击穿问题?

对于比较热点的数据,我们可以在缓存中设置这些数据永不过期;也可以在访问数据的时候,在缓存中更新这些数据的过期时间;如果是批量入库的缓存项,我们可以为这些缓存项分配比较合理的过期时间,避免同一时刻失效。

还有一种解决方案就是:使用分布式锁,保证对于每个 Key 同时只有一个线程去查询后端的服务,某个线程在查询后端服务的同时,其他线程没有获得分布式锁的权限,需要进行等待。不过在高并发场景下,这种解决方案对于分布式锁的访问压力比较大。

4.2、缓存雪崩

关键:多个key同时失效,大量并发

如果在某一时刻缓存集中失效,或者缓存系统出现故障,所有的并发流量就会直接到达数据库。数据存储层的调用量就会暴增,用不了多长时间,数据库就会被大流量压垮,这种级联式的服务故障,就叫作缓存雪崩。

造成缓存雪崩的主要原因就是缓存集中失效,或者缓存服务发生故障,瞬间的大并发流量压垮了数据库

如何解决缓存雪崩问题?

最常用的一种方案就是保证 Redis 的高可用,将 Redis 缓存部署成高可用集群(必要时候做成异地多活),可以有效的防止缓存雪崩问题的发生。

为了缓解大并发流量,我们也可以使用限流降级的方式防止缓存雪崩。例如,在缓存失效后,通过加锁或者使用队列来控制读数据库写缓存的线程数量。具体点就是设置某些 Key 只允许一个线程查询数据和写缓存,其他线程等待。则能够有效的缓解大并发流量对数据库打来的巨大冲击。

另外,我们也可以通过数据缓存预热的方式将可能大量访问的数据加载到缓存,在即将发生大并发访问的时候,提前手动触发加载不同的数据到缓存中,并为数据设置不同的过期时间,让缓存失效的时间点尽量均匀,不至于在同一时刻全部失效。

5、缓存如何回收

1、轮询所有的key,将过期的key值进行删除,缺点,轮询的时候需要暂停工作线程,花费时间过长。
2、在请求的时候,判断key是否过期。
3、定时随机查询部分缓存的key进行校验删除,查询机制可以:分段、随机。

5.1缓存如何淘汰?

1、当redis内存空间不足的情况下,会进行内存淘汰,淘汰机制有如下几种:
2、淘汰机制里不允许淘汰
3、LRU(最近最长未使用)LFU(最近最少访问)、FIFO(先进先出)、random、TTL(设置缓存时间)
4、全空间
6、设置过过期的key的集合中

6、如何进行缓存预热?如何知道哪些是热key?

1、直接写个缓存刷新页面/接口,上线时手工操作下。
2)数据量不大,可以在项目启动的时候自动进行加载。
3)定时刷新缓存。
如何知道热key,通过对请求日志进行数据统计,使用strom/kafaka等进行统计。
缓存冷启动问题与缓存预热解决方案

7、不一致的问题

7.1 redis主从不一致的问题

1、redis默认是弱一致性,异步的同步。
2、锁不能用主从(单实例、分片集群、redlock)==》redisson
3、配置中提供有多少个client连接同步,通过配置同步因子,趋向于强一致性。

7.2、数据库和缓存不一致怎么解决?

不一致的情况有哪些?在并发情况下:
两个线程A、B;
总体分为四步:A 更新数据库值 =1,B更新数据库 =2;A更新缓存,B更新缓存。A删除缓存,B删除缓存。
a、先更新数据库,再更新缓存,–》容易脏读。
b、先更新缓存,再更新数据库,–》容易数据写入失败。
c、先删除缓存,再更新数据库,–》。
d、先更新数据库,再删除缓存,–》。(建议)
e、读写分离、数据库主从数据库不一致-,–》在从数据库同步的时候容易失败。
阿里的canal 、binlong订阅 解决,消息队列MQ监听修改数据
CAP原则

数据不一致出现这些场景

8、描述一下redis持久化

Redis 有两种持久化方案,RDB (Redis DataBase)和 AOF (Append Only File)

Redis与AOF比较

8.1、RDB(Redis DataBase)

RDB是Redis 默认的持久化方案。
持久化策略:在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件,默认是和当前配置文件保存在同一目录。
Redis 重启会通过加载dump.rdb文件恢复数据。

在redis.conf 配置文件的SNAPSHOTTING 下

save 命令:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止
# sava m n 
表示m秒内数据集存在n次修改时,自动触发bgsave  。如果想停用,或者注释掉save,或者 save “ ”  空字符

# bgsave   
执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。

# dir
该参数是设置快照文件的存放路径 ,默认是和当前配置文件保存在同一目录。

# dbfilename 
该参数是设置快照的文件名,默认是 dump.rdb

# rdbchecksum :yes。默认值是yes。
在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

# rdbcompression ;yes。默认值是yes。
对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。

# stop-writes-on-bgsave-error :yes。 默认值为yes。
当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了

基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。

恢复数据
将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可,默认将最新的备份RDB存储在安装目录下,redis就会自动加载文件数据至内存了。Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。

停止备份命令:

>redis-cli config set save " "
或者手动修改配置文件

优点:
1、当备份大量数据的时候,RDB备份速度比AOF快很多。
2、生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
3、RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集,这种文件非常适合用于进行备份和灾难恢复。

缺点:
1、RDB方式数据没办法做到实时持久化/秒级持久化,容易会丢失最后一次缓存内容。
2、RDB是二进制的保存文件,在版本兼容上会存在问题。
3、因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,如果不采用压缩算法,存储内容过大,内存消耗量,频繁执行成本过高(影响性能)。

8.2AOF (Append Only File)

AOF :Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

在 redis.conf 配置文件的==== APPEND ONLY MODE ==== 下:

# appendonly:no。默认值为no
该参数表示是否开启AOF模式

# appendfilename :"appendonly.aof"
该参数表示持久化的文件名,默认是"appendonly.aof"

# appendfsync:no/always/everysec
aof持久化策略的配置;
no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全;
always表示每次写入都执行fsync,以保证数据同步到磁盘,效率很低;
everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。通常选择 everysec ,兼顾安全性和效率。
# dir  
该参数是指文件保存目录

# auto-aof-rewrite-min-size:64mb
设置允许重写的最小aof文件大小,避免了达到约定百分比但内存仍然很小的情况还要重写。

# no-appendfsync-on-rewrite:no。
默认为no,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入.

# auto-aof-rewrite-percentage:默认值为100。
重写的占比。设置当AOF文件大小超过上一次重写的aof文件大小的百分之多少进行新的重写。

AOF重写
因为AOF持久化的方式是将redis的执行命令全部追加到文件中,AOF文件会特别大,占据的内存也越来越大。大多数没用的数据也会加载进去,而重写就是通过判断文件的大小,当文件大小超过上述配置文件配置的阈值时,通过对内容压缩、优化,重写出最小的指令集。也就是将当前内存中的数据重新进行查询,保存。

重写后可以将原有的多条相同key值的命令,进行最后结果汇总,保留一条指令进行持久化。

当AOF重写时,也是通过fork子进程进行重写,不影响父进程对新数据的写入,可以保证数据的安全性。

但是,当父进程的写的值更新了数据库的状态且子进程中重写AOF完毕,导致数据库和AOF不一致,怎么办?

这是就出现了AOF重写缓冲区,这个缓冲区是在创建子进程后开始使用,当Redis服务器执行一个写命令之后,就会将这个写命令也发送到 AOF 重写缓冲区。当子进程完成 AOF 重写之后,就会给父进程发送一个信号,父进程接收此信号后,就会调用函数将 AOF 重写缓冲区的内容都写到新的 AOF 文件中。

优点:
1、AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。
2、AOF 文件使用 Redis 命令追加的形式来构造,因此容易修正 AOF 文件。
3、AOF 文件的格式可读性较强,方便使用者灵活的处理方式。

缺点:
1、对于具有相同数据的的 Redis,AOF 文件通常会比 RDB 文件体积更大。
2、在 Redis 的负载较高时,AOF的同步的频率,会导致Redis的性能降低。

8.3、主从同步也算持久化

参考主从复制,这篇博客有相应的操作步骤。

8.4最后应该采用哪种方式呢?

如果可以忍受一小段时间内数据的丢失,使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,而且使用 RDB 还可以避免 AOF 一些隐藏的 bug;否则就使用 AOF 重写写。正常情况下,两者应该协同使用,应当首先使用AOF恢复数据,毕竟AOF保存的数据是最新的。

Redis的Set类型和Java中的HashSet类型有什么区别?

  1. 数据存储位置:Redis的Set是一种服务器端存储的数据结构,存储在内存中,并且可以进行持久化保存。而Java中的HashSet是一种客户端端存储的数据结构,存储在JVM的内存中,不支持持久化。

  2. 分布式支持:Redis的Set类型是支持分布式部署的,可以在多个Redis节点上进行分片和存储,实现高可用和横向扩展。而Java中的HashSet只能在单个JVM内存中使用,无法分布式部署。

  3. 功能和操作:Redis的Set类型提供了丰富的集合操作,如并集、交集、差集等,可以方便地进行集合运算。而Java的HashSet是基于哈希表实现的Set,提供了常见的集合操作,如添加元素、删除元素、判断元素是否存在等。

  4. 数据类型限制:Redis的Set类型可以存储各种数据类型,包括字符串、数字、对象等,而Java的HashSet只能存储对象类型,基本类型需要通过包装类进行封装。

  5. 数据持久化:Redis的Set类型支持将数据持久化到磁盘中,可以在服务重启后恢复数据。而Java的HashSet在JVM退出时会释放内存,数据无法自动持久化。

总的来说,Redis的Set类型是一种功能更强大、支持分布式部署和持久化的数据结构,而Java的HashSet是一种简单的哈希表实现的集合,适用于单个JVM内存中的数据存储。根据具体的需求和场景选择合适的数据结构。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值