• Redis阻塞


    Redis是典型的单线程架构,所有的读写操作都是在一条主线程中完成的。
    当Redis用于高并发场景时, 这条线程就变成了它的生命线。
    如果出现阻塞,哪怕是很短时间 对于我们的应用来说都是噩梦。
    导致阻塞问题的场景大致分为内在原因和外在原因:
    ·内在原因包括: 不合理地使用API或数据结构、 CPU饱和、 持久化阻塞等。
    ·外在原因包括: CPU竞争、 内存交换、 网络问题等。

    内存原因

    API或数据结构使用不合理

    1.如何发现慢查询

    Redis原生提供慢查询统计功能, 执行slowlog get {n}命令可以获取最近的n条慢查询命令, 默认对于执行超过10毫秒的命令都会记录到一个定长队列中,线上实例建议设置为1毫秒便于及时发现毫秒级以上的命令。 如果命令执行时间在毫秒级,则实例实际OPS只有1000左右。 慢查询队列长度默认128,可适当调大。

    慢查询本身只记录了命令执行时间,不包括数据网络传输时间和命令排队时间,因此客户端发生阻塞异常后, 可能不是当前命令缓慢,而是在等待其他命令执行。 需要重点比对异常和慢查询发生的时间点, 确认是否有慢查询造成的命令阻塞排队。

    慢查询优化:

    1) 修改为低算法度的命令, 如hgetall改为hmget等, 禁用keys、 sort等命令。
    2) 调整大对象: 缩减大对象数据或把大对象拆分为多个小对象, 防止一次命令操作过多的数据。 大对象拆分过程需要视具体的业务决定, 如用户好友集合存储在Redis中, 有些热点用户会关注大量好友, 这时可以按时间或其他维度拆分到多个集合中。

    2.如何发现大对象

    Redis本身提供发现大对象的工具, 对应命令: redis-cli -h {ip} -p {port} --bigkeys。

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

    CPU饱和

    单线程的Redis处理命令时只能使用一个CPU。

    而CPU饱和是指Redis把单核CPU使用率跑到接近100%。

    使用top命令很容易识别出对应Redis进程的CPU使用率。

    CPU饱和是非常危险的, 将导致Redis无法处理更多的命令, 严重影响吞吐量和应用方的稳定性。


    持久化阻塞

    持久化引起主线程阻塞的操作主要有: fork阻塞、 AOF刷盘阻塞、HugePage写操作阻塞。

    1.fork阻塞

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

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

    2.AOF刷盘阻塞

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

    当硬盘压力过大时,fsync操作需要等待,直到写入完成。 如果主线程发现距离上一次的fsync成功超过2秒, 为了数据安全性它会阻塞直到后台线程执行fsync操作完成。

    这种阻塞行为主要是硬盘压力引起, 可以查看Redis日志识别出这种情况, 当发生这种阻塞行为时, 会打印如下日志:

    Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.

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

    3.HugePage写操作阻塞

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

    对于开启Transparent HugePages的操作系统, 每次写命令引起的复制内存页单位由4K变为2MB, 放大了512倍, 会拖慢写操作的执行时间, 导致大量写操作慢查询。 例如简单的incr命令也会出现在慢查询中。

    外在原因

    CPU竞争

    CPU竞争问题如下:

    ·进程竞争: Redis是典型的CPU密集型应用, 不建议和其他多核CPU密集型服务部署在一起。 当其他进程过度消耗CPU时, 将严重影响Redis吞吐量。 
    可以通过top、 sar等命令定位到CPU消耗的时间点和具体进程, 这个问题比较容易发现, 需要调整服务之间部署结构。 ·绑定CPU: 部署Redis时为了充分利用多核CPU, 通常一台机器部署多个实例。 常见的一种优化是把Redis进程绑定到CPU上, 用于降低CPU频繁上下文切换的开销。
    这个优化技巧正常情况下没有问题, 但是存在例外情况,当Redis父进程创建子进程进行RDB
    /AOF重写时, 如果做了CPU绑定,会与父进程共享使用一个CPU。
    子进程重写时对单核CPU使用率通常在90%以上, 父进程与子进程将产生激烈CPU竞争, 极大影响Redis稳定性。 因此对于开启了持久化或参与复制的主节点不建议绑定CPU。


    内存交换

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


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

    1) 查询Redis进程号:

    # redis-cli -p 6383 info server | grep process_id
    process_id:4476

    2) 根据进程号查询内存交换信息:

    # cat /proc/4476/smaps | grep Swap
    Swap: 0 kB
    Swap: 0 kB
    Swap: 4 kB
    Swap: 0 kB
    Swap: 0 kB
    .....

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

    预防内存交换的方法有:

    ·保证机器充足的可用内存。
    ·确保所有Redis实例设置最大可用内存(maxmemory),防止极端情况下Redis内存不可控的增长。
    ·降低系统使用swap优先级, 如echo10>/proc/sys/vm/swappiness。


    网络问题

    网络问题经常是引起Redis阻塞的问题点。 常见的网络问题主要有: 连接拒绝、 网络延迟、 网卡软中断等。

    1.连接拒绝

    当出现网络闪断或者连接数溢出时, 客户端会出现无法连接Redis的情况。 我们需要区分这三种情况: 网络闪断、 Redis连接拒绝、 连接溢出。

    第一种情况: 网络闪断。

    一般发生在网络割接或者带宽耗尽的情况, 对于网络闪断的识别比较困难, 常见的做法可以通过sar-n DEV查看本机历史流量是否正常, 或者借助外部系统监控工具(如Ganglia) 进行识别。
    具体问题定位需要更上层的运维支持, 对于重要的Redis服务需要充分考虑部署架构的优化, 尽量避免客户端与Redis之间异地跨机房调用。

    第二种情况: Redis连接拒绝。

    Redis通过maxclients参数控制客户端最大连接数, 默认10000。 当Redis连接数大于maxclients时会拒绝新的连接进入,info stats的rejected_connections统计指标记录所有被拒绝连接的数量。
    Redis使用多路复用IO模型可支撑大量连接, 但是不代表可以无限连接。 客户端访问Redis时尽量采用NIO长连接或者连接池的方式。

    第三种情况: 连接溢出。

    这是指操作系统或者Redis客户端在连接时的问题。 介绍两种常见原因: 进程限制、backlog队列溢出。
    1) 进程限制 客户端想成功连接上Redis服务需要操作系统和Redis的限制都通过才可以。 操作系统一般会对进程使用的资源做限制, 其中一项是对进程可打开最大文件数控制, 通过ulimit-n查看, 通常默认1024。
    由于Linux系统对TCP连接也定义为一个文件句柄, 因此对于支撑大量连接的Redis来说需要增大这个值, 如设置ulimit-n65535, 防止Too many open files错误。 (2) backlog队列溢出 系统对于特定端口的TCP连接使用backlog队列保存。 Redis默认的长度为511, 通过tcp-backlog参数设置。 如果Redis用于高并发场景为了防止缓慢连接占用, 可适当增大这个设置, 但必须不大于操作系统允许值才能生效。 如果怀疑是backlog队列溢出, 线上可以使用cron定时执行netstat-s|grep overflowed统计, 查看是否有持续增长的连接拒绝情况。

    2.网络延迟

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

    常见的物理拓扑按网络延迟由快到慢可分为: 同物理机>同机架>跨机架>同机房>同城机房>异地机房。 但它们容灾性正好相反, 同物理机容灾性最低而异地机房容灾性最高。

    Redis提供了测量机器之间网络延迟的工具, 在redis-cli -h {host} -p {port}命令后面加入如下参数进行延迟测试:

    ·--latency: 持续进行延迟测试, 分别统计: 最小值、 最大值、 平均值、采样次数。
    ·--latency-history: 统计结果同--latency, 但默认每15秒完成一行统计,可通过-i参数控制采样时间。
    ·--latency-dist: 使用统计图的形式展示延迟统计, 每1秒采样一次。

    网络延迟问题经常出现在跨机房的部署结构上, 对于机房之间延迟比较严重的场景需要调整拓扑结构, 如把客户端和Redis部署在同机房或同城机房等。

    带宽瓶颈通常出现在以下几个方面:

    ·机器网卡带宽。
    ·机架交换机带宽。
    ·机房之间专线带宽。


    带宽占用主要根据当时使用率是否达到瓶颈有关, 如频繁操作Redis的大对象对于千兆网卡的机器很容易达到网卡瓶颈, 因此需要重点监控机器流量, 及时发现网卡打满产生的网络延迟或通信中断等情况, 而机房专线和交换机带宽一般由上层运维监控支持, 通常出现瓶颈的概率较小。

    3.网卡软中断

    网卡软中断是指由于单个网卡队列只能使用一个CPU, 高并发下网卡数据交互都集中在同一个CPU, 导致无法充分利用多核CPU的情况。 网卡软中断瓶颈一般出现在网络高流量吞吐的场景。

  • 相关阅读:
    Web安全实践
    认证授权的设计与实现
    Elasticsearch 分页查询
    【算法】三色旗
    【转】互联网项目中mysql应该选什么事务隔离级别
    Elasticsearch 聚合
    Elasticsearch 结构化搜索、keyword、Term查询
    Elasticsearch 单字符串多字段查询
    Elasticsearch 复合查询——多字符串多字段查询
    JavaScript 原型与原型链
  • 原文地址:https://www.cnblogs.com/MacoLee/p/14140234.html
Copyright © 2020-2023  润新知