深入浅出Redis


redis在互联网界使用如此之广泛,不仅让人赞叹不已,下面博主来和大家一块研究一下redis的雪崩、击穿、穿透等问题。

1. SpringBoot整合Redis方式

  1. 直接通过RedisTemplate来使用
    @RequestMapping("/cachetest")
    public void test() {
        redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25));
        User user = (User) redisCacheTemplate.opsForValue().get("userkey");
        logger.info("当前获取的:{}", user.toString());
    }

2.spring cache注解方式

 		//一般有三种操作方式:
 	    //@Cacheable:在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;没有则调用方法并将方法返回值放进缓存。
        //@CachePut:将方法的返回值放到缓存中。
        //@CacheEvict:删除缓存中的数据。
    @CachePut(value ="user", key = "#user.id")
    public User save(User user) {
        userMap.put(user.getId(), user);
        logger.info("进入save方法,当前存储对象:{}", user.toString());
        return user;
    }
    @CacheEvict(value="user", key = "#id")
    public void delete(int id) {
        userMap.remove(id);
        logger.info("进入delete方法,删除成功");
    }
    @Cacheable(value = "user", key = "#id")
    public User get(Integer id) {
        logger.info("进入get方法,当前获取对象:{}", userMap.get(id)==null?null:userMap.get(id).toString());
        return userMap.get(id);
    }

Redis三种缓存问题

1. 缓存雪崩

假设现在我有一个电商系统,我一般会采取springboot的定时任务去刷新缓存,或者查不到之后去更新缓存。那么定时任务刷新就有一个问题。比如:如果首页所有Key的失效时间都是12小时,中午12点刷新的,我零点有个大型促活动大量用户涌入,假设每秒6000个请求,本来缓存可以抗住这些请求,但是缓存中所有Key都失效了,正好处在失效时间。此时6000个/秒的请求全部落在了DB,DB必然扛不住,好的情况是页面报错,500等,次之就是服务器宕机(在校开发的亲身经历),假如该服务器存在重要数据且尚未缓存,那么造成的损失便是不可估量的,网传之前有一家企业经历过此次事件,该企业损失了几千万

2. 缓存穿透

缓存穿透看上去和缓存穿透字面意思差不多,但其实这是两种概念
假设现在有一个不知名黑客A,不断发起标记有id=-1或者id特别大(即不存在的数据)的请求,我们数据库的id都是从1自增的,这样的不断攻击导致数据库压力很大,严重会击垮数据库,大量的请求也回落到DB上。

3. 缓存击穿

接下来是缓存击穿,这个概念其实跟缓存雪崩有点像,但是又有一点不一样,雪崩是因为大面积的缓存失效,打崩了DB
假设我的系统存在一个热点数据,大量并发集中访问这个点,但是当这个Key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个Key的点上击穿了缓存。这就是缓存击穿

缓存问题解决方案:

1. 缓存雪崩

(1)不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。可以用随机数生成的方式设置缓存失效时间。可以自定义一个annotation(推荐),也可以setRedis(key, value, time+Math.random()*10000);
(2)通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

2. 缓存穿透

(1)BloomFilter ,这是一个避免缓存穿透的一个利器

布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

(2)在代码中加入判断,在前期写死(不建议,判断id等容易引起缓存穿透的点,给他堵死!)
(3)如果一个查询返回的数据为空,仍把这个空结果缓存。这样请求就落到了Redis上

3. 缓存击穿

(1)其实我们对于这种往往可以不设置过期时间的方式,简单粗暴
上线之后可以写个测试页面手动添加所有的想要缓存的数据,热加载以下。可以是一个空白页等等(个人之拙见)

例如我的个人博客网站其中有一段就是我热加载我的所有数据到Redis中,假如涉及到增删改等操作直接 @CacheEvict(value=“user”, key = “#id”)
然后在加载到Redis中

Redis数据类型

结构类型结构存储方式结构的读写方式常见使用场景
String可以是字符串、整数或者浮点数等基本类型对整个字符串或者字符串的其中一部分执行操对存储 json字符串,生成自增 id
List链表上的每个节点都包含了一个字符串从链表的两端推入或者弹出元素:根据偏移量对链表进行修剪。读取单个或者多个元素;根据值来查找或者移除元素消息队列、最新内容
Hash包含键值对的无序散列表添加、获取、移除单个键值对:获取所有键值对对象类数据
Set据值来查找或者移除元素并且被包含的每个字符事都是独一无的、各不相同添加、获取、移除单个元素;检查-一个元素是否存在于某个集合中;计算交集、并集、差集;从集合里卖弄随机获取元素共同好友列表、附近的人
Zset字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定添加、获取、删除单个元素;根据分值范围(range)或者成员来获取元素排行榜
1. string底层实现:

字符串对象底层数据结构实现为简单动态字符串(SDS)和直接存储,但其编码方式可以是int、raw或者embstr,区别在于内存结构的不同。
SDS结构图如下

struct sdshdr {    
  // 用于记录buf数组中使用的字节的数目
  // 和SDS存储的字符串的长度相等  
	int len;    
  // 用于记录buf数组中没有使用的字节的数目   
	int free;    
  // 字节数组,用于储存字符串
	char buf[];   //buf的大小等于len+free+1,其中多余的1个字节是用来存储’\0’的。
};

图示:
在这里插入图片描述

2. hash底层实现

redis的hash架构就是标准的hashtab的结构,通过挂链解决冲突问题。redis的哈希对象的底层存储可以使用ziplist(压缩列表)和hashtable。当hash对象可以同时满足一下两个条件时,哈希对象使用ziplist编码。

在这里插入图片描述

Redis是什么

REmote DIctionary Server(Redis) 是一个由Salvatore
Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI
C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets)
和 有序集合(sorted sets)等类型。

Redis为何这么快
  1. 完全基于内存
  2. redis的数据结构是经过单独设计的,对操作非常简单,类似于HashMap
  3. 采用单线程,避免了不必要的上下文竞争和切换
  4. 使用多路复用IO模型,非阻塞IO
Redis过期策略
  • 定时删除
    含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
    优点:保证内存被尽快释放
    缺点:
    若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
    定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
    没人用

  • 惰性删除
    含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
    优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
    缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)

  • 定期删除
    含义:每隔一段时间执行一次删除过期key操作
    优点:
    通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理"定时删除"的缺点
    定期删除过期key–处理"惰性删除"的缺点
    缺点
    在内存友好方面,不如"定时删除"
    在CPU时间友好方面,不如"惰性删除"

持久化
  1. AOF,对修改的命令都保存到一个文件里
  2. RDB,内存快照,保存到一个dump的文件中(默认方式)
  1. AOF(append only file)持久化:
    以独立日志的方式记录每次写命令, 重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用 是解决了数据持久化的实时性,
    缺点: 文件比较大
    优点: 能够及时恢复数据
  2. RDB
    RDB持久化是把当前进程数据生成快照保存到硬盘的过程,即内存快照
    RDB的优点:
    ·RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据 快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份, 并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复。
    优点:Redis加载RDB恢复数据远远快于AOF的方式。
    缺点:RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运 行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值