redis知识点整理自敖丙

215 阅读6分钟

缓存穿透

是什么

查询一个数据库不存在的数据,缓存没有,一直从数据库查。浪费数据库性能,严重一点db宕;

怎么办
  • 缓存空对像 就算数据不存在,我们也缓存一个空对象进去,下次查询的时候就不会去数据库查了。弊端是需要消耗一定的内存,如果此刻数据加进去了,存在一段时间的数据不一致,可以在加入数据的时候清缓存。
  • 布隆过滤器
  • 接口增加校验 例如数据库id都是>0的,你来个查id为-1的数据,这时候直接return

布隆过滤器

原理

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

弊端
  • 存在误判,可能要查到的元素并没有在容器中,但是hash之后得到的k个位置上值都是1。如果bloom filter中存储的是黑名单,那么可以通过建立一个白名单来存储可能会误判的元素。
  • 删除困难。一个放入容器的元素映射到bit数组的k个位置上是1,删除的时候不能简单的直接置为0,可能会影响其他元素的判断。
代码

对于一个确定的场景,我们预估要存的数据量为n,期望的误判率为fpp,然后需要计算我们需要的Bit数组的大小m,以及hash函数的个数k,并选择hash函数

// guava
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), total);

// redisson
RBloomFilter<Integer> bloomFilter = redissonClient.getBloomFilter("test");
// 数据量,误判率
bloomFilter.tryInit(100000000L,0.03);

缓存雪崩

是什么

缓存集中失效,发生大量缓存穿透。所有的查询落在数据库上,导致雪崩

怎么办
  • 加锁排队 一个key只能有一个线程去数据库查询,添加到缓存中
  • 数据预热 预先更新缓存
  • 过期时间加随机值 防止大量缓存在同一时间过期

缓存击穿

某个扛着大并发的key失效了,大量请求打到数据库,导致db宕

Redis的持久化

持久化有两种形式、一种是RDB即内存数据的二进制序列化形式、第二种是AOF即内存数据修改的日志文本。

  • RDB

Redis使用操作系统的多进程COW(Copy On Write)机制来实现快照持久化即调用glibc函数fork产生一个子进程、父进程继续处理客户端请求、两者共享内存。这时候如果客户端请求过来修改内存数据、就会使用COW机制进行数据段分离、将要修改的那一页数据复制一份、父进程对复制的进行修改、随着修改的越来越多、会慢慢接近原来的2倍。

// 相关配置
//900秒内有1个更改 || 300秒内有10个更改 || 60秒内有10000个更改,满足其一就快照一次
save 900 1
save 300 10
save 60 10000

dbfilename dump.rdb
  • AOF

重新执行这些指令即可恢复。随着运行时间的增加、日志文件越来越大、这时候可以使用bgwriteaof指令进行重写即fork一个子进程对内存遍历转化为set指令+期间的增量指令形成新的日志文件。默认配置是aof文件大小是上次rewrite后大小的一倍且文件大于64M。

// 相关配置
// no:表示等操作系统进行数据缓存同步到磁盘(快)
// always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
// everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec

appendonly no
appendfilename appendonly.aof
  • Redis4.0混合持久化

Redis重启的时候、先加载RDB文件、然后重放增量AOF日志、大大提高了重启效率。

Redis过期键的删除策略

Redis中同时使用了惰性过期和定期过期两种过期策略。

  • 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
    (expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)

分布式锁

Redis2.8之后可以使用该指令,临界区代码控制在60s内执行完毕,超过60s将无法保证严格串行,需要人工介入修正数据。

set lockName true ex 60 nx

Redis的使用场景

  • 分布式锁
  • 分布式缓存
  • 分布式session
  • 限流器
  • 计数器
  • 排名

Redis的数据类型

  • 字符串(String) 内部结构类似ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配即分配空间(capacity)> 长度(len)。1M以下翻倍扩容,以上每次增加1M(最大512M)。
  • 列表(list) 内部结构类似LinkedList,插入快、查询慢。内部采用quicklist,元素较少的情况下使用一块连续的内存存储、无前驱和后继指针即压缩列表(ziplist),元素多的时候以ziplist为单位加上前驱和后继指针构成的quicklist。
  • 字典(hash) 内部结构类似HashMap,数组+列表构成,redis采用渐进式rehash,同时保存新旧两个哈希结构,然后后续通过定时任务逐渐将旧的迁移到新的,迁移完成,旧的删除。
  • 集合(set) 内部结构类似HashSet,无序唯一。
  • 有序集合(zset) 跳跃列表