redis是一个内存数据库,使用key-value形式在内存中管理数据。

一、redis使用场景

  1、热数据存储。对于需要频繁读写的数据,可以放到redis中,不用频繁的请求数据库。再设置策略持久化到数据库中。

  2、token管理。用户登录成功后,生成token,存储到redis中,并设置过期时间,可以模拟session,用于做认证中心。

  3、缓解数据库压力。大量并发请求访问数据库会造成数据库崩溃,可以使用redis做缓冲,先访问redis。 

二、redis为啥这么块

  主要有3个原因:

  • 纯内存操作。所有的操作都是在内存中进行。
  • 单线程操作。单线程操作避免了频繁的上下文切换。
  • 使用非阻塞I/O多路复用机制。客户端会产生具有不同事件类型的socket。在服务端,有一段I/0多路复用程序,将socket置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。

三、redis数据结构

  • string 就是常规的set/get操作
  • list 队列
  • set 不重复的集合
  • sorted set 有序的不重复集合
  • hash 结构化的对象。

四、redis持久化

  在因为故障等原因导致服务停止时,内存的数据就会丢失。因此,需要将内存的数据持久化到硬盘上。

  redis持久化有两种方式:RDB、AOF

  1、RDB方式持久化

  是通过快照来完成的。当满足一定条件时,redis会将内存中的数据进行快照并存储到硬盘上。

  进行快照的条件在配置文件中配置,由两个参数构成:时间、改动键的个数。当在指定时间内,被改动键的个数大于指定个数时,则触发RDB持久化。

  配置文件中默认有3个条件,这些条件是或的关系。  

1 save 900 1 # 15分钟内至少有一个键被更改 
2 save 300 10 # 5分钟内至少有10个键被更改
3 save 60 10000 # 1分钟内至少有10000个键被更改

  默认的rdb文件在当前的目录,文件名是dump.rdb。

  可以对Redis执行手动快照操作,命令是:save、bgsave。两者有区别:save是由主进程进行快照,会阻塞其他请求。bgsave通过fork子进程进行快照。

  注意:由于Redis使用fork来复制一份当前进程,那么子进程就会占有和主进程一样的内存资源,比如说主进程8G内存,那么在备份的时候,必须保证有16G的内存,要不然会启用虚拟内存,性能非常的差。

  2、AOF方式持久化

  是将发送到Redis服务端的每一条命令都记录下来,并且保存在硬盘的AOF文件中。

  可以通过参数appendonly来设置是否启用AOF。AOF文件的位置和RDB的位置相同,都是通过dir参数设置,默认的文件名是appendonly.aof,可以通过appendfilename参数修改。

五、redis过期删除策略以及内存淘汰机制

  过期删除策略有:定时删除、惰性删除。

  定时删除:有一个定时器在定时随机扫描一些key,key过期了则删除。这种策略因为定时器一直在扫描,很消耗系统资源。同时有一些key到了过期时间也没有删除。

  惰性删除:在使用key的时候,再检查是否过期,过期了就删除。

  定时删除和惰性删除可以配合使用。

  有个问题是:如果有些key没有被定时删除任务扫描到,同时又长时间没有被调用到,那么这些key是不会被删除的。长期这样会占用大量内存。

  这就需要使用到内存淘汰机制。

  内存淘汰机制在配置文件中开启和配置:

  在redis.conf中有一行配置

# maxmemory-policy volatile-lru

  有6种内存淘汰机制,一般使用的是:allkeys-lru。当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。

六、问题

  1、redis和数据库一致性问题

    数据库和缓存双写,必然存在数据不一致问题,对数据有强一致性要求的不能将数据放到缓存中。

  2、缓存穿透。

    大量的请求没有请求缓存,而是都请求数据库了。一般发生在黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。 

  解决方案:

  (一)利用互斥锁。当请求从缓存中得不到数据时,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试,这样能避免大量的请求数据库。

  (二)采用异步更新策略。无论请求是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

  (三)提供一个能迅速判断请求是否有效的拦截机制。比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出请求所携带的Key是否合法有效。如果不合法,则直接返回。

  3、缓存雪崩

    缓存在同一时间大面积失效,这时候又有大量的请求,则这些请求会请求到数据库,导致数据库异常。

  解决方案:

  (一)给缓存的失效时间加上一个随机值,避免集体失效。

  (二)使用互斥锁,避免大量数据库请求。

  (三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点:

  • 从缓存A读数据库,有则直接返回。
  • A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
  • 更新线程同时更新缓存A和缓存B。

  4、redis并发竞争key

    多个子系统去set一个value。

  解决方案:

  1、设置分布式锁,抢到锁的就去set。

  2、设置队列,根据实际需求将set操作变成串行。

版权声明:本文为yangtze-yufei原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yangtze-yufei/p/9141385.html