《Redis开发与运维》阅读日记-2019.2.13

上一篇日记中提到,Redis执行速度很快,一方面原因是因为Redis是将数据存储在内存中,内存的读取速度大约为100ns另一方面就是,“Redis”源码是用C语言进行编写,众所周知,C语言实现的程序“距离”操作系统更近,执行速度相对会更快一些。由于Redis是单线程架构设计而成,这样做预防了多线程可能产生的竞争问题,但是也会出现阻塞严重情况。今天对Redis阻塞的情况,将引起阻塞的内在和外在原因进行记录。

“Redis的噩梦:阻塞”

开头简单提到,由于Redis是典型的单线程架构,所有的读写操作都是在一条主线程中完成的。在高并发场景时,这条线程就变成了它的生命线。如果出现阻塞,哪怕是很短时间,对于我们的应用来说都是噩梦。

刚才就发生一个噩梦,在记录日记的时候,被同事叫走,处理docker容器故障。呃呃 ┭┮﹏┭┮。

继续总结,导致阻塞问题的场景大致分为内在原因和外在原因。

内在原因:不合理使用API或数据结构、CPU饱和、持久化阻塞等

外在原因:CPU竞争、内存交换、网络问题等

发现阻塞

当Redis阻塞时,线上应用服务应该最先感知到,这时应用方就收到大量Redis超时异常,常见的做法是在应用方加入异常统计并通过邮件/短信/微信报警,以便即使发现通知问题,借助日志系统监控

开发提示:借助日志系统统计异常的前提是,需要项目必须使用日志API进行异常统一输出

监控系统所监控的关键指标有很多,命令耗时、慢查询、持久化阻塞、连接拒绝、CPU/内存/网络/磁盘使用过载等。

内在原因:

   API或数据结构不合理

    通常Redis执行命令速度非常快,但也存在例外,如对一个包含上万个元素的hash结构执行hgetall操作,由于数据量比较大且命令算法复杂度是O(n),这条命令执行速度必然很慢,这个问题就是典型的不合理使用API和数据结构。

    如何发现慢查询

        Redis原生提供慢查询统计功能,执行slowlog get{n}命令可以获取最近的n条慢查询命令,默认对于执行超过10毫秒的命令都会记录到一个定长队列中,线上实例建议设置为1毫秒便于及时发现毫秒级以上的命令。

        发现慢查询后,按照以下两个方向去调整:

        1 修改低算法度的命令,例如hgetall改为hmget等,禁用keys、sort等命令

        2 调整大对象:缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。

             大对象拆分过程需要视具体的业务决定,如用户好友集合存储在Redis中,有些热点用户会关注大量好友,这时可以按时间或其他纬度拆分到多个集合中

        3 如何发现大对象

            redis-cli --bigkeys

           内部原理采用分段进行scan操作,把历史扫描过的最大对象统计出来便于分析优化

   CPU饱和的问题

 CPU饱和

        单线程的Redis处理命令只能使用一个CPU。而CPU饱和是指Redis把CPU使用率跑到接近100%。使用too命令很容易识别出对应Redis进程的CPU使用率

        redis-cli --stat 获取当前redis使用情况,该命令每秒输出一行统计信息



   持久化相关的阻塞     

 持久化阻塞

        开启了持久化功能的Redis节点,需要排查是否是持久化导致的阻塞。持久化引起主线程阻塞的操作主要有:fork阻塞、AOF刷盘阻塞、HugePage写操作阻塞

        1 fork阻塞

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

            可以执行info stats命令获取到latest_fork_usec指标,表示redis最近一次fork操作耗时

        2 AOF刷盘阻塞

            当我们开启AOF持久化功能时,文件刷盘的方式采用每秒一次,后台线程每秒对AOF文件做fsync操作。

            可以查看info persistence统计中的aof_delayed_fsync指标

            硬盘压力可能是redis进程引起的,也可能是其他进程引起的,可以使用iotop查看具体是哪个进程消耗过多的硬盘资源

        3 HugePage写操作阻塞

            子进程在执行重写期间利用Linux写时复制技术降低内存开销,因此只有写操作时Redis才复制要修改的内存页

外在原因

CPU竞争

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

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


 内存交换

        内存交换对于Redis来说你非常致命的,Redis保证高性能的一个重要前提是所有的数据都在内存中。

        1 查询Redis进程号 redis-cli -p 6383 info server|grep process_id

        2 根据进程号查询内存交换信息 cat /proc/4476/smaps | grep Swap

            如果交换量都是0KB或者个别是4KB,则是正常现象,说明Redis进程内存没有被交换。

            预防内存交换的方法有:

                保证机器充足的可用内存

                确保所有Redis实例设置最大可用内存,防止极端情况下Redis内存不可控的增长

                降低系统使用swap优先级

  网络问题

        1 连接拒绝

            当出现网络闪断或者连接数溢出时,客户端会出现无法连接Redis的情况。

              第一种情况:网络闪断。一般发生在网络割接或者带宽耗尽的情况,对于网络闪断的比较困难

              第二种情况:Redis连接拒绝。Redis通过maxclients参数控制客户端最大连接数,默认10000。

              第三种情况:连接溢出。指操作系统或者Redis客户端在连接时的问题。

                    1 进程限制 2 backlog队列溢出

        2 网络延迟

            网络延迟取决于客户端到Redis服务器之间的网络环境。主要包括它们之间的物理拓扑和带宽占用情况

        3 网卡软中断

            网卡软中断指由于单个网卡队列只能使用一个CPU ,高并发下网卡数据交互都集中在同一个CPU,导致无法充分利用多喝CPU的情况


人们起点不同,路径不同,乃至遭遇不同,命运不同。有人认命,有人顺命,有人抗命,有人玩儿命,希望和失望交错迭生,倏尔一生

你可能感兴趣的:(《Redis开发与运维》阅读日记-2019.2.13)