Redis的内部原理解密

过期时间设置及原理分析

expire key seconds

setex(String key,int seconds,String value)

ttl key 命令可以查看是否设置了过期时间以及时间变化

persist key 取消过期时间

expire 原理:

消极方法(passive way): 当key在访问的时候发现已经失效会删除

积极方法(): 周期性去已经设置过过期时间的key里面去选择一部分已经失效的key进行删除(周期性的去随机测试一些key,每秒钟会进行十次这样的操作)

1、随机测试20个带有timeout信息的key

2、会从这20个中删除已经过期的key

3、如果超过25%的key被删除,则重复执行整个流程

发布订阅

pub/sub

发送端针对通道去发送一个消息,然后接收端去订阅这个通道,然后可以接收到这样一个信息内容

publish channel message 发布消息

subscribe channel 订阅消息

这种订阅和消息中间件的订阅差异在哪?

1、在redis里面pub/sub不是一个专业的、专门的消息中间件,它只是提供了这样的一个功能而已

2、一般的消息中间件的使用条件,希望它能够支持不同的协议、持久化、数据回滚和可靠性的一个保障, 这是正常的消息中间件所具备的功能,但是redis并不具备,所以如果有这些需求的场景下就不能使用redis这种pub/sub方式

Redis持久化及原理分析

redis什么时候持久化?

1、希望把数据做持久化

2、希望redis是一个高效的或者大规模分布式应用缓存服务器的话,如果因为redis的一个数据失效或者数据的丢失导致的因为本身利用redis的缓存去完成的一些高性能操作,最后因为缓存失效导致所有的压力全部抛到后端的服务器,比如数据库服务器,这个时候会引发雪崩问题

redis提供了两种持久化方式:
一、RDB(快照):当符合【条件】的时候,fork子进程,通过子进程把数据写入到一个临时文件,等到整个持久化过程结束以后,它会把临时文件,如果上一次已经有一个持久化文件,它会去替换,如果没有直接会生成这样一个dump.rdb快照文件,对某一个时间点的数据做一个快照

缺点:两次快照间隔的事件会产生数据丢失

四种条件产生快照

1、配置规则

save seconds changes

save 900 1 在900s或15分钟之内有一个以上的key被更改过得话,会去触发一个快照
save 300 10 在300s之内有10个key被更改过得话,会去触发一个快照
save 60 10000

条件是或的关系,不是且的关系,只要满足其中之一就会触发快照

2、通过用户主动去执行save或者 ; 会阻塞所有客户端的请求,不要在客户端请求比较大的时候执行

​ bgsave;在后台异步的去做一个快照的任务,不会阻塞当前的客户端请求

3、flushall 清除所有内存数据,执行了这个命令之后,只要redis中配置了快照(save)规则存在的话,就会去执行一次快照操作

4、执行复制操作,通过主从复制去完成一个同步,master-slave

产生一个问题:这个阶段的快照刚执行完到下一次执行之前这个空档期,这个空档期数据会丢失

缺点:数据丢失

二、AOF(Append Only Fair)【默认关闭】 类似日志记录的一种方式

执行把数据变更的数据会写入到磁盘里,这个过程会降低redis的性能

优点: 通过修改配置规则保证数据尽可能少地丢失,丢失范围降到最低的

缺点: 导致性能开销可能会比较大,每次去写入一个事务操作的时候,会先写入到一个文件里面,这个文件同步会先从操作系统的磁盘缓存再去写入到文件里面

开启AOF:

vim …/redis.conf

appendonly no参数修改为yes

aof文件的写入,只针对数据变更的操作

aof文件的重写,某一时间点,对当前内存中的指令重新生成aof文件,类似于快照的方式,会遍历当前内存中的数据,逐个地写到aof文件当中,这个过程是异步的,通过fork子进程的方式去完成的

重写缓存,在aof文件的重写过程中新产生的redis数据,aof文件重写完之后把重写缓存中的数据追加达到aof文件中

什么时候同步到文件里面?

修改redis.conf中的appendfsync属性,

always:每次操作都执行同步,对数据最安全的,但是性能比较慢

everysec:每秒执行一次,默认的

no:不主动执行,操作系统自己去完成

redis特性:

可以开启两种方式,AOF开启的情况下,默认会从.aof中加载数据

如果redis的使用场景对数据的安全性要求非常高的时候,可以使用两种,为什么使用两种呢?

1、可以保证数据的安全性,aof是一个每秒同步的,所以它的数据安全性会比较高点

2、rdb文件的恢复速度和快照的方式比较方便,一个备份方式和恢复速度会比aof速度要快

Redis的内存回收策略

LRU

物理内存是1G,随着业务增长数据超过1G,但是仍然可以正常运行,因为操作系统里面可见内存并不是受限于物理内存,还有一个虚拟内存的概念,物理内存不够时,操作系统会去磁盘划分一块空间去构成一个虚拟内存,这个是操作系统的一个优化

redis.conf中内存回收策略配置属性(maxmemory-policy):

noeviction(默认),表示当内存达到预值的时候,去申请内存操作的时候会报错

allkeys-lru,最少使用的数据去淘汰

allkeys-random,随机去移除某些key,当应用对于缓存访问本身也是一个概率性事件的时候使用这种策略

从已经设置了过期时间的key中去选择

volatile-random(从已经设置了过期时间的一个数据集里面随机淘汰)

volatile-lru(从已经设置了过期时间的数据集里面挑选最近使用比较少的数据淘汰)

volatile-ttl(即将过期的数据进行淘汰)

面试题:你怎样保证redis里面都是热点数据?

答:使用maxmemory-policy volatile-lru内存回收配置

实际上redis中实现的lru并不是一个非常可靠的lru,使用的是lru算法去完成淘汰,但是这个淘汰并不一定是真正符合最少使用的数据,试想一下,当我们数据量很大的时候,怎样从里面筛选出一定全是最少使用的,如果完全要满足这个条件的话,性能是很低的,所以为了权衡,只通过采样的方式进行随机抽样,放弃了完整的搜索,而是从里面采取一些满足于当前规则的key去做一个淘汰

什么时候触发淘汰(及lru)?

答:本身的内存和你实际上使用的内存达到预值的时候,没有办法去继续申请新的内存去存储数据的时候,会触发lru操作

怎么样去提高命中率?

答:高频次访问的一些数据需要去统计,统计完之后预热放到缓存里面,可以增加存储容量,高频访问的词全部持久化到内存里面。

Redis单线程为什么性能很高?
为什么redis使用单线程?
  • 可以通过单线程处理客户端所有的并发请求,并不是一个一个排队访问,而是用到多路复用技机制;让所有操作可以按照顺序线性去执行,但是它不会去阻塞,可以在同一个时刻去处理很多的io请求
  • 多线程的好处在于充分去利用CPU本身多核心的资源,但是redis本身瓶颈不在于CPU资源的利用,它主要瓶颈在于内存和网络的带宽,然而内存操作效率很高
  • 可以避免线程的一个竞争和上下文切换的一个开销(单线程的好处),但是如果有CPU有多个核心就不能好好利用CPU的资源(劣势)

提醒:设置redis的key时,不建议把key设置很长,增加网络传输的负担,对性能产生影响

同步阻塞、同步非阻塞、异步阻塞、异步非阻塞区别?

答:同步和异步:指的是用户线程和内核的交互方式

​ 阻塞和非阻塞:指的是线程调用内核做一些io操作的时候,是一个阻塞还是非阻塞的情况

​ 阻塞IO:用户线程请求内核空间,内核空间收到请求处理数据直到数据处理完成返回,此时内核空间处理数据这段时间,用户线程处于阻塞状态

​ 非阻塞IO:用户线程请求内核空间,内核空间收到请求并直接返回结果,没有准备好返回的数据是没有的,所以在用户线程这边会不断地去轮询,向内核空间发送请求去获取结果,直到数据读取成功(此时表示内核空间数据准备好了),这个时候整个IO处理过程就会结束,这种方式会浪费CPU资源,不断地无效地循环(缺点)

​ 异步阻塞IO:IO多路复用,用户线程请求内核空间后不会阻塞,而是监视,相当于被动的,当内核空间数据准备好以后会通知用户线程哪一个操作是可以进行的。这样可以保证不需要循环的去做一个无效的调用;可以有效的获取每一次请求(相当于解决了上面阻塞和耗资源的缺点),相当于一个事件的操作

​ 多路复用的优点:一个用户可以在一个线程内同时处理多个请求的,有n个请求过来都可以丢给后面操作去处理,什么时候处理成功了会有一个通知告知

Lua脚本在Redis中的应用

redis的缺点?
  • 不能保证原子性
  • 进行存在数据依赖的操作,网络会影响整个吞吐量
  • 复用性:redis目前指令满足不了需求,需要更灵活化处理,希望多个指令复合形成一个新的指令,这个时候可以通过某种方式去写这样一个新的指令存到redis里面,下次再用的时候可以通过这种方式使用

redis对lua脚本的集成是非常好的,我们可以在redis客户端使用lua脚本去开发一些相应带有业务逻辑的功能,能够解决上面所诉的问题

Lua:轻量级脚本语言,可以嵌入在我们应用程序里面以提供更灵活的功能

在lua脚本中去执行redis的命令

redis.call(‘set’,‘gupao’,‘123’);

local val = redis.call(‘get’,‘gupao’);

eval去执行lua脚本

lua里面通过redis.call()调通redis

eval “return redis.call(‘get’,‘gupao’)” 0

eval “return redis.call(‘set’,KEYS[1],ARGV[1])” 1 asf val

编写firstDemo.lua,通过./redis-cli --eval firstDemo.lua (后面可以接参数)执行

1、可以让脚本设置摘要,通过应用执行lua脚本减少网络传输的开销

把多个操作放到一个lua脚本中去执行

script load “return redis.call(‘get’,‘gupao’)”

然后生成摘要:“bb1e196eaecc630cec338f038c0ba2549834175e”

摘要的值会缓存到脚本缓存里面,下次执行的时候不需要传递整个脚本值,

通过evalsha “bb1e196eaecc630cec338f038c0ba2549834175e” 0 执行摘要可以

达到执行lua脚本的效果

2、满足原子性

通过script kill 停止执行

lua脚本保证原子性的操作 shutdown nosave

3、复用性

比如说定义一些公共的操作,把这个脚本作为对某一个业务场景实现的操作,脚本是可以复用的,

如果其他的场景有涉及到该功能的时候就可以直接复用,类似用我们自己去开发了一些redis命令

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值