Redis 持久化,缓存穿透,缓存雪崩和缓存击穿详解

文章参考部分网上代码及资料整合 

  图片参考:https://blog.csdn.net/kongtiao5/article/details/82771694

  使用场景参考:(https://www.cnblogs.com/dukuan/p/9132600.html)

redis是什么?

redis是一种支持Key-Value等多种数据结构的存储系统。可用于缓存、事件发布或订阅、高速队列等场景。该数据库使用ANSI C语言编写,支持网络,提供字符串(String)、哈希(hash)、列表(list)、集合(set)、有序集合zset(sorted set)  基数统计(HyperLogLog)等结构直接存取,基于内存,可持久化。

使用场景参考:(https://www.cnblogs.com/dukuan/p/9132600.html)

1、字符串使用场景

    a) 缓存功能

        典型使用场景:Redis作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从Redis中获取,由于Redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。

        开发提示:与MySQL等关系型数据库不同的是,Redis没有命令空间,而且也没有对键名有强制要求,但设计合理的键名,有利于防止键冲突和项目的可维护性,比较推荐的方式是使用“业务名:对象名:id:[属性]”作为键名。例如MySQL的数据库名为vs,用户表名为user,那么对应的键可以用"vs:user:1","vs:user:1:name"来表示,如果当前Redis只被一个业务使用,甚至可以去掉vs。如果键名比较长,例如"user:{uid}:friends:message:{mid}",可以在能描述含义的前提下适当减少键的长度,例如采用缩写形式,从而减少由于键过长的内存浪费。

    b) 计数

        典型应用场景:视频播放数计数的基础组件,用户每播放一次视频,相应的视频播放数就会自增1。Redis可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源。

        开发提示:实际上一个真实的计数系统要考虑的问题会很多,防作弊、按照不同维度计数,数据持久化到底层数据源等。

    c) 共享Session

        典型应用场景:用户登陆信息,Redis将用户的Session进行集中管理,每次用户更新或查询登陆信息都直接从Redis中集中获取。

    d) 限速

        典型应用场景:验证码接口访问频率限制,用户登陆时需要让用户输入手机验证码,从而确定是否是用户本人,但是为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过5次。

2、哈希使用场景

    a) 缓存用户信息

        相比于使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上会更加便捷。可以将每个用户的id定义为键后缀,多对field-value对应每个用户的属性。

        哈希类型和关系型数据库不同之处:

              哈希类型是稀疏的,而关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,而关系型数据库一旦添加新的列,所有行都要为其设置值(即使为NULL)。

              关系型数据库可以做复杂的关系查询,而Redis去模拟关系型复杂查询开发困难,维护成本高。

        三种缓存用户信息优缺点比较:

              原生字符串类型:每个属性一个键

                   优点:简单直观,每个属性都支持更新操作。

                   缺点:占用过多的键,内存占用量较大,同时用户信息内聚性比较差,所以此种方案一般不会在生产环境使用。

             序列化字符串类型:将用户信息序列化后用一个键保存。

                  优点:简化编程,如果合理的使用序列化可以提高内存的使用效率。

                  缺点:序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据取出进行反序列化,更新后再序列化到Redis中。

             哈希类型:每个用户属性使用一对field-value,但是只用一个键保存。

                  优点:简单直观,如果使用合理可以减少内存空间的使用。

                  缺点:要控制哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存。

3、列表使用场景

    a) 消息队列

        Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的"抢"列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。

    b) 文章列表

        每个用户有属于自己的文章列表,现在需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。

    c) 开发提示

        lpush + lpop = Stack(栈)

        lpush + rpop = Queue(队列)

        lpush + ltrim = Capped Collection(有限集合)

        lpush + brpop = Message Queue(消息队列)

4、集合

    a) 标签(tag)

        集合类型比较典型的使用场景是标签(tag),例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣就是标签。 开发提示:用户和标签的关系维护应该在一个事物执行,防止部分命令失败造成的数据不一致。

5、有序集合

    a) 排行榜系统

        有序集合比较典型的使用场景就是排行榜系统,例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播放数量、按照获得的赞数。

 

Redis的持久化机制

  RDB:(Redis DataBase)

  RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。RDB是Redis默认的持久化方式,会在对应的目录下生产一个dump.rdb文件,重启会通过加载dump.rdb文件恢复数据。

2、优点

1)只有一个文件dump.rdb,方便持久化;

2) 容灾性好,一个文件可以保存到安全的磁盘;

3) 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化(使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能) ;

4)如果数据集偏大,RDB的启动效率会比AOF更高。

3、缺点

1)数据安全性低。(RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不是特别严格的时候)

2)由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

  AOF(Append-only file)

二、AOF

1、AOF持久化是以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,文件中可以看到详细的操作记录。她的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

2、优点

1)数据安全性更高,AOF持久化可以配置appendfsync属性,其中always,每进行一次命令操作就记录到AOF文件中一次。

2)通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。

3)AOF机制的rewrite模式。(AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall))

3、缺点

1)AOF文件比RDB文件大,且恢复速度慢;数据集大的时候,比rdb启动效率低。

2)根据同步策略的不同,AOF在运行效率上往往会慢于RDB。

Redis缓存穿透

  redis的缓存穿透是指查询一个数据库不存在的数据,一个正常的操作是用户查询数据首先查看redis缓存是否存在数据,不存在则查询数据库,查询出有数据则放入redis缓存,下次直接读取redis缓存,避免频繁读取数据库进行IO.

如果我们查询一个不存在的数据,则会造成一直读取数据库,如果有人恶意攻击,则会造成数据库压力过大,甚至压垮服务器,这就是所谓的缓存穿透.

一个小小的个人解决方案: 如果查询出的数据为空,也放入redis缓存,只是缓存时间设置短一些.

缓存雪崩

  redis缓存放入大量的key,然后在某个时间点缓存集中过期失效.

此刻就会造成大量的请求过来都会去同时查询数据库,而不走redis缓存,数据库压力陡增,在秒杀,双11等场景下,很容易压垮服务器.

小小的解决方案

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
  3. 设置热点数据永远不过期.

缓存击穿

  在某些特殊节点,一个热点数据被频繁访问,在失效的瞬间就会有大量的请求进来,导致部分越过缓存去读取数据库

  在多线程高并发情况下,更为常见.

 

 解决方案: 双重校验(Dubbo Check)类似线程安全的懒汉单例模式实现,保证只会有一个线程去访问数据库

 

posted @ 2019-10-10 14:37  蚂蚁style  阅读(562)  评论(0)    收藏  举报