《Redis开发与运维》学习笔记2:阻塞,内存模型以及缓存设计

这篇主要整理第七章,第八章和第十一章的内容,主要是一些缓存的优化内容。主从复制,哨兵和集群下一章节再讲,书中像是持久化,五种基础类型api,运维报警,统计配置等章节不会讲,转载注明出处:https://blog.csdn.net/Koikoi12
在这里插入图片描述

Redis阻塞

Redis是单线程架构,所有读写都在一条主线程完成,因此如果出现了阻塞是一件严重的事情。

1.发现阻塞

当Redis阻塞时,线上应用服务应该最先感知到,这时应用方会收到大量Redis超时异常。

常规做法是在应用方加入异常统计并通过邮件/微信/短信报警,以便及时发现通知问题。

对于开发人员,需要处理如何统计异常以及触发报警的时机。何时报警根据应用的并发量决定。

由于Redis调用API会分散在项目的多个地方,每个地方都监听异常并加入监控代码必然难以维护。这可以借助于日志系统,使用logback或者log4j。当异常发生时,异常信息最终会被日志系统收集到Appender,默认的Appender一般是具体的日志文件,开发人员可以自定义一个Appender,用于专门统计异常和触发报警逻辑。
在这里插入图片描述
书中以java logback作为例子,190页可以自行查看。代码大致干了几件事,就是把某个级别(如error级别)的redis异常捕获,如果报错10次以上,就触发自己写的报警代码。最后一定要remove掉防止内存泄露

如何定位Redis集群中哪个Redis节点(ip和port信息)发生了异常?由于客户端类库都会保存IP和PORT信息,可以在异常发生时打印出对应节点的IP和PORT信息。

修改Redis客户端成本很低,比如Jedis只需要修改Connection类下的connect、sendCommand、readProtocalWithCheckintBroken 方法专门捕获连接、发送命令、协议读取事件的异常。

也可以借助Redis监控系统(如CacheCloud或一些公司内部自行开发的监控系统)监控的关键指标,命令耗时,慢查询,持久化阻塞,连接拒绝、CPU、内存、网络、磁盘使用过载的问题等。

注意:线上Redis没有监控报警,是很不负责任和危险的

2.内在原因

1.API或者数据结构使用不合理

比如hgetall操作 数据量比较大且命令算法复杂度是O(n) 这条命令执行速度必然很慢。

解决方案:

  • 通过$slowlog get 获取慢查询日志
  • $redis-cli -h {ip} -p {port} --bigkeys 发现大对象后进行优化

2.CPU饱和问题

如何排查cpu使用率是否过高:

  • 通过linux的top命令查看redis进程的cpu使用率
  • $redis-cli -h {ip} -p {port} --stat 输入当前rediscpu使用率,该命令会每秒输出一行统计信息

垂直层面的命令优化很难达到效果,这时就需要水平扩展来分摊OPS压力。

如果只有几百或几千OPS的Redis接近CPU饱和是很不正常的,有可能使用了高算法复杂度的命令,需要注意排查。

3.持久化相关的阻塞

3.1. fork阻塞

fork操作发生在RDB和AOF重写时,redis主线程调用fork操作产生共享内存的子进程,由子进程完成持久化文件重写工作。如果fork操作本身耗时过长,必然会导致主线程的阻塞。

可以执行info stats 获取到latest_fork_usec指示,表示redis最近一次fork操作耗时,如果耗时很大,比如超过1秒,则需要作出优化调整,如避免使用过大的内存实例和规避fork缓慢的操作系统等

3.2. AOF刷盘阻塞

文件刷盘采用每秒一次,后台线程每秒对AOF文件做fsync操作。当硬盘压力过大时候,fsync操作需要等待,直到写入完成。如果主线程发现距离数十年该一次的fsync成功超过2秒,为了数据安全性它会阻塞直到后台线程执行fsync操作完成。这种阻塞行为主要是硬盘压力引起,可以查看redis日志识别出这种情况。

也可以查看指令info persistence 统计aof_delayed_fsync指标,每次发生fdatasync阻塞主线程时会累加。

3.3. HugePage 写操作阻塞

子进程在执行重写期间利用Linux写时复制技术降低内存开销,因此只有写操作时候,redis才复制要修改的内存页。只引用书中原话,没接触过不做具体讨论

Redis官方文档列出了阻塞详细的分类说明,感兴趣的可以直接去啃生肉:https://www.redis.io/topics/latency

3.外在原因

1.CPU竞争

进程竞争:Redis是典型的CPU密集型应用,不建议和其他多核CPU密集型服务部署在一起。当其他进程过度消耗CPU时候,将严重影响Redis吞吐量。可以通过top、sar等命令定位到CPU消耗的时间点和具体进程。

绑定CPU:部署Redis时候为了充分利用多核CPU,通常一台机器部署多个实例。常见的一种优化是把Redis进程绑定CPU上,用于降低CPU频繁上下文切花的开销。

在这里插入图片描述
注意:不建议对持久化的Redis进行绑定CPU操作,因为fork出来的子进程会去争抢CPU,极大影响了稳定性

2.内存交换

Redis保证高性能的一个重要前提是所有数据都在内存中,若果操作系统把Redis使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的Redis性能急剧下降。

这一篇主要是linux层面的优化,详细可看书中的第12章

识别Redis内存交换的方法如下:

  1. 查询Redis进程号#redis-cli -p 6383 info server | grep process_id,返回process_id: 4476
  2. 根据进程号查询内存交换信息#cat /proc/4476/smaps | grep Swap

预防内存交换:

  1. 保证机器充足的可用内存
  2. 确保所有Redis实例设置最大可用内存,防止极端情况下内存不可控的增长
  3. 降低系统使用swap优先级

3.网络问题

大致了解下就行,常见问题:

  1. 连接拒绝
  2. 网络延迟
  3. 网卡软中断

内存模型

1.内存消耗

获取内存统计相关指标命令:info memory

内存消耗划分:自身内存(作为Redis空进程,自身消耗非常少)+对象内存+缓冲内存+内存碎片
在这里插入图片描述

  • 对象内存:Redis内存占用最大的一块,存储着用户所有的数据。对象内存消耗可以简单理解为sizeOf(keys) + sizeof(values)
  • 内存碎片:默认的内存分配器采用jemalloc,可选的分配器还有:glibc,tcmalloc。出现高内存碎片的解决办法:1、数据对齐;2、安全重启:将碎片率过高的主节点转为从节点,进行重启。
  • 缓冲内存:客户端缓冲、复制积压缓冲、AOF缓冲
   客户端缓冲:指的是所有接入到Redis服务器TCP连接的输入输出缓冲。输入缓冲无法控制,最大空间为1G,如果超过将断开连接。输出缓冲通过参数client-output-buffer-limit控制。
   复制积压缓冲:主节点的写命令会缓冲到复制积压缓冲区,从节点从中读取,根据repl-backlog-size参数控制,默认1MB。
   AOF缓冲:用于在Redis重写期间保存最近的写入命令。

2.内存管理

限制内存的方法:

  1. 使用maxmemory参数限制最大可用内存
  2. 也可以通过config set maxmemory进行动态修改

限制内存的目的:

  1. 用于缓存场景,当超出内存上限maxmemory时使用LRU等回收策略释放空间
  2. 防止所用内存超过服务器物理内存

注意:maxmemory限制的是Redis实际使用的内存量。由于内存碎片率的存在,实际消耗的内存可能会比maxmemory设置的更大,实际使用时要小心内存溢出。

内存回收策略:

  1. 删除过期键对象
  2. 内存达到上限时触发回收策略

在这里插入图片描述
如果过期键一直在内存中没被删除,就需要用到内存的回收策略,Redis中大部分都是基于LRU实现:逐步解析力扣146. LRU算法(哈希表+双向链表,LinkedHashMap源码解析,Redis内存淘汰机制)
在这里插入图片描述

3.内存优化

优化包括redisObject对象,缩减键值对象,共享对象池,字符串优化,编码优化,控制键数量等方式。

别人整理的内容和书中一致,可以看这篇:《Redis开发与运维》---- 理解内存

缓存设计

书中都是基于优化来实现,目的无非就是加速读写,降低db压力,以及各类问题的解决,需要实践出真知

之前整理的:
缓存穿透、缓存击穿、缓存雪崩区别和解决方案
布隆过滤器实践,缓存穿透的预防及和bitmap的区别
Mysql和Redis数据同步策略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值