Redis基础学习笔记--这一篇差不多了
前言
Redis使用也挺久了,也没用到过高级用法,就是简单的做个缓存。以前也没系统的学过。前几天看了个Redis视频课程,就做了下笔记,大部分都是基础知识,没有涉及到高阶,但是也算是全面的梳理了一遍(排版难受,可以点击原文)。纯手打! 实属不易! 点个赞吧!
String类型
常用命令(点击参考)
set命令设置 key value
1
2
3
4127.0.0.1:6379> set key "Hello"
OK
127.0.0.1:6379> set key "hello" ex 1000
OKEX
seconds – 设置键key的过期时间,单位时秒PX
milliseconds – 设置键key的过期时间,单位时毫秒NX
– 只有键key不存在的时候才会设置key的值XX
– 只有键key存在的时候才会设置key的值get key
:获取某个key的valueAPPEND key value
:追加一个值到key
上,如果key
不存在则创建一个key
MSET key1 value1 key2 value2]
:设置多个 key valueMGET key1 key2
:获取多个值,如果key不存在,返回nil
INCR key
(只操作整数):执行原子加一操作,如果key不存在,则key从0开始加1.INCRBY key increment
(只操作整数)`:执行原子增加一个值,如果key不存在,key被设置为0,如果数据类型错误返回错误。1
2
3
4
5
6127.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 rangeDECR key(只操作整数)
:整数原子
减一,如果key不存在,则key从0开始减一,如果数据类型错误,返回错误。DECRBY key decrement
(只操作整数):原子性减指定的数,如果key不存在,则key被置为0,然后相减1
2
3
4127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> DECRBY num 5
(integer) 5
应用场景
缓存
Hashes(哈希)
常用命令(点击参考)
HSET key field value
:设置hash里面一个字段的值HGET key field
:获取某个字段的值HDEL key field
:删除1个或多个字段的值1
2
3
4
5
6
7
8127.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)HKEYS key
:获取 key 所有字段的名字HVALS key
:返回key所有字段的value值1
2
3
4
5
6
7
8
9
10127.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"HGETALL key
:返回 key 指定的哈希集中所有的字段和值。返回值中,每个字段名的下一个是它的值,所以返回值的长度是哈希集大小的两倍应用场景
个人用户首页访问量
List(列表)
简介
Redis lists基于Linked Lists实现。这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的
常用命令(点击参考)
LSET key index value
:设置 index 位置的list元素的值为 value,当index超出范围返回一个errorLPUSH key value1 value2
:从队列左边向右边入队一个或多个元素LPOP key
:移除并且返回 key 对应的 list 的第一个元素。1
2
3
4127.0.0.1:6379> LPUSH sqt name age address
(integer) 3
127.0.0.1:6379> LPOP sqt
"address"LLEN key
:返回存储在 key 里的list的长度。如果 key 不存在,会被看作是空list,并且返回长度为 0。当存储在 key 里的值不是一个list的话,会返回error。应用场景
粉丝列表、关注列表(利用队列的性质)
实现消息队列
Sets(集合)
简介
不重复且无序的字符串元素的集合
常用命令(点击参考)
sadd key value1 value2
:添加一个和多个元素sinter key1 key2
:求交集SCARD key
:集合存储的key的基数 (集合元素的数量)SREM key member1 member2
:移除key集合指定的元素. 如果指定元素不存在则忽略,如果key集合不存在返回0.SPOP key [count]
:从存储在key
的集合中移除并返回一个或多个随机元素1
2
3
4
5
6
7
8
9
10
11
12127.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>SRANDMEMBER key [count]
:随机返回key集合中的一个元素,不删除
应用场景
共同好友—->
sinter key1 key2
抽奖系统—–>
SRANDMEMBER key [count]
或SPOP key [count]
Sorted Sets(有序集合)
简介
类似Sets。但是元素不重复且有序。但是每个字符串元素都关联到一个叫score浮动数值(floating number value)。里面的元素总是通过score进行着排序,所以不同的是,它是可以检索的一系列元素。
基本命令(点击参考)
ZADD key score element
:添加一个或者多个元素, 如果元素(element)已存在,则更新并重新排序。如果key不存在,则创建新集合。复杂度 O(logN)XX: 仅仅更新存在的成员,不添加新成员。
NX: 不更新存在的成员。只添加新成员。
CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,
ZADD
返回值只计算新添加成员的数量。INCR: 当
ZADD
指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作。ZCARD key
:返回 key 集合元素的数量ZREM key member [member ...]
:删除一个或多个元素,当key存在,但是其不是有序集合类型,就返回一个错误。1
2
3
4
5
6
7127.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>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
17127.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" ZREVRANGEBYLEX key max min [LIMIT offset count]
:返回指定成员区间内的成员,按成员字典倒序排序, 分数必须相同完整语法:
ZREVRANGEBYLEX key max min [LIMIT offset count]
-
指令 是否必须 说明 ZREVRANGEBYLEX 是 指令 key 是 有序集合键名称 max 是 字典中排序位置较大的成员,必须以”[“开头,或者以”(“开头,可使用”+”代替 min 是 字典中排序位置较小的成员,必须以”[“开头,或者以”(“开头,可使用”-“代替 LIMIT 否 返回结果是否分页,指令中包含LIMIT后offset、count必须输入 offset 否 返回结果起始位置 count 否 返回结果数量
应用场景
排行榜(根据点赞数、评论数)
热点数据
集合 VS 有序集合
集合 | 有序集合 |
---|---|
无重复元素 | 无重复元素 |
无序 | 有序 |
element | element + score |
列表 VS 有序集合
列表 | 有序集合 |
---|---|
元素可以重复 | 元素不可以重复 |
无序 | 有序 |
element | element + score |
慢查询
客户端超时不一定是慢查询,但慢查询是客户端超时的一个因素
生命周期:
1 |
发送命令->命令排队->命令执行->返回结果 |
慢查询发生在 命令执行 阶段。
配置
配置
slowlog-log-slower-than
指定执行时间超过多少微秒的命令会被记录到日志上(建议设置为1000)配置
slowlog-max-len
**指定最多保存多少条慢查询记录.(默认10ms,建议1ms)**
pipeline(流水线)
一次网络时间,多条命令。节省网络传输时间
redis持久化的取舍和选择
持久化方式
快照(RDB)
例如:Redis RDB、Mysql Dump
写日志(AOF)
例如:Redis AOF、Mysql Binlog、Hbase Hlog
RDB
定义
触发机制的三种方式
save 命令(同步)
数据量大的时候会阻塞
如存在老的RDB文件,新替换老
复杂度O(N)
bgsave(异步)
save 和 bgsave 简单对比
命令 | bgsave | save |
---|---|---|
IO类型 | 异步 | 同步 |
阻塞 | 是(阻塞发生在fork) | 是 |
复杂度 | O(n) | O(n) |
优点 | 不阻塞客户端命令 | 不会消耗额外内存 |
缺点 | 需要fork,消耗内存 | 阻塞客户端命令 |
最佳配置
1 |
# 下面 3 条 save 命令满足其中任何一个,都会执行 bgsave |
触发机制-不容忽略方式
全量复制 (比如主从复制的时候)
debug reload
shutdown (关闭的时候,有可能生成 RDB文件)
总结
RDB是Redis内存到硬盘的快照,用于持久化
save命令通常会阻塞Redis
bgsave不会阻塞Redis,但是会fork新的进程
save自动配置满足任一就会被执行
有些触发机制不容忽视
AOF
AOF运行原理-创建/恢复
三种策略
always
每个命令都会进行持久化,数据不会丢失
everysec
Redis的默认配置,每一秒执行一次持久化,最多丢失一秒的数据。
no
由操作系统法国决定
优缺点
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每秒fsync,有可能丢1秒数据 | 不用管 |
缺点 | IO开销比较大,一般的 sata盘只有几百TPS |
丢失一秒数据 | 不可控 |
AOF重写
AOF重写作用
减少硬盘占用量
加速恢复速度
AOF实现的两种方式
bgrewriteaof
AOF重写配置
配置
配置名 含义 auto-aof-rewrite-min-size AOF文件重写需要的尺寸 auto-aof-rewrite-percentage AOF文件增长率 统计
统计名 含义 aof_current_size AOF当前尺寸(单位:字节) aof_base_size AOF上次启动和重写的尺寸(单位:字节) AOF自动触发时机
aof_current_size > auto-aof-rewrite-min-size
(aof_current_size - aof_base_size)/aof_base_size > auto-aof-rewrite-percentage
AOF重写流程
AOF和RDB对比
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
轻重 | 重 | 轻 |
RBD最佳策略
建议关掉RDB
集中管理
主从,从开
AOF最佳策略
建议开启(缓存和存储)
AOF重写集中管理
使用everysec
最佳策略
小分片
缓存或者存储
监控(硬盘、内存、负载、网络)
足够的内存
运维常见问题
fork操作
同步操作
与内存量息息相关:内存越大,耗时越长(与机器类型有关)
info:lastest_fork_usec(查看fork执行的时间信息)
改善fork
优先使用 物理机或者高效支持fork操作的虚拟化技术
控制Redis实例最大可用内存:maxmemory
合理配置Linux内存分配策略:
vm.overcommit_ory=1
降低fork频率:例如放款AOF重写自动触发时机,不必要的全量赋值
子进程的开销和优化
CPU
开销:RDB和AOF文件生成,属于CPU密集型
优化:不做CPU绑定,不和CPU密集型的服务一块部署
内存
开销:fork内存开销, copy-on-write
优化:echo never > /sys/kernel/mm/transparent_hugepage/enabled(禁用透明大页功能)
AOF追加阻塞
AOF阻塞定位
Redis日志
出现以下日志有可能出现AOF阻塞
命令:执行
info persistence
aof_delayed_fsync:100
:表示累计阻塞的次数top命令看硬盘情况
Redis复制的原理与优化
主从复制两种实现方式
命令实现:
开启 slaveof ip port
执行开启命令后,会把当前redis服务中的数据清楚,然后去复制主节点中的数据
取消主从复制 slaveof no one
执行命令后,6380端口不会把之前同步6379服务的数据清除,只是不会在同步6379后面写入的数据。
修改配置
1 |
slaveof ip port |
两种方式比较
方式 | 命令 | 配置 |
---|---|---|
优点 | 不需要重启 | 统一配置 |
缺点 | 不便于管理 | 需要重启 |
全量复制
概述
用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销
流程
开销
bgsave时间
RDB文件网络传输时间
从节点清空数据时间
从节点加载RDB的时间
可能的AOF重写时间
部分复制
概述
用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销,需要注意的是,如果网络中断时间过长,造成主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制
流程
开发运维中的问题
读写分离
概述
读流量分摊到从节点
可能遇到的问题
复制数据延迟
读到过期的数据(redis3.2解决了该问题)
从节点故障
配置不一致(利用标准化工具安装可避免)
例如
maxmemory
不一致,导致数据丢失例如数据结构优化参数(例如hash-max-ziplist-entries):内存不一致
规避全量复制
第一次全量复制:
第一次不可避免
在低峰的时候进行
节点运行ID不匹配
主节点重启(运行ID变化)
故障转移,例如哨兵、集群
复制积压缓冲区不足
网络中断,部分复制无法满足
增大复制缓冲区配置
rel_backlog_size
,网络增强
复制风暴
单主节点复制风暴
问题:主节点重启,多从节点复制
解决:更换复制拓扑(读写分离有问题)
单机器复制风暴
全量复制和部分复制相关问题
redis什么时候会发生全量复制?
redis slave首启动或者重启后,连接到master时
redis slave进程没重启,但是掉线了,重连后不满足部分复制条件
redis什么时候会发生部分复制?
先来看部分复制需要的条件
主从的redis版本>=2.8
redis slave进程没有重启,但是掉线了,重连了master(因为slave进程重启的话,run id就没有了)
redis slave保存的run id与master当前run id一致 (注:run id并不是pid,slave把它保存在内存中,重启就消失)
redis slave掉线期间,master保存在内存的offset可用,也就是master变化不大,被更改的指令都保存在内存
redis进程重启后会发生全量复制还是部分复制?
master重启时,run id会发生变化
slave重启时,run id会丢失
答:很显然,会发生全量复制,因为部分复制的条件之一run id已经不能满足
当全量复制或者同步复制完毕,增量是如何更新到slave的?
答:通过流式的命令更新,此时master就是slave的client,这样去理解。
run id如何查看?
答:通过info server命令查看
Redis Sentinel(哨兵)
主从复制高可用问题
手动故障转移(master出问题时要手动去处理)
写能力和存储能力受限
Redis Sentinel故障转移流程
多个sentinel发现并确认master有问题
选举出一个sentinel作为领导
选出一个slave作为master
通知其余slave成为新的master的slave
通知客户端主从变化
等待老的master复活成为新的master的slave
安装与配置
配置开启主从节点
配置开启sentinel监控主节点(sentinel是特殊的redis,支持命令有限,本身不存储数据)
详细配置节点
三个定时任务
1.每10秒每个sentinel对master和slave节点执行info
操作
发现slave节点
确认主从关系
2.每2秒每个sentinel通过master节点的channel交换信息(pub/sub)
通过
__sentinel__
:hello交互频道信息交互对节点的”看法”和自身的信息
3.(重要的一个)每1秒每个Sentinel对其他的Sentinel和Redis执行Ping(心跳检测,失败判断依据)
主观下线和客观下线
主观下线
某个sentinel
节点认为Redis
节点不可用,’偏见’
客观下线
当Sentinel将一个Redis
判断为主观下线之后,为了确定这个Redis
是否真的下线了,它会向同样监视这个Redis
的其它Sentinel进行询问,看它们是否也认为Master已经进入下线状态。当所有的sentinel
节点对Redis
节点失败”达成共识”(超过quorum个统一),则该Redis
节点客观下线
领导者选举
原因
只有一个sentinel几点完成故障转移
选举
概述
通过sentinel is-master-down-by-addr
命令都希望成为领导者
选举流程
每个做主观下线的Sentinel节点向其他Sentinel节点发送命令,要求将它设置为领导者
收到命令的Sentinel节点如果没有同意通过其他Sentinel节点发送的命令,那么将同意该请求,否则拒绝
如果该Sentinel节点发现自己的票数已经超过Sentinel集合半数且超过
quorum
,那么它将成为领导者如果此过程有多个Sentinel节点成为了领导者 ,那么将等待一段时间重新进行选举。
Redis Sentinel总结
Redis Sentinel是Redis高可用的实现方案(故障发现、故障自动转移、配置中心、客户端通知)
尽可能的在不同的机器上部署Redis Sentinel节点
Redis Sentinel中Sentinel节点个数应该大于等于3,最好为奇数
Redis Sentinel中的数据节点与普通数据节点没有区别
客户端初始化链接的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心不是代理
Redis Sentinel是通过三个定时任务实现了Sentinel节点对于主节点、从节点、其余Sentinel节点的监控
Redis Sentinel在对节点做失败判定时分为主观下线和客观下线
Redis Sentinel实现读写分离高可用可以依赖Sentinel节点的消息通知,获取Redis数据节点的状态变化
Redis Cluster
虚拟槽分区
预设虚拟槽:每个槽映射一个数据子集,一般比节点数大
良好的哈希函数:例如 CRC16
服务端管理节点、槽、数据:例如Redis Cluster
客户端路由
moved重定向
槽命中:直接返回
槽不命中:moved异常
ASK重定向
moved和ASK
两者都是客户单重定向
moved:槽已经确定迁移
ASK:槽还在迁移中
客户端使用
执行流程
从集群中选一个可运行节点,使用 cluster slots初始化槽和节点映射
将cluser slots的结果映射到本地,为每个节点创建JedisPool
准备执行命令
故障发现
通过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需要停写
不支持断点续传
单线程迁移:影响速度