目录

1:redis的数据类型和使用特点

2:redis实现分布式锁

3:如何保证redis的数据都是热点数据

4:redis数据的过期策略(懒汉模式和定期模式100毫秒)

5:redis的缓存穿透(一个key的并发数据库大量查询)

5.1:什么是缓存穿透

5.2:怎么解决缓存穿透

6:redis缓存雪崩(大量key失效的数据库大量查询)

6.1:什么是缓存雪崩

6.2:怎么解决雪崩

7:缓存击穿(大并发下才有,几率小)

7.1:什么是缓存击穿

7.2:怎么解决缓存击穿

8:缓存预热

8.1:什么是缓存预热

9:redis的线程模型

10:数据持久化

10.1:RDB方式

10.2:AOF方式

10.3:RDB和AOF对比


1:redis的数据类型和使用特点

类型 简介 特性 场景
String(字符串) 二进制安全 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M ---
Hash(字典) 键值对集合,即编程语言中的Map类型 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) 存储、读取、修改用户属性
List(列表) 链表(双向链表) 增删快,提供了操作某一段元素的API 1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列
Set(集合) 哈希表实现,元素不重复 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合) 将Set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合时,已经进行天然排序 1、排行榜 2、带权重的消息队列

HyperLogLog

每个 HyperLogLog 键只需要花费 12 KB 内存,

就可以计算接近 2^64 个不同元素的基 数。

这和计算基数时,

元素越多耗费内存就越多的集合形成鲜明对比。

key的大小固定,但是可以存储超过2的32次方的value。

牺牲精准性来提升性能和空间。

1、网站每日访问量

setnx("key","value")方法

如果key的值不存在,就是赋值,否则直接返回。不存在返回1,存在返回0

使用案例:多线程执行定时任务的时候,判断key的状态,如果存在就返回,不执行,否则某一个线程抢到直接执行

boolean setnx1= redisTemplate.opsForValue().setIfAbsent("setnx","true",10,TimeUnit.SECONDS);

3:如何保证redis的数据都是热点数据

1:(常用的数据不断加大淘汰时间)保证热点数据的数据生存日期不断更新,比如用户登陆信息,只要访问网站(命中一次)就把redis中的用户信息生存时间在当前时间的基础上累计一段时间,那么不是热点的数据,自然过期时间提前,就容易给回收,从而腾出内存空间

2:(优化存储结构)比如最近一个月用户最近登陆,用zset存储数据,每次有新用户就zadd一次,同时修改用户的分值,从而分值就是最近的登陆时间。

3:(设置数据过期策略)设置redis中的数据回收策略,当数据在到达内存的最大容量的时候,redis提供6中数据淘汰策略

volatile-lru:推荐 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止插入数据,报错

# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.# The default is:
# 默认redis淘汰策略 内存不够的时候 数据插入不进来
# maxmemory-policy noevictionnoeviction:当数据插入的时候 禁止插入(默认策略)noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。allkeys-lru:在主键空间中,优先移除最近未使用的key。(推荐)volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。allkeys-random:在主键空间中,随机移除某个key。volatile-random:在设置了过期时间的键空间中,随机移除某个key。volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

4:redis数据的过期策略(懒汉模式和定期模式100毫秒)

redis数据的回收策略

1:定时删除(大量过期会消耗CPU资源,会创建定时器)

redis中的key存在的过期时间,每个key都有一个计时器,到了时间自动删除

2:懒汉删除(弊病是存在大量过期数据占用内存)

redis中的key存在的过期时间,到了时间不会自动删除,等待下次get的时候,先查看是否过期,然后在删除,

3:定期删除(两者的优缺点都有,100毫秒触发一次,配置文件的HZ属性设置)

redis中的key存在的过期时间,到了时间不会自动删除,redis每隔一段时间扫描key然后删除数据。

5:redis的缓存穿透(一个key的并发数据库大量查询)

5.1:什么是缓存穿透

缓存穿透的,大量的用户查询在redis中的数据,但是redis中的数据不存在,就会查询到数据库,导致数据库压力飙升,影响效率。

实际案例:春运抢票,很多人抢一趟车,但是这趟车没有票了,所以redis中的票务数据不存在,查询就会命中到数据库,造成压力暴增。

5.2:怎么解决缓存穿透

1:布隆过滤器(不存在就一定不存在,存在的不一定存在)

1.1:什么是布隆过滤器

本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。

1.2:布隆过滤器数据结构

bit数组和哈希函数是布隆过滤器的核心要素。

布隆过滤器是一个 bit 向量或者说 bit 数组,长这样:

image

如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为:

image

Ok,我们现在再存一个值 “tencent”,如果哈希函数返回 3、4、8 的话,图继续变为:

值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。

这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会越来越多,这样某个值 “taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “taobao” 这个值存在。

总结:存在的不一定存在,因为有哈希冲突,不存在的则一定不存在

1.3:布隆过滤器用法

1.3.1:导入谷歌的jar

 <!--布隆过滤器--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId></dependency>

1.3.2:定义布隆filter

package com.thit.util;import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.Funnels;
import com.thit.dao.OrderMapper;
import com.thit.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;/*** @author :huyiju* @date :2020-06-12 17:23*/
@Component
public class BloomFilters {@AutowiredOrderMapper orderMapper;BloomFilter bf;/*** 创建布隆过滤器** @PostConstruct:程序启动时候加载此方法*/
//    @PostConstruct
//    public void initBloomFilter() {
//       List<Order> list= orderMapper.selectAll();
//        System.out.println("======布隆过滤器缓存数据长度======:"+list.size());
//
//
//        //创建布隆过滤器(默认3%误差)
//        bf = BloomFilter.create(Funnels.integerFunnel(), list.size());
//        for (Order order : list) {
//            bf.put(order.getOrder_id());
//        }
//    }@Beanpublic BloomFilter bloomFilter() {BloomFilter  bf = BloomFilter.create(Funnels.integerFunnel(), 1000,0.00001);return bf;}//    @Bean
//    public BloomFilterHelper<String> initBloomFilterHelper() {
//        return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8)
//                .putString(from, Charsets.UTF_8), 1000000, 0.01);
//    }/*** 判断id可能存在于布隆过滤器里面** @param id* @return*/public boolean userIdExists(int id) {return bf.mightContain(id);}}

1.3.3:在代码中首先将所有的id都放进filter,然后在代码中判断布隆过滤器中的id是否存在

//1:首先将所有的id使用put方法放到布隆中@Overridepublic Order selectAll() {//1:查询全部 将数据存储到布隆过滤器List<Order> list=orderMapper.selectAll();System.out.println("查询数据长度:"+list.size());for (Order order : list) {bloomFilter.put(order.getOrder_id());}return null;}@AutowiredBloomFilter bloomFilter;@Override//2:通过id验证布隆过滤器,使用mightContainpublic void selectid(int id) {boolean a=bloomFilter.mightContain(id);System.out.println("布隆过滤器是否存在"+id+":"+a);//如果是falseif (a==false){//false 布隆过滤器 不存在这个id 拦截不能查询 redis和数据库}else {// true 布隆过滤器 存在这个id 通过可以查询}}

2:暴力空值缓存(redis中的该辆车次数据不存在的时候查询数据库,数据库没有车票,我们就把0的值缓存到redis,设置过期时间为1分钟,其他的查询就会查到redis中不会再次查询数据库,不过依然显示没有车票)

 //1:查询redisString result=jedis.get("缓存穿透key");if (result==null){//2:查询数据库result="数据库查询的值";//3:数据库存在数据if (result!=null){//数据库存在数据,设置redis 直接返回jedis.set("缓存穿透key",result);jedis.expire("缓存穿透key","合适的时间")return result;}else {//4:数据库不存在数据,但是依然存到redis中jedis.get("缓存穿透key","数据库在不存数据");jedis.expire("缓存穿透key","短暂的时间")return null;}}

6:redis缓存雪崩(大量key失效的数据库大量查询)

6.1:什么是缓存雪崩

缓存雪崩的意思是在一个时间段大量的key失效,导致cpu集中回收这是失效的key,从而导致了大量key的查询落到了数据库。

实际案例:在双十一的时候会,有大量的秒杀商品会缓存到redis中,24小时失效,过了双十一,大量的key失效。用户的数据失效,查询就会落到数据上,造成大量波峰,系统压力大卡顿。

6.2:怎么解决雪崩

1:随机缓存时间,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。后者是热点商品和非热点商品采取不同的缓存失效时间。

        jedis.expire("key","随机过期时间")

7:缓存击穿(大并发下才有,几率小)

7.1:什么是缓存击穿

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

7.2:怎么解决缓存击穿

互斥锁实现

  String value=jedis.get("热点key");if (value==null){//代表缓存过期if(jedis.setnx("锁","1")==1){//加互斥锁value="查询数据库返回值";//次数查询数据库jedis.set("热点key","查询数据库返回值");//赋值到热点keyjedis.del("锁");//删除互斥锁}else {//这个时候代表已经再次把数据缓存到redis中了value=jedis.get("热点key");}return value; }

8:缓存预热

8.1:什么是缓存预热

就是将热点数据缓存到数据库,比如电商的秒杀,系统有秒杀商品管理页面,通过添加秒杀商品的形式将商品库存信息添加到redis中,实现缓存预热。

9:redis的线程模型

epoll模型的IO多路复用

10:数据持久化

redis数据保存在电脑的内存中,当电脑断电、死机的时候。数据怎么保证不丢失,重启之后怎么保证数据存在,redis提供了两种数据持久化的策略。

10.1:RDB方式

在redis.conf配置文件中可见一下配置信息,rdb是定时保存,可能会丢失一个时间间隔的信息。通过save命令可以在文件下看到最新保存的dump.rdb文件

#在下面的示例中,行为将是保存:#900秒(15分钟)后,如果至少更换了一个钥匙#300秒(5分钟)后,如果至少10个键发生变化#60秒后,如果至少10000个密钥更改
#默认保存文件名字
dbfilename "dump.rdb"
#默认保存文件地址
dir "/Users/huyiju/work/redis/z-redis-5.0.7/src"

10.2:AOF方式

把操作命令追加到日志的底部。保留所有的操作记录。

想比较与rdb的方式,数据完整,故障数据丢失少。可以对历史操作进行处理

#Append Only文件是另一种持久性模式,它提供#更耐用。例如,使用默认的数据fsync策略#(请参阅后面的配置文件)Redis在#戏剧性的事件,比如服务器断电,或者如果发生了什么事情#Redis进程本身会出错,但操作系统是#仍然正常运行。##可以同时启用AOF和RDB持久性,而不会出现问题。#如果在启动时启用了AOF,Redis将加载AOF,即文件#有更好的耐久性保证。appendonly yes
#默认文件名字
appendfilename "appendonly.aof"
#每秒同步一次
appendfsync everysec

aof缺点:

1:aof会把所有的操作拼接到文件中,导致文件过大,数据故障回复比较慢。

10.3:RDB和AOF对比

Redis崩溃后,重启redis会自动找备份恢复文件,下图大致描述了redis重启后的过程。

总结:

在实际应用中,根据场景不同,选择的方式也不尽相同,各有优缺点。但我个人看法,RDB的快照方式相比于AOF的逐步记录模式要好一些。至于RDB丢数据的风险,我们完全可以通过控制备份的时间间隔来避免这个问题。当然,也是可以两种方式同时使用的,只是大多不会这么做。

Redis学习_第六章(Redis面试、进阶知识点)相关推荐

  1. Redis学习笔记(八)redis之lua脚本学习

    redis系列文章目录 使用spring-data-redis实现incr自增 Redis 利用Hash存储节约内存 Redis学习笔记(九)redis实现时时直播列表缓存,支持分页[热点数据存储] ...

  2. 系统架构师学习笔记_第六章(下)_连载

    系统架构师学习笔记_第六章(下)_连载 6.3 基于 UML 的软件开发过程 6.3.1  开发过程概述 UML 是独立于软件开发过程的,能够在几乎任何一种软件开发过程中使用.迭代的渐进式软件开发过程 ...

  3. 《Go语言圣经》学习笔记 第六章 方法

    <Go语言圣经>学习笔记 第六章 方法 目录 方法声明 基于指针对象的方法 通过嵌入结构体来扩展类型 方法值和方法表达式 示例:Bit数组 封装 注:学习<Go语言圣经>笔记, ...

  4. Unix原理与应用学习笔记----第六章 文件的基本属性2

    Unix原理与应用学习笔记----第六章 文件的基本属性2 改变文件权限命令:chmod 提示:文件或目录创建后,就被赋予一组默认的权限.所有的用户都有读,只有文件的所有者才有写. 相对权限设置 Ch ...

  5. 《疯狂Java讲义》学习笔记 第六章 面向对象(下)

    <疯狂Java讲义>学习笔记 第六章 面向对象(下) 6.1包装类 基本数据类型 包装类 byte Byte short Short int Integer long Long char ...

  6. 《SysML精粹》学习记录--第六章

    <SysML精粹>学习记录 第六章:活动图 活动图简介 活动图外框 活动图的关键元素   动作   对象节点   边   动作详述   控制节点 活动分区 小结 第六章:活动图 活动图简介 ...

  7. java程序设计基础_陈国君版第五版_第六章例题

    java程序设计基础_陈国君版第五版_第六章例题 class Cylinder {double radius;int height;double pi = 3.14;void area(){Syste ...

  8. Java基础学习——第十六章 Java8新特性

    Java基础学习--第十六章 Java8 新特性 Java8(JDK8.0)较 JDK7.0 有很多变化或者说是优化,比如 interface 里可以有静态方法和默认方法,并且可以有方法体,这一点就颠 ...

  9. 中根遍历二叉查找树所得序列一定是有序序列_学习数据结构--第六章:查找(查找)

    第六章:查找 1.查找的基本概念 查找:在数据集合中寻找满足某种条件的数据元素的过程. 查找的结果 查找成功和查找失败 查找表:用于查找的数据集合,由同一种数据类型(或记录)的组成,可以是一个数组或链 ...

最新文章

  1. 烂泥:ubuntu 14.04搭建Open***服务器
  2. mysql 授权管理
  3. 《Maven官方指南》指南第三方部署到远程仓库
  4. Java 10个调试技巧
  5. HDU - 4027 Can you answer these queries?(线段树)
  6. mysql - Docker Wordpress连接到本地主机上的数据库服务器
  7. mi6 android版本,小米6:我依旧是王,MIUI10.4.2稳定版与AndroidP同时到来
  8. 【机器学习-西瓜书】八、Bagging;随机森林(RF)
  9. .Net中加密解密相关知识
  10. python移动文件,将某一文件夹下的文件移至另一个文件夹下
  11. dvi线支持多少分辨率_为什么用DVI接口分辨率不能达到1080
  12. fgo国服登陆显示服务器错误,命运冠位指定安装失败怎么办 FGO国服安装失败解决办法...
  13. 极路由第三方插件大全_极路由极硬货HC5663春节折腾记
  14. SPI驱动之南瑞NRSEC3000加密芯片驱动开发总结
  15. VS Code 基础入门使用(配置)教程
  16. 三条中线分的六个三角形_三角形三条中线的交点叫什么
  17. Linux开发运维常用命令
  18. python画一片树叶的故事_《Python数据分析与机器学习实战-唐宇迪》读书笔记第7章--决策树...
  19. Jenkins创建一键发布即父子项目
  20. Jupiter notebook如何改变绘图大小

热门文章

  1. OBS显示器捕获不到问题 (无法分享屏幕或PPT)
  2. 关于springcloud使用shiro的权限控制
  3. CF1088D Ehab and another another xor problem
  4. 量子计算最新突破:在硅中实现三个自旋量子比特纠缠
  5. 机架服务器管理系统,搭建机架服务器管理系统Racktables
  6. 数字化制造系统参数化建模及仿真
  7. [量化学院]借助talib使用技术分析指标来炒股
  8. PrimeTime 工具学习笔记(1)
  9. Google 翻译(中英,英中)
  10. AndroidX 兼容包的时代