分布式缓存技术-redis高级篇 (redis为啥这么快?同事又删库跑路咋办?没关系,教你)

18 篇文章 0 订阅
13 篇文章 0 订阅

一、发布订阅模式

 下面简单介绍使用方式,是不是感觉有点像MQ 他确实可以做但是性能,并发度不如MQ然后也没有做持久话,很鸡肋是吧,这里稍微提下是因为它会在内部的功能中被用到 分布式篇中会说

1.1 直接订阅与发送

如下是订阅指定频道后 然后发送消息的命令

1.2 按规则订阅然后发送

二、Redis事务

下面看看就好,这玩意有点鸡肋

2.1 事务相关命令

我们都知道redis中单个命令都是原子性的不存在并发的问题,那么我们想要多个命令全部执行成功或者失败咋办呢?这个时候就涉及到事务了

 主要有如下几个命令 跟那啥数据库的 begin commit rollback有点像

猜猜如下 命令执行完后控制台的结果

 

 这里使用了exec提交了事务 所以 网络没有故障的情况下 tom减去100变成900 mic加上100变成1100

然后是 watch 这个命令啥意思呢 

 如下 我给 balance设置值为 1000 然后watch它

然后重开一个窗口 减去100 变为 900 

再到本窗口 开启事务 针对 balence的一切操作都会失效 这就相当于个乐观锁 监视的时候它的值是

1000那么当前事务就只能在它是1000的时候修改 当被改成900后 再修改 就没反应了

 

2.2 redis事务中没有回滚

关于异常 redis是怎样处理的呢? 

再看下下面俩图  其实图一(语法错误)有点像我们的预编译异常 图二(String类型当Hash用)则像 运行时异常

而预编译异常(语法错误) 给你编译的时候就报错了 自然 指令也就没有执行,而运行时异常 执行 了才知道有没异常 而遇到这种异常 则是不能回滚滴

 看看redis官方的解释,这是你程序员的锅,上线前这问题都发现不了 搞个锤子 因为redis不需要回滚机制 也是它简单 快速的原因之一

 那既然事务不能保证原子性 要咋保证呢?还有啥方法没 这个时候lua脚本登场了

三、Lua脚本

3.1介绍

当某个客户端执行lua脚本中命令时,其他客户端的命令会被阻塞 

3.2 命令

3.2.1 eval

helloworld登场!语法是    eval 脚本 参数个数 参数值

3.2.2 redis.call(...)

那要在lua脚本中执行 redis命令咋办呢?不像上面的return 我要执行个 set命令 如下

使用redis.call(...)

带参数版本

 3.2.3 调用lua脚本

前面我们都是写的直接在指令中操作 ,正常来说,我们是把逻辑操作放在脚本文件中的

然后调用

如下案例

 lua代码 如下

 测试

我们知道脚本在客户端 每次服务端去执行是不是都要io影响性能呢? 所以就把它缓存起来

 

 

 kill

还有一点 ,都知道redis中lua脚本执行时 其他客户端 脚本是不能执行的 那么 万一一段lua脚本执行太久 比如 一万年 想要停掉咋办 

如下 不可能一直让它执行把

可以看到在上面那客户端 lua脚本还在执行的时候  另一个客户端 他里面是无法执行命令的,然后建议也是把它kill掉

 现在执行script kill 成功停止了那段 永远执行的脚本

shutdown nosave

 但是 那段脚本是简单的 如果 一段脚本中 有许多指令 我想要他们全部失败咋做呢?

比如这个

 使用这个 命令 shutdown nosave

 四、Redis为什么这么快

可以看到跟官网说的10万QPS差不了多少 

那它为啥这么快呢?

4.1 纯内存 KV

为啥 使用内存存储就快呢? 是因为我们的程序都是运行在内存中的 数据库的话 数据是存在磁盘中 省去了 IO的性能消耗 所以快

使用内存为什么快这里

4.2 单线程

 为什么用单线程?

 单线程的话不会浪费CPU的资源吗

看官网作者的回答,说 单线程已经够用了

单线程不会成为redis的瓶颈 最可能成为瓶颈的是 你的内存与带宽 ,所以单线程的话就不要在生产环境中的redis中执行一些耗时非常长的命令 如果某些情况下真的单核不够了 可以 集群 下篇redis的分布式会说

4.3 同步非阻塞I/O --多路复用

如下图 主存就是我们说的 内存  而硬盘 我们可以把它叫做辅存 

主存可以把它当成一个很长的数组,一个字节一单元 所以主存可以被划分为很多很多的单元,每一个单元都有个地址 ——物理地址  如果cpu使用内存 都需要 使用物理地址去访问 我们都知道操作系统可以有多个用户 而 每次又有多个进程同时运行 那么 进程之间 如果 都使用 物理地址访问

主存 从而瓜分主存 主存就不够用了 ,而如果 某块主存 在不同时刻被不同进程使用 有可能产生很多问题 

 所以就多了个中间者 MMU(内存管理单元) 负责管理分配内存

每个进程在被创建的时候 都会给他分配个虚拟地址 通过映射的方式去操作,在windows环境上

你的电脑买来是不是电脑C盘就被用了很多个G 这就大部分是虚拟内存 32位操作系统是 2^32 4G 

64位就是 2^64 1024*1024 TB吗? 显然不需要

linux环境一般会采用低48位做虚拟地址空间 2^48 256T

 总结:为啥需要这个虚拟地址空间呢? 它可以把同一块地址映射到不同的虚拟地址空间 在不同的进程之间实现一个内存的共享 又可以把物理内存做一个隔离 实现 不同进程不会修改别人的数据,虚拟内存 更大 而且地址空间连续

在linux环境下虚拟内存又被划分为了两块,为什么划分为两块呢?就跟男女生宿舍样

用户空间放的是用户的代码和数据 内核空间放的是系统的代码和数据 ,如果你的进程在用户空间的话 我们就叫做用户态 如果是内核空间的话 就是 内核态 内核空间可以直接访问系统的资源,

而用户空间想要访问系统资源的话就必须要调用系统的接口

 他们其实是肉眼不可见的并发执行,只是cpu时间片上下文切换速度太快 你无法感知罢了

 

 linux操作系统内一切皆文件,而执行系统的调用操作 就通过文件描述符 (相当与一套操作io系统调用的一套规范) 

 如果是传统的IO数据拷贝 那么需要 操作系统将磁盘的数据拷贝到内核缓冲区中 然后 用户空间使用系统接口进行调用 内核空间数据准备好后再把数据从内核缓冲区复制到用户缓冲区,这里面就出现了多次的数据拷贝与进程状态的切换

阻塞 

而用户调用系统接口时,数据从硬盘到内核缓冲区 与 内核缓冲区 到 用户缓冲区 这两个 步骤的时候 用户进程都会被阻塞 那么 怎样解决呢?

服务端创建多个线程?这样并发度高的时候 顶不住

客户端不断轮询? 轮询频率高消耗客户端性能,低的化页难等 

 服务端咋解决 ?终于到了主题

 多路复用

这个需要操作系统支持,通过一个多路复用程序对客户端的socket连接进行管理

 

 五、Redis内存回收

5.1 过期策略

常见的过期策略有以下三种,而redis采用的其中两种的结合 

立即过期:过期时间一到立马回收,能支持这样就必须针对每个设置了过期时间的键值对都有个任务在跑,到了过期时间就回收,这样无疑极度消耗性能

惰性过期:我回收你,在当你再次访问的时候 才去判断你是否到了过期时间,到了就回收

定期过期:有个定时任务一直在跑 扫描所有键值对,发现过期了的就回收

想到了是哪两种结合了把? 没错就是 第二种和第三种 

 而redis的键值对都是存在内存中的,这个内存容量大小咋设置呢?

在配置文件中有个maxmemory 如果不设置 32位操作系统就是3G 64就不限制 

 还可以在控制台设置 如下

 纵然我们前面设置了过期策略,但是 回收跟不上新增的速度时 内存满了咋办?

就只能淘汰了... 淘汰策略分别为下面这些 

默认过期策略是 不淘汰,就是内存满了就不让你新增了

我们可以设置为LRU 这种过期策略很常见 没听过的可以去补一补 ,很简单

就是链表 里面存放所有的数据节点 每当某个节点新增进来或者被访问时 就把该节点放到 链表头部

而后面的节点依次往后挪 操作链表长度时 末尾的节点被删除 如下 

而redis本身就有 自己的数据结构 kv难道还要放到别的地方判断然后删除么?

所以它进行了改良,它是将 该kv对象 最近访问时间存放了起来 每次被访问时修改 然后

每隔 100ms又会更新系统当前时间到它的一个全局变量内 (如果每次都要访问系统接口获取当前时间无疑会太消耗性能了)

然后比较这两者的差值 越大 说明 这个 kv对象越久没有被访问,也就是最近一次 

但是 如果遇到了这样一个情况,如下 我淘汰时刚好 最近访问时间B是最近的,这样会淘汰

有点不太合适 所以 又加了一个 频率低优先淘汰的策略 也就是 LFU 

LFU

而频率不是访问一次频率+1 这样系统顶不住 会设置一个频率增长因子 在达到固定访问次数的时候频率才会增加到某个值 如下  

 然后 频率如果只增加不减少也不行 如下 频率降低因子也可以设置

六、Redis持久化机制

因为redis中数据都放在内存中 所以 宕机或者断电会导致数据丢失 所以需要 有一套持久化的方案

如下 前者记录某一时刻redis所有数据的快照  后者记录操作命令 的日志

  如果两种都开启了,后者优先

数据快照的生成又分为自动触发手动触发

自动触发:如下在配置文件redis.conf中 有这样几个配置 意思分别是 900秒之内有1个key修改了 就生成1个RDB 上300秒内有10个key修改了 就生成1个RDB 后面也同理,这文件内还有些关于RDB的配置,比如RDB是否使用压缩算法,RDB存放的路径,RDB的文件名(默认叫dump.rdb)等等 一般使用默认配置即可

 然后除了下面这种针对key操作时的触发时机,还有 shutdown 与 flushall 也会理所当然自动保存快照

RDB  手动触发

save的话如果redis数据量比较大,保存快照时会阻塞其他的命令,生产环境一般不用

bgsave 是 fork出一个子进程 然后来处理 

示例:如果redis被删库跑路咋办 

下面是正常关机 我重启redis后 肯定能重新找到数据

 可以看到 我在如下 flushall 后直接shutdown 这个时候重启后显然数据都没了 ,所以

 生产环境我们一般都会自动备份 然后设置权限 如上图 我 在 某个时机已经备份了 

这个时候我发现redis数据没了,所以把 用备份RDB 把数据恢复

RDB的特点

 因为 只会在某些时机生成,比如固定时机段内被修改的键达到某数量 flushall shutdown

所以我们也可以使用AOF (默认是使用RDB) AOF同步有几种方式,第一种没执行一次命令时同步一次 第二种每过一秒同步一次 一般采用第二种 第一种 太消耗性能 第二种可以放定时任务跑

 aof文件中的内容就是 redis的命令 

当AOF文件中数据量很大时 会有个 AOF重写 就是根据当前 redis中的数据去生成这样一些命令

 重写的时机也有两种 一种是到达当前文件大小百分比 另一种是 固定多少兆时 

 

当在到达AOF备份时机时 如果现有数据量很大时需要消耗一定的时间 这时新增过来的数据就没有被重写到AOF文件 所以这时需要先把他们的写命令放到 一个缓存中 前面AOF同步后接着同步这个缓存中的内容

如果能看到这里 你是真滴秀 欢迎关注 B站 请叫我觉哥   我会定期在B站直播陪伴学习

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我才是真的封不觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值