七夕节迟来的礼物

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 七夕节迟来的礼物

聊聊缓存

缓存是一种用空间换时间来解决性能问题的架构设计模式,缓存设计简单,功能相对单一,通常我们会拿它来保存一些常被用到且不常修改的数据,以便减少对数据库(磁盘)的压力。

Redis是互联网开发中最为常用的分部署缓存数据库,它相对于我们常用的MySQL关系型数据库,在整体吞吐量上,可以是MySQL几倍甚至几十倍。因此它特别适用于互联网应用这种容易高并发的场景。

但是使用Redis做缓存,在使用和设计上并不是看上去那么简单,用得好可以锦上添花,用错了,也会落井下石。

1.不要把Redis当作数据库去使用

Redis虽然具有数据持久化的功能,可以实现服务重启后数据不丢失。这一点,很容易让人误以为Redis可以作为高性能的KV数据库。实际上,免费版的Redis是个内存数据库,所有数据都是保存在内存里的,而且它通常存的不是原始数据,更多的是面向呈现的数据,可以减少了对磁盘的压力,也减少了原始数据的计算工作。但是又因为可以直接从内存读取数据响应操作,所以加了使用缓存机制的功能,在处理请求上很快。

它的特点是处理操作快,但是它有内存限制,无法存储超过内存大小的数据。(VM模式虽然可以,但是性能低下,早在版本功能迭代的时候就废弃了)

所以使用Redis做缓存,需要注意两点:

  1. 不能认为缓存系统绝对可靠,更不能认为它不会删除过期数据。
  2. 设置必要参数maxmemory来达到限制缓存对内存的使用与结合业务场景选择适合我们自己的数据淘汰策略。

2.Redis淘汰策略

策略 简单说明
noeviction 达到内存限制时不再保存新值
allkeys-lru 针对所有 Key,优先删除最近最少使用的 Key
allkeys-lfu 针对所有常用的键,优先删除最不常用的LRU键
volatile-lru 针对带有过期时间的 Key,优先删除最近最少使用的 Key
volatile-lfu 针对expire字段设置为true,优先删除最不常用的键
allkeys-random 随机删除键,为添加的新数据腾出空间
volatile-random 随机移除过期字段设置为true的键
volatile-ttl 针对带有过期时间的 Key,优先删除即将过期的 Key

具体可参考官方文档 https://redis.io/docs/manual/eviction/

在开发时,我会尽可能的选择有TTL的算法,这样即便我对于业务了解的再不怎么深刻,选择错了,在TTL的算法下,只要设置的时间不要太长,那么它所带来的损失是最小的。但是这个只是万不得已的情况下,如果有时间还是要去好好了解一下这些策略,然后再去结合业务场景的使用,去选择一个适合业务数据的缓存策略。

其次就说allkeys的算法,从key的范围角度来看,allkeys可以确保即使key没有TTL也能够被回收。毕竟总会有开发者“忘了”设置缓存的过期时间。

3.关于使用缓存的那些跑不掉的问题

说到缓存,就难免不会被提及并发,雪崩,穿透,同步等问题,面试的时候,这些问题更是常见于交谈中。

一、为什么会导致雪崩?

前面说了,缓存是为了减少请求一些不常被修改的数据对数据库的压力,因此我们要注意避免短时间内大量的缓存失效的情况,一旦发送了,就有可能瞬间会有大量的数据需要回源到数据库去查询,一瞬间去数据库带来极大的压力,极限情况下导致后端数据库直接崩溃,使得缓存失效,这就是缓存雪崩

所以我们要做的,其实就是保证不让大量的缓存同时失效。

//我们通常都是这样缓存数据的,以string为key,缓存data数据30秒
redisTemplate.opsForValue().set("string",data, 30, TimeUnit.SECONDS);
//为了保证不让大量缓存同时失效,我们可以控制这个30秒的时间,随机为这30秒加上多少秒,保证他们都不会在同一个时间点失效。
redisTemplate.opsForValue().set("string",data, 30 + ThreadLocalRandom.current().nextInt(20), TimeUnit.SECONDS)

这样设置的key就不会在30秒后同时失效,而会分散在30-50秒内失效。

也可以不为缓存设置TTL时间,不让它主动失效,但是需要后台使用定时任务每30秒去更新缓存数据,这也是一种方法。

但是无论是哪个方法,我们都需要注意一个问题,那就是data值判空问题,如果不注意这个问题,那么缓存对于你的系统来说就是落井下石的作用了。曾经我写过一次IOT的上层应用的数据展示,当时的数据获取优先度是先拿缓存,缓存没有再拿平台层。就是因为存入了null值,导致了明明平台层一直拥有数据,但是我上层应用就是拿不到,只返回了null。说到null,就不得不说一下跟null有关的缓存穿透了。

二、为什么会缓存穿透?

当我们缓存没有的时候,我们就会去数据库请求数据,然后把它当前只存在缓存里,为它设置TTL过期时间,以减少数据库压力。但是如果说,它在数据库里一直没有数值呢?或者这个数据永远都不会存在,只是被恶意请求了呢?

如果没有做任何防范措施,那么它将会每次都穿透缓存,直接请求到数据库身上,给数据库造成极大的性能压力。

对于穿透,我一般要么使用布隆过滤器来判断是否要请求数据库,要么给没有值的缓存个字符来减少对数据库的性能压力。

布隆过滤器的原理是:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。

BloomFilter<CharSequence> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), int);
//查到数据的时候,通过这个方法把数据放进去
bf.put("" + data);
//通过这个方法去查询是否有这个值
bf.mightContain("" + data)

布隆过滤器是概率型数据结构,所以它不一定完全对,有一定的误判,但是10e数据下,也不过1/1000。哪怕1/1000误判了,也不过去请求一下数据库罢了,比起10e,已经减少了很多性能压力,可以忽略不计了。

如果有人对这个数据的求证有兴趣,可以去下面这个地址试试

三、为什么同步也是问题

因为在高并发的情况下,同一时间A线程与B线程,在系统执行同步的时候,获取的值极有可能都不一样,获取值后缓存的前后顺序不一样,都会影响后续的所有使用。

比如我们设置的逻辑是:

先更新缓存,再更新数据库呢?那么在数据库压力大时,若数据库更新失败了,就会导致两者数据不一致问题。

先更新数据库,再更新缓存呢?拿物联网行业举例,因为ping不通设备,刚上报离线状态,下一毫秒就被通网的设备发送了在线状态,那么到了平台层必定是两个线程A和B去执行,如果线程 A 和 B 先后完成数据库更新,但更新缓存时却是 B 和 A 的顺序,那很可能会把旧数据更新到缓存中引起数据不一致。

所以在这方面没有绝对的方案,只有根据自身业务的内容去定制同步更新机制,尽可能的减少误差。我这边使用时是先更新数据库,然后删除缓存,被访问时按照需要再去缓存数据。虽然在个别极端的情况,可能也会出现数据不一致的问题,但是概率很低,也很快会被新数据给修复覆盖掉,所以忽略不记。

4.Redis的各种操作

简单举例最常用的几种缓存操作

一、String操作
@Autowired
private StringRedisTemplate redisTemplate;
//设置无过期时间的缓存
redisTemplate.opsForValue().set(key, "Java面试");
//设置过期时间为30秒的缓存
redisTemplate.opsForValue().set(key, "Java面试", 30, TimeUnit.SECONDS);
//获取缓存值
redisTemplate.opsForValue().get(key)
二、Set操作
@Autowired
private StringRedisTemplate redisTemplate;
//为key缓存三次
redisTemplate.opsForSet().add(key,"语文");
redisTemplate.opsForSet().add(key,"数学");
redisTemplate.opsForSet().add(key,"英语");
//为key1缓存两次
redisTemplate.opsForSet().add(key1,"语文");
redisTemplate.opsForSet().add(key1,"英语");
//取出key和key1的交集Set集合值
Set<String> intersect = redisTemplate.opsForSet().intersect(key, key1);
//最后肯定是打印出这样的结构
["语文","英语"]
三、Hash操作
@Autowired
private StringRedisTemplate redisTemplate;
//key是缓存key,"身份证","民族"为hashkey,最后才是value值
redisTemplate.opsForHash().put(key,"身份证","4415211XXXXXXXXX");
redisTemplate.opsForHash().put(key,"民族","汉族");
List<Object> values = redisTemplate.opsForHash().values(key);
四、List操作
@Autowired
private StringRedisTemplate redisTemplate;
String key = "opsForList";
redisTemplate.opsForList().leftPush(key,"张三");
redisTemplate.opsForList().leftPush(key,"李四");
redisTemplate.opsForList().leftPush(key,"王五");
//rightPop:删除并返回列表中以键存储的最后一个元素、打印张三
System.out.println(redisTemplate.opsForList().rightPop(key));
//leftPop:删除并返回列表中以键存储的第一个元素、打印王五
System.out.println(redisTemplate.opsForList().leftPop(key));

5.最后结语

对于redis的使用,本文仅是做简单介绍入门,如果只是入门项目使用,上面的四种操作足够应付一般场景。操作的使用需要依旧业务场景,每个人都不一样,但是对于缓存的使用设计的思考却是大家都需要关注的,毕竟方法用途是不变的,不断变化的只有业务需求而已。不必太过拘于数据该用哪种缓存方法,能为我们更好的解决业务问题,才是缓存被设计出来的最初目的。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
JSON API 数据格式
自造微博轮子,再爬姐姐和奶奶殿下
自造微博轮子,再爬姐姐和奶奶殿下
|
机器学习/深度学习 算法 测试技术
面试官在“逗”你系列:到底应该怎么爬楼梯?! | 牛气冲天新年征文
算法题是在面试过程中考察候选人逻辑思维能力、手写代码能力的一种方式,因为有一句古话说的好:“说一千道一万,不如写段代码看一看”。今天我们就来个单刀直入,直奔主题,从一个真实面试题到底怎么爬楼梯来聊一聊算法中的动态规划 。
203 0
|
人工智能 编解码 达摩院
中秋,和千年前的朋友一起上央视
昨天,杭州图书馆和达摩院的合作项目「AI算法复现古人“吟咏”中秋画面」,获得了CCTV13的报道。
492 0
中秋,和千年前的朋友一起上央视
|
人工智能 算法 容灾
喜迎女神节 高颜值支付宝程序媛的硬核人生
桃之夭夭,灼灼其华,在疫情渐退的三月,我们迎来第110个“女神节”——“三八”国际妇女节。 
597 0
|
云栖大会 云计算
大咖、颜值、逼格、礼物都齐了,只差一个你!
传说中,这是一个格子衬衫、双肩包、拖鞋的聚集地。 传说中,参加这个“集会”的人一言不合就会“噼噼啪啪”……敲代码。 传说中,这是一场烧脑盛宴,也是面基的好去处……
49399 0
|
云安全 安全
今天和朋友们做了五道新春大餐!
农历新年将至 安全君携几位好伙伴们, 给大家献上几道“新春大餐”。 愿您在新的一年里, 安心、顺心、省心! 第一道: 阿里云与PCCW Global  共同为全球用户提供DDoS防御服务 让更多企业的业务, 穿上稳定、有效的防护铠甲。
1884 0
我被阿里云美女清宵的观后感给撩了
这是她看完药神之后的观后感,看到chanel的声明,就吸引我一直把它读完。 package main import ( "fmt" "math/rand" "sync" "time" ) var ( r = rand.
1595 0
|
程序员 前端开发
晒晒昨天的节日礼物
昨天1024,各位发的什么?都晒晒吧。我先晒我的。 老板昨天给我发了一个邮件。内容是一周之内学会react并按需求写一个demo。 来看看别人家的公司是怎么过节的: 再来看看拿到融资后的创业公司怎么过的: 某创业公司(以拿到融资) 哎,余不禁想吟诗一首: 写字楼里写字间,写字间里程序员; 程序人员写程序,又拿程序换酒钱。
974 0
解救被困传销女演员 助人减肥找老婆 蚂蚁森林又现神功能
近日,一篇《女演员被传销组织拘禁30多天 竟因蚂蚁森林幸运逃离》的报道引发了全网热议。网友纷纷表示:蚂蚁森林功能强大,不仅能帮人减肥、找老婆,还能在关键时刻保命!
5437 0