iralance / myblog

notes
0 stars 0 forks source link

redis笔记 #5

Open iralance opened 7 years ago

iralance commented 7 years ago

redis使用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。

数据结构和内部编码

每种数据结构都有自己底层的内部编码实现。这样设计有两个好处。第一,可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数据结构和命令。第二,多重内部编码实现可以再不同场景下发挥各自的优势,例如ziplist比较节省内存,但在列表元素比较多的情况下,性能会有所下降,这时候Redis会根据配置选项将列表类型的内部实现转换为linkedlist。

object encoding key

string

  • int:8个字节的长整型
  • embstr:小于等于39个字节的字符串
  • raw:大于39个字节的字符串

    hash

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的时候复杂度为O(1)。

    list

  • ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-emtries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用。
  • linkedlist(链表):当列表类无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。 lpush+lpop = Stack(栈) lpush+rpop = Queue(队列)

    set

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。
  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。

    sortedSet

  • ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。
  • skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降。

    功能

    慢查询分析

    redis客户端执行一条命令分为如下4个部分:

    1. 发送命令
    2. 命令排队
    3. 命令执行
    4. 返回结果 其中1)+4)称为Round Trip Time(RTT,往返时间,下面的pipline会讲到) 需要注意,慢查询只统计步骤3的时间,所以没有慢查询并不代表客户端没有超时时间。
  • slowlog-log-slower-than预设阀值,单位是微妙(1秒=1000毫秒=1000000微妙),默认是10000微妙。
  • slowlog-max-len:慢查询日志最多存储多少条。
    config set slowlog-log-slower-than 20000
    config set slowlog-max-len 1000
    config rewrite

    查询慢日志

    slowlog get [n]

    最佳实践:

  • slowlog-max-len配置建议:线上建议调大慢查询列表,记录慢日志时Redis会对长命令做截断操作,并不会占用大量内存。增大慢查询列表可以减缓慢查询被剔除的可能。
  • slowlog-log-slower-than配置建议:默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值。由于Redis采用单线程相应命令,对于高流量的场景,如果命令执行时间在1毫秒以上,那么Redis最多可支撑OPS不到1000。
  • 慢查询只记录命令的执行时间,并不包括命令排队和网络传输时间。因此客户端执行命令的时间会大于命令实际执行时间。因为命令执行排队机制,慢查询会导致其他命令级联阻塞,因为当客户端出现请求超时,需要检查该时间点是否有对应的慢查询,从而分析出是否为慢查询导致的命令级联阻塞。
  • 由于慢查询日志是一个先进先出的队列,可定期执行slow get命令讲慢查询日志持久化到其他存储中(避免丢失)。

    Redis Shell

redis-cli

Pipline

事务+lua

持久化

RDB

把当前进程数据生成快照保存到硬盘里的过程,触发RDB持久化的过程分为手动触发和自动触发。

  1. 手动触发
    • save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对内存比较大的实例会造成长时间阻塞。
    • bgsave命令:Redis进程fork子进程,RDB持久化过程由子进程负责,完成后自动结束。
  2. 自动触发
    • 使用save相关配置,如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。
    • 如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点。
    • 执行debug reload命令重新加载Redis时,也会自动触发save操作。
    • 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave。

      AOF

      append only file:以独立日志的方式记录每次命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。Redis引入AOF重写机制压缩文件体积。 配置:appendonly yes,默认不开启。

    • RDB使用一次性生成内存快照的方式,产生的文件压缩比高,因此读取RDB速度快。每次生成RDB开销大,无法做到实时持久化,一般用于数据冷备和复制传输。
    • save命令会阻塞主线程不建议使用,bgsave命令通过fork操作创建子进程生成RDB避免阻塞。
    • AOF通过追加写命令到文件实现持久化,通过appendfsync参数可以控制实时/秒级持久化。需要定期执行重写操作来降低文件体积。
    • AOF重写可以通过auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数控制自动触发,也可以使用bgrewriteaof命令手动触发
    • 子进程执行期间使用copy-on-write机制和父进程共享内存,避免内存消耗翻倍。AOF重写期间还需要维护重写缓冲器,保存新的写入命令避免数据丢失。
    • 持久化阻塞主线程场景有:fork阻塞和AOF追加阻塞。fork阻塞时间跟内存量和系统有关,AOF追加阻塞说明硬盘资源紧张。
    • 单机下部署多个实例时,为了防止出现多个子进程执行重写操作,建议做隔离控制,避免CPU和IO资源竞争。

      复制

      常见问题:读写分离、数据不一致、规避全量复制等。

      建立连接

      配置都是从节点发起。
      slaveof {masterHost} {masterPort}
      查看复制相关状态
      info replication

      断开连接

      从节点
      slaveof no one
      断开与主节点复制关系。
      从节点晋升为主节点。

      补充:

  3. 默认情况下,从节点使用salve-read-only=yes配置为只读模式。
  4. 如果部署在不同的机器,需考虑传输延迟。repl-disable-tcp-nodelay参数用于控制是否关闭TCP_NODELAY,默认关闭。如果关闭,实时,但增加网络带宽的消耗。开启后,会合并较小的TCP数据包节省贷款。默认发送时间取决于LInux的内核,一般默认为40毫秒。适用于跨机房部署。
  5. 主从节点建立连接后,通过长连接发送心跳命令。通过client list命令查看复制相关客户端的信息。主flags=M,从flags=S。主节点默认每隔10s对从节点发送ping命令,判断从阶段的存活性和连接状态。参数repl-ping-slave-period控制发送频率。

阻塞

监控点:命令耗时、慢查询、持久化阻塞、连接拒绝、CPU/内存/网络/磁盘使用过载等。 排查: 内在原因

  1. API或数据结构不合理
  2. CPU饱和的原因
  3. 持久化相关的阻塞
    3.1 fork阻塞 发生在RDB和AOF重写时 3.2 AOF刷盘阻塞 当我们开启AOF持久化功能时,文件刷盘的方式一般采用每秒一次,后台线程美妙对AOF文件做fsync操作。当磁盘压力过大时,fsync操作需要等待,直到写入完成。 3.3 HugePage阻塞 外在原因
  4. cpu竞争 1.1 进程竞争 1.2 绑定CPU
  5. 内存交换 操作系统把Redis使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后Redis性能急剧下降。 检查方法:2.1查询Redis的进程号:redis-cli -p 6379 info server | grep process_id 2.2 根据进程号查询内存交换信息:cat /proc/4476/smaps | grep Swap
  6. 网络问题 3.1 连接拒绝 3.2 网络延迟 3.3 网卡软中断

内存

Redis进程内消耗主要包括:自身内存+对象内存+缓冲内存+内存碎片,其中Redis空进程自身内存消耗非常少,通常used_memory_rss在3MB左右,used_memory在800kb左右

  1. 对象内存:sizeof(keys)+size(values)
  2. 缓冲内存:客户端缓冲、复制积压缓冲区、AOF缓冲区
  3. 内存碎片 Redis默认的内存分配器是jemalloc,可选的分配器还有:glibc、tcmalloc。

设置内存上限:maxmemory 1.用于缓存场景,当超出内存上限maxmemory时使用LRU等删除策略释放空间 2.防止所用内存超出服务器物理内存 注意:由于内存碎片率的存在,实际消耗的内存可能回避maxmemory设置的更大,实际使用时要小心这部分内存溢出。

其他