【Redis】

基本类型

String——字符串 或 Json
缓存:
计数器:
自增ID:

List——LinkedList, 队列:右进左出 。:右进右出
异步队列:
任务轮询:
文章列表:

Hash
整个博客的访问人数:
某页墨客的访问量,姓名,联系方式,住址等。

Set
抽奖:随机返回元素
共同关注:交集

Sorted set
排行榜:
订单支付超时:score为订单超时时间戳,然后写个定时任务每隔一段时间执行zrange。

签到——位图数据结构

bitmap储存的是连续的二进制数字。

基本命令:
setbit key [第几位] [0或1]
getbit key [第几位] [0或1]

bitcount key 计数里面为1的个数。
bitcount key starrt end 范围计数,是指字符的下标,所以,是位数的8的整数倍。

bitpos key bit(0或1) [start-字符下标] [end-字符下标]

bitfield:

## 模拟用户2021年7月15日签到,偏移量从0开始
setbit userid:sign:202107 14 1
## 获取用户2021年7月的签到数据
127.0.0.1:6379> bitfield userid:sign:202107 get u31 0

统计UV——HyperLogLog数据结构

UV 要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的 ID,无论是登陆用户还是未登陆用户都需要一个唯一 ID 来标识。

方案一:
我们第一反应就是为每一个页面搞一个独立的 set 集合来存储所有当天访问过此页面的用户 ID。当一个请求过来时,我们使用 sadd 将用户 ID 塞进去就可以了。通过 scard 可以取出这个集合的大小,这个数字就是这个页面的 UV 数据。没错,这是一个非常简单的方案。
存在问题:
1:用户ID一个约占32个字节,占用内存过大。
2:数据量大时,sadd性能会下降。

方案二:
HyperLogLog 提供不精确的去重计数方案。

首先得到64位的hash值,用前14位来定位桶的位置(共有 [公式] ,即16384个桶)。后面50位即为伯努利过程,每个桶有6bit,记录第一次出现1的位置count,如果count>oldcount,就用count替换oldcount。

在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

常用命令:

pfadd uv user1  #新元素
pfcount uv #计数

去重——布隆过滤器

当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。

添加布隆过滤器插件:

[root@izuf65itgtxe1lpfpb***** redis]# git clone https://github.com/RedisBloom/RedisBloom.git
[root@izuf65itgtxe1lpfpb***** redis]# cd RedisBloom/
[root@izuf65itgtxe1lpfpb***** RedisBloom]# make
[root@izuf65itgtxe1lpfpb***** RedisBloom]# vi /usr/local/redis/redis.conf 
## 增加配置
loadmodule /usr/local/redis/RedisBloom/redisbloom.so
## 重启redis就行

常用命令:

bf.add bloomFilterKey user1
bf.exists bloomFilterKey user3

添加元素时,先把value转化为字节(getBytes(value,”UTF-8")),通过算法对元素计算出k(14)个独立的hash值,然后用这k个独立的hash值与位图长度( 201978)进行取余,对应位置设置1。

判断元素是否存在,对元素计算出k个独立的hash值,然后用这k个独立的hash值与位图长度(201978)进行取余,所有的位置都是1表示存在,只要有一位为0都是不存在。

注意:位图长度越长错误率越低,但是需要很大的空间,一般这里都是用预计放入元素量,当实际数量超出这个值时,误判率会上升

错误率计算器:https://krisives.github.io/bloom-calculator/

实现:
使用双重校验

if(bloomFilter.exist(data)) {
    // 2.如果布隆过滤器存在,需要在MySQL中进行二次校验
    if(mysqlService.exist(data)) {
      // 3.数据存在
      existFlag = true;
    }
  }

布隆过滤器使用场景:

  • 黑名单
  • 爬虫
  • 缓存穿透

Redis单线程为什么这么快

严格来说, Redis Server是多线程的, 只是它的请求处理整个流程是单线程处理的。 这一点我们一定要清楚了解到,不要单纯地认为Redis Server是单线程的。

  • Redis大部分操作在内存完成
  • 采用IO多路复用机制
  • 非CPU密集型任务
  • 单线程的优势

1.纯内存操作

内存操作快,数据结构搞笑——hash和跳表。

2.采用IO多路复用机制

文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。

3.非CPU密集型任务

采用单线程的缺点很明显,无法使用多核CPU。

Redis的瓶颈在于内存和网络带宽。

在高并发的情况下,可以采用集群,而不是多线程。

4.单线程的优点

  • 避免上下文切换
  • 避免访问共享资源加锁导致性能损耗

5.单线程的缺点

单线程处理最大的缺点就是,如果前一个请求发生耗时比较久的操作,那么整个Redis都会被阻塞,其他请求也无法进来,直到这个耗时久的操作处理完成并返回,其他请求才能被处理到。

所以,我们在使用Redis时,一定要避免非常耗时的操作,例如使用时间复杂度过高的方式获取数据、一次性获取过多的数据、大量key集中过期导致Redis淘汰key压力变大等等,这些场景都会阻塞住整个处理线程,直到它们处理完成,势必会影响业务的访问。

6.多线程优化
耗时操作采用异步:

  • 比如AOP每秒刷盘。——异步
  • lazyfree机制,异步释放内存。——异步
  • 请求数据的协议解析。——多线程

过期策略

1.惰性删除

访问(读写)时,会检测key是否过期。

2.定期删除

把设置了有效期的key存在一个集合里,默认每秒检测10次,随机抽查20个查看是否过期。

如果比例超过1/4,就重复上述操作。(贪心算法)

为了保证过期扫描不会出现循环过度,线程卡死。增加了扫描时间的上线,25ms。

大量key集中过期导致卡顿,如何解决?

方案一:在设置 key 的过期时间时,增加一个随机时间

redis.expireat(key, expire_time + random(300))

方案二:Redis 4.0 以上版本,开启 lazy-free 机制

lazyfree-lazy-expire yes

内存淘汰机制

当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。 交换会让 Redis 的性能急剧下降。

配置参数 maxmemory 来限制内存超出期望大小。

8种算法:

  • noeviction:不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
  • volatile-lru:尝试淘汰设置了过期时间的 key,通过LRU算法驱逐最近最少使用的key。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
  • volatile-random:尝试淘汰设置了过期时间的 key,在设置了过期时间的key集合中随机选择数据淘汰。
  • volatile-ttl:尝试淘汰设置了过期时间的 key,在设置了过期时间的key集合中优先淘汰ttl小的。
  • allkeys-lru:和volatile-lru的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
  • allkeys-random:和volatile-random的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
  • volatile-lfu:尝试淘汰设置了过期时间的 key,通过LFU算法驱逐使用频率最少的key。没有设置过期时间的 key 不会被淘汰。
  • allkeys-lfu:和volatile-lfu的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。

Redis持久化

RDB:
优点:

  • RDB文件紧凑,体积小,网络传输快,适合全量赋值
  • RDB最大化了Redis的性能,可以在从节点fork一个子进程。不影响主进程

缺点:

  • 快照是定期生成,损失数据较多。
  • 当数据量较大时,fork 的过程是非常耗时的,fork 子进程时是会阻塞的,在这期间 Redis 是不能响应客户端的请求的。

AOF:
优点:

  • 数据安全性高
  • AOP文件易读,可修改。

缺点:

  • 文件过大,即使通过AOF重写,文件体积依然很大。
  • 数据恢复速度比RDB慢。

混合持久化:
开启混合持久化时,fork出的子进程先将共享的内存副本全量的以 RDB 方式写入 AOF 文件。

然后在将重写缓冲区的增量命令以 AOF 方式写入到文件。

简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。

主从复制

  • 全量复制
  • 增量复制
  • 无盘复制

哨兵

1.状态感知
2.心跳检测
3.主观下线和客观下线——n/2+1
4.选举哨兵领导者——随机超时时间
5.谁来做master——优先级配置>数据完整性>runid较小
6.通知客户端

Redis集群

1.数据如何分片 ——虚拟槽分区–所有实例共用16834个槽位
2.MOVED和ASK的共同点: 两者都是重定向
MOVED和ASK的不同点
MOVED:槽已经确定
ASk:槽在迁移过程中,key有可能在source节点有可能在target节点
3.槽位迁移感知:
客户端保存了槽位和节点的映射关系表,当客户端收到moved指令的时候,他会去刷新槽位映射关系表,获取到最新的映射关系。当收到ask转向异常时,不会刷新槽位映射关系表,因为它是临时纠正。

Redis分布式锁

缓存穿透、缓存击穿、缓存雪崩

缓存处理流程
请添加图片描述
缓存穿透:
请添加图片描述
缓存穿透指用户不断访问不存在的数据。很可能是攻击者。

解决方案:
1:严格的权限控制:
2:请求参数进行校验:
3:使用布隆过滤器:在服务启动的时候先把数据的查询条件,例如数据的 ID 映射到布隆过滤器上,当然如果新增数据时,除了写入到数据库中之外,也需要将数据的ID存入到布隆过滤器中。快速判断key是否在数据库中存在,不存在直接返回。如果存在则继续查询缓存和数据库。
4:(不常用)查询不到的数据也放入缓存:

缓存击穿:
请添加图片描述
解决方案:
1.设置热点数据永不过期,
2.加锁:tryLock()+递归——这里粒度太大,对key加锁最好。——引入分布式锁。

缓存雪崩:
请添加图片描述
解决方案:
1.过期时间加一个随机值。
2.缓存预热。定时对缓存进行更新:缓存快实现时,或制作刷新缓存页面。
3.设置热点数据永不过期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值