vlambda博客
学习文章列表

Redis基础学习笔记--这一篇差不多了

前言

Redis使用也挺久了,也没用到过高级用法,就是简单的做个缓存。以前也没系统的学过。前几天看了个Redis视频课程,就做了下笔记,大部分都是基础知识,没有涉及到高阶,但是也算是全面的梳理了一遍(排版难受,可以点击原文)。纯手打! 实属不易! 点个赞吧!

String类型

常用命令(点击参考)

  1. set命令设置 key value

    1
    2
    3
    4
    127.0.0.1:6379> set key "Hello"
    OK
    127.0.0.1:6379> set key "hello" ex 1000
    OK
    • EX seconds – 设置键key的过期时间,单位时秒

    • PX milliseconds – 设置键key的过期时间,单位时毫秒

    • NX – 只有键key不存在的时候才会设置key的值

    • XX – 只有键key存在的时候才会设置key的值

  2. get key:获取某个key的value

  3. APPEND key value:追加一个值到key上,如果key不存在则创建一个key

  4. MSET key1 value1 key2 value2]:设置多个 key value

  5. MGET key1 key2:获取多个值,如果key不存在,返回nil

  6. INCR key(只操作整数):执行原子加一操作,如果key不存在,则key从0开始加1.

  7. INCRBY key increment(只操作整数)`:执行原子增加一个值,如果key不存在,key被设置为0,如果数据类型错误返回错误。

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> set num 10
    OK
    127.0.0.1:6379> INCRBY num 2
    (integer) 12
    127.0.0.1:6379> INCRBY num "sss"
    (error) ERR value is not an integer or out of range
  8. DECR key(只操作整数):整数原子减一,如果key不存在,则key从0开始减一,如果数据类型错误,返回错误。

  9. DECRBY key decrement(只操作整数):原子性减指定的数,如果key不存在,则key被置为0,然后相减

    1
    2
    3
    4
    127.0.0.1:6379> set num 10
    OK
    127.0.0.1:6379> DECRBY num 5
    (integer) 5

应用场景

  1. 缓存

Hashes(哈希)

常用命令(点击参考)

  1. HSET key field value:设置hash里面一个字段的值

  2. HGET key field:获取某个字段的值

  3. HDEL key field:删除1个或多个字段的值

    1
    2
    3
    4
    5
    6
    7
    8
    127.0.0.1:6379> HSET sqt name 'listener'
    (integer) 1
    127.0.0.1:6379> HGET sqt name
    "listener"
    127.0.0.1:6379> HDEL sqt name
    (integer) 1
    127.0.0.1:6379> HGET sqt name
    (nil)
  4. HKEYS key:获取 key 所有字段的名字

  5. HVALS key:返回key所有字段的value值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> hset sqt name listener age 20 address '西安'
    (integer) 3
    127.0.0.1:6379> HKEYS sqt
    1) "name"
    2) "age"
    3) "address"
    127.0.0.1:6379> HVALS sqt
    1) "listener"
    2) "20"
    3) "\xe8\xa5\xbf\xe5\xae\x89"
  6. HGETALL key:返回 key 指定的哈希集中所有的字段和值。返回值中,每个字段名的下一个是它的值,所以返回值的长度是哈希集大小的两倍

    应用场景

    1. 个人用户首页访问量

List(列表)

简介

Redis lists基于Linked Lists实现。这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的

常用命令(点击参考)

  1. LSET key index value:设置 index 位置的list元素的值为 value,当index超出范围返回一个error

  2. LPUSH key value1 value2:从队列左边向右边入队一个或多个元素

  3. LPOP key:移除并且返回 key 对应的 list 的第一个元素。

    1
    2
    3
    4
    127.0.0.1:6379> LPUSH sqt name age address
    (integer) 3
    127.0.0.1:6379> LPOP sqt
    "address"
  4. LLEN key:返回存储在 key 里的list的长度。如果 key 不存在,会被看作是空list,并且返回长度为 0。当存储在 key 里的值不是一个list的话,会返回error。

    应用场景

    1. 粉丝列表、关注列表(利用队列的性质)

    2. 实现消息队列

Sets(集合)

简介

不重复且无序的字符串元素的集合

常用命令(点击参考)

  1. sadd key value1 value2:添加一个和多个元素

  2. sinter key1 key2:求交集

  3. SCARD key:集合存储的key的基数 (集合元素的数量)

  4. SREM key member1 member2:移除key集合指定的元素. 如果指定元素不存在则忽略,如果key集合不存在返回0.

  5. SPOP key [count]:从存储在key的集合中移除并返回一个或多个随机元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    127.0.0.1:6379> sadd set 1 2 3 4 5 6
    (integer) 6
    127.0.0.1:6379> SPOP set
    "5"
    127.0.0.1:6379> SPOP set
    "6"
    127.0.0.1:6379> SPOP set
    "3"
    127.0.0.1:6379> SPOP set 2
    1) "4"
    2) "1"
    127.0.0.1:6379>
  6. SRANDMEMBER key [count]:随机返回key集合中的一个元素,不删除

应用场景

  • 共同好友—->sinter key1 key2

  • 抽奖系统—–>SRANDMEMBER key [count]SPOP key [count]

Sorted Sets(有序集合)

简介

类似Sets。但是元素不重复且有序。但是每个字符串元素都关联到一个叫score浮动数值(floating number value)。里面的元素总是通过score进行着排序,所以不同的是,它是可以检索的一系列元素。

基本命令(点击参考)

  1. ZADD key score element:添加一个或者多个元素, 如果元素(element)已存在,则更新并重新排序。如果key不存在,则创建新集合。复杂度 O(logN)

    • XX: 仅仅更新存在的成员,不添加新成员。

    • NX: 不更新存在的成员。只添加新成员。

    • CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,ZADD返回值只计算新添加成员的数量。

    • INCR: 当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作。

  2. ZCARD key:返回 key 集合元素的数量

  3. ZREM key member [member ...]:删除一个或多个元素,当key存在,但是其不是有序集合类型,就返回一个错误。

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> zadd user 100 listener 120 sqt
    (integer) 2
    127.0.0.1:6379> zrem user listener
    (integer) 1
    127.0.0.1:6379> zcard user
    (integer) 1
    127.0.0.1:6379>
  4. ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]:有序集合中指定分数区间内的成员,分数由高到低排序

    • 完整语法示例:ZREVRANGEBYSCORE key max min WITHSCORES LIMIT offset count

    • 指令 是否必须 说明
      ZREVRANGEBYSCORE 指令
      key 有序集合键名称
      max 最大分数值,可使用”+inf”代替
      min 最小分数值,可使用”-inf”代替
      WITHSCORES 将成员分数一并返回
      LIMIT 返回结果是否分页,指令中包含LIMIT后offset、count必须输入
      offset 返回结果起始位置
      count 返回结果数量
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      127.0.0.1:6379> zadd exam_score 100 sqt 110 wby 70 cl 10 zhangsan 33 lisi 66 wangwu 77 chenliu 83 listener
      (integer) 8
      127.0.0.1:6379> ZREVRANGEBYSCORE exam_score 100 5 WITHSCORES
      1) "sqt"
      2) "100"
      3) "listener"
      4) "83"
      5) "chenliu"
      6) "77"
      7) "cl"
      8) "70"
      9) "wangwu"
      10) "66"
      11) "lisi"
      12) "33"
      13) "zhangsan"
      14) "10"
  5. ZREVRANGEBYLEX key max min [LIMIT offset count]:返回指定成员区间内的成员,按成员字典倒序排序, 分数必须相同

    • 完整语法:ZREVRANGEBYLEX key max min [LIMIT offset count]

    • 指令 是否必须 说明
      ZREVRANGEBYLEX 指令
      key 有序集合键名称
      max 字典中排序位置较大的成员,必须以”[“开头,或者以”(“开头,可使用”+”代替
      min 字典中排序位置较小的成员,必须以”[“开头,或者以”(“开头,可使用”-“代替
      LIMIT 返回结果是否分页,指令中包含LIMIT后offset、count必须输入
      offset 返回结果起始位置
      count 返回结果数量

应用场景

  1. 排行榜(根据点赞数、评论数)

  2. 热点数据

集合 VS 有序集合

集合 有序集合
无重复元素 无重复元素
无序 有序
element element + score

列表 VS 有序集合

列表 有序集合
元素可以重复 元素不可以重复
无序 有序
element element + score

慢查询

客户端超时不一定是慢查询,但慢查询是客户端超时的一个因素

生命周期:

1
发送命令->命令排队->命令执行->返回结果

慢查询发生在 命令执行 阶段。

配置

  1. 配置 slowlog-log-slower-than 指定执行时间超过多少微秒的命令会被记录到日志上(建议设置为1000)

  2. 配置slowlog-max-len **指定最多保存多少条慢查询记录.(默认10ms,建议1ms)**

pipeline(流水线)

一次网络时间,多条命令。节省网络传输时间

redis持久化的取舍和选择

持久化方式

快照(RDB)

例如:Redis RDB、Mysql Dump

写日志(AOF)

例如:Redis AOF、Mysql Binlog、Hbase Hlog

RDB

定义

触发机制的三种方式
save 命令(同步)

Redis基础学习笔记--这一篇差不多了

  1. 数据量大的时候会阻塞

  2. 如存在老的RDB文件,新替换老

  3. 复杂度O(N)

bgsave(异步)

Redis基础学习笔记--这一篇差不多了

save 和 bgsave 简单对比
命令 bgsave save
IO类型 异步 同步
阻塞 是(阻塞发生在fork)
复杂度 O(n) O(n)
优点 不阻塞客户端命令 不会消耗额外内存
缺点 需要fork,消耗内存 阻塞客户端命令
最佳配置
1
2
3
4
5
6
7
8
# 下面 3 条 save 命令满足其中任何一个,都会执行 bgsave
save 900 1 # 900秒内 改变超过 1条数据会执行 bgsave
save 300 10 # 300秒内 改变超过 10条数据会执行 bgsave
save 60 10000 # 60秒内 改变超过 10000条数据会执行 bgsave
stop-writes-on-bgsave-error yes # bgsave 发生错误 停止写入
rdbcompression yes # RDB文件是否采用压缩的格式
rdbchecksum yes # RDB文件是否检验
dbfilename dump-${port}.rdb # RDB文件名字
触发机制-不容忽略方式
  1. 全量复制 (比如主从复制的时候)

  2. debug reload

  3. shutdown (关闭的时候,有可能生成 RDB文件)

总结
  1. RDB是Redis内存到硬盘的快照,用于持久化

  2. save命令通常会阻塞Redis

  3. bgsave不会阻塞Redis,但是会fork新的进程

  4. save自动配置满足任一就会被执行

  5. 有些触发机制不容忽视

AOF

AOF运行原理-创建/恢复

Redis基础学习笔记--这一篇差不多了

三种策略
always

每个命令都会进行持久化,数据不会丢失

Redis基础学习笔记--这一篇差不多了

everysec

Redis的默认配置,每一秒执行一次持久化,最多丢失一秒的数据。

Redis基础学习笔记--这一篇差不多了

no

由操作系统法国决定

Redis基础学习笔记--这一篇差不多了

优缺点
命令 always everysec no
优点 不丢失数据 每秒fsync,有可能丢1秒数据 不用管
缺点 IO开销比较大,一般的
sata盘只有几百TPS
丢失一秒数据 不可控
AOF重写
AOF重写作用
  1. 减少硬盘占用量

  2. 加速恢复速度

AOF实现的两种方式
bgrewriteaof

Redis基础学习笔记--这一篇差不多了

AOF重写配置
  1. 配置

    配置名 含义
    auto-aof-rewrite-min-size AOF文件重写需要的尺寸
    auto-aof-rewrite-percentage AOF文件增长率
  2. 统计

    统计名 含义
    aof_current_size AOF当前尺寸(单位:字节)
    aof_base_size AOF上次启动和重写的尺寸(单位:字节)
  3. AOF自动触发时机

    • aof_current_size > auto-aof-rewrite-min-size

    • (aof_current_size - aof_base_size)/aof_base_size > auto-aof-rewrite-percentage

  4. AOF重写流程

            

Redis基础学习笔记--这一篇差不多了

AOF和RDB对比

命令 RDB AOF
启动优先级
体积
恢复速度
数据安全性 丢数据 根据策略决定
轻重
RBD最佳策略
  1. 建议关掉RDB

  2. 集中管理

  3. 主从,从开

AOF最佳策略

  1. 建议开启(缓存和存储)

  2. AOF重写集中管理

  3. 使用everysec

最佳策略
  1. 小分片

  2. 缓存或者存储

  3. 监控(硬盘、内存、负载、网络)

  4. 足够的内存

运维常见问题

fork操作
  1. 同步操作

  2. 与内存量息息相关:内存越大,耗时越长(与机器类型有关)

  3. info:lastest_fork_usec(查看fork执行的时间信息)

改善fork
  1. 优先使用 物理机或者高效支持fork操作的虚拟化技术

  2. 控制Redis实例最大可用内存:maxmemory

  3. 合理配置Linux内存分配策略: vm.overcommit_ory=1

  4. 降低fork频率:例如放款AOF重写自动触发时机,不必要的全量赋值

子进程的开销和优化
CPU
  • 开销:RDB和AOF文件生成,属于CPU密集型

  • 优化:不做CPU绑定,不和CPU密集型的服务一块部署

内存
  • 开销:fork内存开销, copy-on-write

  • 优化:echo never > /sys/kernel/mm/transparent_hugepage/enabled(禁用透明大页功能

AOF追加阻塞

Redis基础学习笔记--这一篇差不多了

AOF阻塞定位
  1. Redis日志

    出现以下日志有可能出现AOF阻塞

    Redis基础学习笔记--这一篇差不多了

  2. 命令:执行info persistence

    aof_delayed_fsync:100 :表示累计阻塞的次数

  3. top命令看硬盘情况

Redis复制的原理与优化

主从复制两种实现方式

命令实现:
开启 slaveof ip port

 执行开启命令后,会把当前redis服务中的数据清楚,然后去复制主节点中的数据

Redis基础学习笔记--这一篇差不多了

取消主从复制 slaveof no one

执行命令后,6380端口不会把之前同步6379服务的数据清除只是不会在同步6379后面写入的数据

Redis基础学习笔记--这一篇差不多了

修改配置

1
2
slaveof ip port
slave-read-only yes #从节点只是读,不进行写的操作
两种方式比较
方式 命令 配置
优点 不需要重启 统一配置
缺点 不便于管理 需要重启

全量复制

概述

用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销

流程

Redis基础学习笔记--这一篇差不多了

开销

  1. bgsave时间

  2. RDB文件网络传输时间

  3. 从节点清空数据时间

  4. 从节点加载RDB的时间

  5. 可能的AOF重写时间

部分复制

概述

用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销,需要注意的是,如果网络中断时间过长,造成主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制

流程

Redis基础学习笔记--这一篇差不多了


开发运维中的问题

读写分离
概述

读流量分摊到从节点

可能遇到的问题
  1. 复制数据延迟

  2. 读到过期的数据(redis3.2解决了该问题)

  3. 从节点故障

配置不一致(利用标准化工具安装可避免)
  1. 例如maxmemory不一致,导致数据丢失

  2. 例如数据结构优化参数(例如hash-max-ziplist-entries):内存不一致

规避全量复制
第一次全量复制:
  • 第一次不可避免

  • 在低峰的时候进行

节点运行ID不匹配
  • 主节点重启(运行ID变化)

  • 故障转移,例如哨兵、集群

复制积压缓冲区不足
  • 网络中断,部分复制无法满足

  • 增大复制缓冲区配置rel_backlog_size,网络增强

复制风暴
单主节点复制风暴
  • 问题:主节点重启,多从节点复制

  • 解决:更换复制拓扑(读写分离有问题)

    Redis基础学习笔记--这一篇差不多了

单机器复制风暴

Redis基础学习笔记--这一篇差不多了

全量复制和部分复制相关问题

redis什么时候会发生全量复制?
  1. redis slave首启动或者重启后,连接到master时

  2. redis slave进程没重启,但是掉线了,重连后不满足部分复制条件

redis什么时候会发生部分复制?

先来看部分复制需要的条件

  1. 主从的redis版本>=2.8

  2. redis slave进程没有重启,但是掉线了,重连了master(因为slave进程重启的话,run id就没有了)

  3. redis slave保存的run id与master当前run id一致 (注:run id并不是pid,slave把它保存在内存中,重启就消失)

  4. redis slave掉线期间,master保存在内存的offset可用,也就是master变化不大,被更改的指令都保存在内存

redis进程重启后会发生全量复制还是部分复制?
  1. master重启时,run id会发生变化

  2. slave重启时,run id会丢失

答:很显然,会发生全量复制,因为部分复制的条件之一run id已经不能满足

当全量复制或者同步复制完毕,增量是如何更新到slave的?

答:通过流式的命令更新,此时master就是slave的client,这样去理解。

run id如何查看?

答:通过info server命令查看

Redis Sentinel(哨兵)

主从复制高可用问题

手动故障转移(master出问题时要手动去处理)

Redis基础学习笔记--这一篇差不多了

写能力和存储能力受限

Redis Sentinel故障转移流程

  1. 多个sentinel发现并确认master有问题

  2. 选举出一个sentinel作为领导

  3. 选出一个slave作为master

  4. 通知其余slave成为新的master的slave

  5. 通知客户端主从变化

  6. 等待老的master复活成为新的master的slave

安装与配置

  1. 配置开启主从节点

  2. 配置开启sentinel监控主节点(sentinel是特殊的redis,支持命令有限,本身不存储数据)

  3. 详细配置节点

三个定时任务

1.每10秒每个sentinel对master和slave节点执行info操作
  1. 发现slave节点

  2. 确认主从关系

Redis基础学习笔记--这一篇差不多了

2.每2秒每个sentinel通过master节点的channel交换信息(pub/sub)

  1. 通过__sentinel__hello交互频道信息

  2. 交互对节点的”看法”和自身的信息


    Redis基础学习笔记--这一篇差不多了

3.(重要的一个)每1秒每个Sentinel对其他的Sentinel和Redis执行Ping(心跳检测,失败判断依据)

Redis基础学习笔记--这一篇差不多了

主观下线和客观下线

主观下线

某个sentinel节点认为Redis节点不可用,’偏见’

客观下线

当Sentinel将一个Redis判断为主观下线之后,为了确定这个Redis是否真的下线了,它会向同样监视这个Redis的其它Sentinel进行询问,看它们是否也认为Master已经进入下线状态。当所有的sentinel节点对Redis节点失败”达成共识”(超过quorum个统一),则该Redis节点客观下线

领导者选举

原因

只有一个sentinel几点完成故障转移

选举
概述

通过sentinel is-master-down-by-addr命令都希望成为领导者

选举流程
  1. 每个做主观下线的Sentinel节点向其他Sentinel节点发送命令,要求将它设置为领导者

  2. 收到命令的Sentinel节点如果没有同意通过其他Sentinel节点发送的命令,那么将同意该请求,否则拒绝

  3. 如果该Sentinel节点发现自己的票数已经超过Sentinel集合半数且超过quorum,那么它将成为领导者

  4. 如果此过程有多个Sentinel节点成为了领导者 ,那么将等待一段时间重新进行选举。

Redis Sentinel总结

  1. Redis Sentinel是Redis高可用的实现方案(故障发现、故障自动转移、配置中心、客户端通知)

  2. 尽可能的在不同的机器上部署Redis Sentinel节点

  3. Redis Sentinel中Sentinel节点个数应该大于等于3,最好为奇数

  4. Redis Sentinel中的数据节点与普通数据节点没有区别

  5. 客户端初始化链接的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心不是代理

  6. Redis Sentinel是通过三个定时任务实现了Sentinel节点对于主节点、从节点、其余Sentinel节点的监控

  7. Redis Sentinel在对节点做失败判定时分为主观下线和客观下线

  8. Redis Sentinel实现读写分离高可用可以依赖Sentinel节点的消息通知,获取Redis数据节点的状态变化

Redis Cluster

虚拟槽分区

  1. 预设虚拟槽:每个槽映射一个数据子集,一般比节点数大

  2. 良好的哈希函数:例如 CRC16

  3. 服务端管理节点、槽、数据:例如Redis Cluster

客户端路由

moved重定向

Redis基础学习笔记--这一篇差不多了

槽命中:直接返回

Redis基础学习笔记--这一篇差不多了

槽不命中:moved异常

Redis基础学习笔记--这一篇差不多了

ASK重定向

moved和ASK
  • 两者都是客户单重定向

  • moved:槽已经确定迁移

  • ASK:槽还在迁移中

客户端使用

执行流程
  1. 从集群中选一个可运行节点,使用 cluster slots初始化槽和节点映射

  2. 将cluser slots的结果映射到本地,为每个节点创建JedisPool

  3. 准备执行命令

故障发现

  • 通过ping/pong消息实现故障发现,不需要sentinel

  • 主观下线和客观下线

故障恢复

资格检查
  • 每个从节点检查与故障主节点的断线时间

  • 超过cluster-node-tit * cluster-slave-validity-facor取消资格

  • cluster-slave-validity-factor:默认为10

准备选举时间
选举投票
替换主节点
  • 当前从节点取消复制变为主节点。(slaveof no one

  • 执行clusterDelSlot撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽分配给自己

  • 向集群广播自己的ponmg消息,表明已经替换了故障主节点

Redis Cluster开发运维常见问题

集群完整性
  • cluster-require-full-coverage默认为yes:表示集群所有节点正常才提供服务

  • 多数业务无法容忍,建议cluster-require-full-coverage设置为no

带宽消耗
  • Ping/Pong消息

  • 官方建议节点不要超过1000个

    优化
    • 避免多个业务使用一个大集群

    • cluster-node-timeout:带宽和故障转移速度的均衡

    • 尽量均匀分配到多机器上:保证高可用和带宽

Pub/Sub广播
问题:

publish在集群每个节点广播:加重带宽

解决

单独”走”一套Redis Sentinel

集群倾斜
数据倾斜:内存不均匀
  • 节点和槽分配不均匀

  • 不同槽对应键值数量差异较大

  • 包含bigkey(可在从节点执行redis-cli --bigkeys查找)

  • 内存相关配置不一致

请求倾斜
  • 热点key或者bigkey

集群读写分离
只读链接

集群模式的从节点不接受任何读写请求

  • 读取数据时会重定向负责槽的主节点

  • readonly命令可以在从节点上读,但是这是链接级别的命令

读写分离(较复杂,cluster模式下不建议使用)
数据迁移
官方迁移工具(redis-tribrb import)
  • 只能从单机迁移到集群

  • 不支持在线迁移:source需要停写

  • 不支持断点续传

  • 单线程迁移:影响速度