Redis-复习

八、Redis

  • Redis为什么这么快?redis采用多线程会有哪些问题?
  • Redis跳跃表的问题;
  • Redis单进程单线程的Redis如何能够高并发?
  • Redis如何使用Redis实现分布式锁?
  • Redis分布式锁操作的原子性,Redis内部是如何实现的?

数据类型

https://www.cnblogs.com/lizhenghn/p/5322887.html

redis的hash结构,字典结构

拉链法解决的hash冲突,那么在数据过多时,hash冲突不可避免,为了避免链表太长,直接进行rehash。rehash:Redis 默认使用了两个全局哈希表:哈希表 1 和哈希表 2。一开始,当你刚插入数据时,默认使用哈希表 1,此时的哈希表 2 并没有被分配空间。随着数据逐步增多,Redis 开始执行 rehash,这个过程分为三步:给哈希表 2 分配更大的空间,例如是当前哈希表 1 大小的两倍;把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;释放哈希表 1 的空间。到此,我们就可以从哈希表 1 切换到哈希表 2,用增大的哈希表 2 保存更多数据,而原来的哈希表 1 留作下一次 rehash 扩容备用。这个过程看似简单,但是第二步涉及大量的数据拷贝,如果一次性把哈希表 1 中的数据都迁移完,会造成 Redis 线程阻塞,无法服务其他请求。此时,Redis 就无法快速访问数据了。为了避免这个问题,Redis 采用了渐进式 rehash。简单来说就是在第二步拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的 entries。redis也会每秒扫描一下未迁移的数据,防止一直不被访问就无法迁移到新hash表。

redis的pipeline和lua有什么区别

当多个redis命令之间没有依赖、顺序关系(例如第二条命令依赖第一条命令的结果)时,建议使用pipline;如果命令之间有依赖或顺序关系时,pipline就无法使用,此时可以考虑采用lua脚本的方式来使用。pipeline不是原子的,lua是原子的,都不能回滚。https://www.jianshu.com/p/f06472f537bb

为什么不支持回滚?

对于语法错误,事务将不会被提交。
对于运行时错误,只有对某个键执行不符合其类型的命令时才会发生,也就是程序代码错误,这种错误只有在开发阶段才会发生,很少在生环境中发生。
因此,为了保持Redis的简单性,不提供回滚功能。

1 skiplist的复杂度和红黑树一样,而且实现起来更简单。
2 在并发环境下skiplist有另外一个优势,红黑树在插入和删除的时候可能需要做一些rebalance的操作,这样的操作可能会涉及到整个树的其他部分,而skiplist的操作显然更加局部性一些,锁需要盯住的节点更少,因此在这样的情况下性能好一些。

Redis实现分布式锁

https://juejin.cn/post/6844903830442737671
1.setnx+expire。问题1:如果只是代码里面写,代码执行到一半出异常,没有expire,锁无法释放,可以通过lua解决。问题2:a设置过期时间30s,锁自动释放,但是a没执行完,此时b获取到了锁,a使用del,释放的是b的锁。解决方式:需要把value设置为唯一的,比如UUID,标志是否是当前线程持有的,删除之前判断一下。问题3:还是上一个问题,此时a没执行完,b就开始执行了,并发执行肯定是不对的。解决办法:设置足够长的过期时间,为获取锁的线程增加守护线程,快过期时增加有效时间。分布式锁,无法等待锁释放。命令都是立即返回的,不会等待。解决方法1:轮询。解决方法2:redis有发布订阅功能,获取锁失败时订阅释放消息,获取的锁成功释放后,发送锁释放的消息。注意,redis的消息被发送后,没人接受就会丢失。2.redlock原理:假设有5个redis节点,获取锁时候从5个实例,使用相同的key和唯一的UUID获取锁,如果能获取到3个(n/2+1)个锁,并且时间在范围内,就算成功。如果锁获取失败,在所有的redis实例上面解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)

Redis缓存一致性

https://developer.aliyun.com/article/712285 -> 前面是分析,直接拉到最后。更新缓存最大的问题是更新的顺序可能不一致。先更新数据库,再删除缓存好一些。还可以通过定于binlog来异步更新缓存。

订阅binlog:A线程进行写操作,先成功淘汰缓存,但由于网络或其它原因,还未更新数据库或正在更新
B线程进行读操作,发现缓存中没有想要的数据,从数据库中读取数据,但B线程只是从数据库中读取想要的数据,并不将这个数据放入缓存中,所以并不会导致缓存与数据库的不一致
A线程更新数据库后,通过订阅binlog来异步更新缓存
延时双删: A线程进行写操作,先成功淘汰缓存,但由于网络或其它原因,还未更新数据库或正在更新
B线程进行读操作,从数据库中读入旧数据,共耗时N秒
在B线程将旧数据读入缓存后,A线程将数据更新完成,此时数据不一致
A线程将数据库更新完成后,休眠M秒(M比N稍大即可),然后再次淘汰缓存,此时缓存中即使有旧数据也会被淘汰,此时可以保证数据的一致性
先更新数据库,再删除缓存,会有短暂不一致。
如何保证redis中存放的都是热点数据
使用lru算法,从含有过期时间的key中lru,或者所有key中lru

Redis为什么单线程

因为redis没有磁盘操作,一般都是在io操作时才会多线程,而redis是内存存储,切换线程反而更耗时。b+树也是磁盘操作,所以使用跳表而不是b+树。那么redis瓶颈在哪里?两个地方,内存大小,因为关系到存储的数据量,第二个是网络带宽,因为redis客户端服务端可能不在一个机器上。

redis大key问题

如果是单个对象非常大:可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;如果是set和list存储的元素非常多,可以根据元素类型分类,把key分散到许多集群中。删除时在redis4.0之后可以用lazy-free,异步删除。
这是kaito的redis建议:http://kaito-kidd.com/2021/03/04/redis-best-practice-optimization-road/

redis的lru有什么优化

https://zhuanlan.zhihu.com/p/142893249,
如果redis中每个key都存放在这个链表中,那么内存需要很大!!!
因此redis实现了一个简化版的lru.主要目的是减少 内存占用!.
在Redis 3.0中对近似的LRU算法做了一些优化,Redis中会维护大小是16的一个候选池的内存。
当第一次随机选取的采样数据,数据都会被放进候选池中,并且候选池中的数据会根据时间进行排序。
当第二次以后选取的数据,只有小于候选池内的最小时间的才会被放进候选池中。
当某一时刻候选池的数据满了,那么时间最大的key就会被挤出候选池。当执行淘汰时,直接从候选池中选取最近访问时间最小的key进行淘汰。

redis的lfu算法

https://zhuanlan.zhihu.com/p/142893249,文章后半段
有些数据以前经常被访问到,只是最近的时间内没有被访问到,这样就导致这些数据很可能被淘汰掉,这样一来就会出现误判而淘汰热点数据。
所以有lfu(Least Frequently Used):最近频繁被使用。根据key最近被访问的频率进行淘汰。

redis6.0的多线程

Redis 的多 IO 线程只是用来处理网络请求的,对于读写命令,Redis 仍然使用单线程来处理。通过配置项启用多线程。

redis如何防止数据丢失

aof和rdb一起用;做集群方案

redis主从复制

一开始是全量复制,建立连接之后,会维护长连接,把每一次命令同步给从节点。

redis过期键删除策略:惰性删除和定期删除

定期删除:每100ms扫描20个key(配置文件配置的),删除过期的,如果25%的过期了,就继续扫描删除操作。4.0之前会阻塞主进程,4.0之后可以选择异步执行。如果有大量的key同时过期了,就会阻塞住。

Redis定时删除,这个定时底层是怎么实现的

https://www.cnblogs.com/ysocean/p/12422635.html

为什么redis用跳表不用红黑树

跳表有五种操作,插入,删除,查找,有序输出所有元素,按照范围区间查找元素,比如[100,600]的元素。红黑树最后一种操作,效率没有跳表高。树的插入删除也比跳表实现复杂。

缓存雪崩/缓存穿透/缓存击穿

缓存雪崩

缓存大面积失效,过期时间为随机就行,加上熔断策略,限制某些业务的访问,双缓存:一级缓存失效后使用二级缓存,双缓存。另一种情况是redis宕机,此时只能熔断,限流,所以建议事前预防,集群部署。

缓存穿透

查询数据库里面没有的数据,不合规的数据,导致每次都直接查询数据库,接口增加违规校验,把数据库中获取不到的数据存入redis里面,布隆过滤器(原理:https://zhuanlan.zhihu.com/p/43263751)。

缓存击穿

热点数据失效,导致大部分请求直接查数据库,热点数据不过期,或者互斥锁就可以。

redis默认的持久化方式

rdb

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值