之前《MongoDB是什么?看完就知道了》收到许多赞,作为该文章的姐妹篇,本文主要从运维开发的角度总结Redis的知识,力求简单,形成思维导图,总结Redis的一些特点优点,为之后的技术选型做一个有力的指导笔记。或是给新手苦恼找不到Redis的学习资料,不知道如何入手,深入时的一个好的建议。本文1w+字,如果你只想了解其中一些知识,看目录啦
注意!本文不是满满的API干货也不是实战,他更像是一个缓存选型时对Redis的统揽,如果你需要API的话移步百度啦
本文的整理心路历程
1、快读《Redis开发与运维》
2、精度读《Redis开发与运维》+整理xmind
3、xmind输出文章
了解Redis的过程中,主要从三本书籍入手,分别说下测评,让新手能第一次就选中合适的书籍。
1.1、《Redis开发与运维》:强烈推荐,该书从开发与运维的角度解析Redis的每个知识点,采用分点论述的结构,容易梳理成思维导图,由浅入深,从基本使用到原理解析全面覆盖,本文章也是按该书的读后思维导图进行编写。
1.2、《Redis设计与实现》:该书主要从源码的角度讲述Redis知识点,比较适合玩过一段时间的老鸟看,特别是适合从事Redis相关运维平台开发的人员观看,它带你理解透彻底层源码。
1.3、《Redis实战》:主要通过叙事的方式讲述Redis的知识点,逻辑层次结构不是很明显突出,不好整理,但是对于急于入门Redis开发的人员来说也是一个比较好的选择,毕竟我也是实战系列的崇拜者。
2.1、一句话描述:是一个键值对的NoSQL的内存数据库。
2.2、存储区域
2.3、基本数据结构
这里可以对标java的Collection框架,但是Redis牛逼它几个数量级
2.4、其他牛逼的功能
3.1、速度快
3.2、基于键值对的数据结构服务器,为缓存场景而产生
3.3、拥有许多丰富的功能
3.4、简单稳定,它的代码不超过1w行,你敢信?
3.5、支持客户端语言多,Jedis u know
3.6、支持持久化
3.7、支持主从复制(哨兵)
3.8、支持高可用与分布式(分布式,你可以理解为分区表)
keys * #获得所有键,不要用咯,会卡死你的服务器
dbsize #键总数,统计的,不用担心啦
exist {key} #是否存在
del key {key}
expire {key} seconds #设置过期
ttl {key} #查看是否过期
type {key} #键的类型
object encoding {key} #查看内部编码,有助于内存优化,编码类型决定内存使用大小
rename key newkey #重命名
randomkey #随机返回一个
select dbIndex #选择数据库,注意因为Redis是单线程的,所以不建议使用多个数据库
flushdb #清库(危险!小心!)
fluashall #清所有(危险!小心!)
config set #修改配置(并不是所有配置都可以动态修改)
config rewrite #配置写出到文件
2.1、单个键值对的最大值不能超过512m
2.2、内部编码
2.3、经典使用场景
2.4、命令
set key value [ ex seconds ] [ px milliseconds ] [ nx|xx ] #设置值
setex #设置值
setnx #设置值
get key #获取值
mset key value [ key value ] #批量设置值
mget key [ get key ] #批量获取值
incr key #自增
decr key #自减
incrbyfloat #自增浮点数
append key value #追加值
strlen key #字符串长度
getset key value #设置并返回值
setrange key offset value #设置指定位置的字符
getrange key start end #获取部分字符串
3.1、哈希类型中的映射关系叫做field-value 对应的键是叫field
3.2、内部编码
3.3、使用场景
3.4、命令
hset key field value #设置值
hget key field #获取值
hdel key field #删除
hlen key #计算个数
hmset key field value [ field value ] #批量设置
hmget key field #批量获取
hexist key field #是否存在
hkeys key #获取所有域
hvals key #获取所有值
hincrby bincrbyfloat #累加
htrlen key field #计算字符串长度
4.1、可以充当栈与队列
4.2、特点
4.3、内部编码
ziplist:当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时 (默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使
用
linkedlist:当列表类型无法满足ziplist的条件时,Redis会使用
linkedlist作为列表的内部实现。
quicklist(^3.2)
4.4、经典使用场景
4.5、命令
rpush key value #右插入
lpush key value #左插入
linsert key before|after pivot value #在某个元素的前或后插入
lrange key start end #获取指定范围的元素
lindex key index #获取指定下标的元素
llen key #获取长度
lpop key #从左侧弹出元素
rpop key #从右侧弹出元素
lrem key count value #删除指定元素
ltrim key start end #按索引范围修剪
lset key index newValue #修改
blpop key timeout #阻塞式的弹出
rlpop key timeout #阻塞式的弹出
5.1、内部编码
5.2、使用场景
5.3、命令
sadd key element #添加元素
srem key element #删除元素
scard key #计算元素个数
sismember key element #是否存在
srandmemeber key #随机从集合中返回个数元素
spop key #从集合中随机弹出元素
smembers key #获取所有元素
sinter key #交集
sunion key #并集
sdiff key #差集
6.1、内部编码
6.2、使用场景
6.3、命令
zadd key score member #添加成员
zcard key #计算个数
zscore key member #计算成员分数
zrank key member #计算成员排名
zrevrank key member
zrangebyscore key min max #根据范围获取
zcount key min max #返回指定分数范围的个数
zremrangebyrank key start end #删除指定排名内的升序元素
zremrangebyscore key min max #删除指定分数范围的成员
zinterstore destination numkeys key #交集
zunionstore destination numkeys key #并集
7.1、可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。将访问的用户记做1,没有访问的用户记做0,用偏移量作为用户的id
7.2、在存储量很大的情况下可以节省内存,但是在存储量很小的情况下,并不会节省内存
7.3、命令
setbit key offset value #设置值
gitbit key offset #获取值
bitcount #统计范围个数
bitop op destkey key #Bitmaps间的运算
bitops key targetBi #计算偏移量
8.1、HyperLogLog并不是一种新的数据结构(实际类型为字符串类型),而是一种基数算法,通过HyperLogLog可以利用极小的内存空间完成独立总数的统计
8.2、特点
8.3、命令
pfadd key element #添加
pfcount key #计算
pfmerge dseskey sourcekey #合并
9.1、Redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能
9.2、命令
geoadd #增加
geopos key member #获取
geodist key member #计算距离
georadius key #获取指定范围的数据
zrem #删除
1.1、客户端命令的生命周期
客户端->缓冲队列->被执行->响应结果
1.2、相关配置
1.3、配置方法
1.4、获取
slowlog get [n]
slowlog len
slowlog reset
2.1、redis-cli
-r #重复执行
-i #每隔几秒执行
-x #执行命令
-a #认证
--latency #检测网络延迟
--stat #统计信息
--pipe #执行流水线
2.2、redis-server
--test-memory #探测内存是否足够
2.3、redis-benchmark
一款自带的压测工具
3.1、它能将一组命令进行组装,通过RTT传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端
3.2、性能
3.3、与原生批量命令的区别
4.1、执行命令
multi #事务开始
exec #事务结束
discard #取消事务
注意:不支持事务回滚
6.1、使用场景
6.2、特点
6.3、命令
publish channel message #发布
subscribe channel #订阅
unsubscribr #取消订阅
psubscribe pattern #按模式订阅
pubsub channels #查看活跃频道
pubsub numsub channel #查看频道订阅数
pubsub numpat #查看模式订阅数
客户端与服务器端之间的通信协议是在TCP协议之上的,Redis制定RESP协议,实现客户端与服务器端的正常交互
2.1、请求格式
*3 #参数数量
$3 #下面这个参数的字节数
SET
$5 #下面这个参数的字节数
hello
$5 #下面这个参数的字节数
world
#注意以上是一条命令,它们之间使用 \r\n 去隔离
2.2、响应格式
+OK
#状态回复:在RESP中第一个字节为"+"。
#错误回复:在RESP中第一个字节为"-"。
#整数回复:在RESP中第一个字节为":"。·字符串回复:在RESP中第一个字节为"$"。
#多条字符串回复:在RESP中第一个字节为"*"。
#nil结果响应 $-1
3.1、用法
4.1、罗列当前所有连接客户端
client list
id=2296950 addr=20.X.X.XX:17969 fd=19 name= age=66163 idle=17 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
4.2、杀死客户端
client kill ip:port
4.3、阻塞客户端
client pause timeout
4.4、监控
monitor #注意会导致输出缓冲区急剧增大
4.5、关于客户端的相关配置
4.6、关于客户端统计的信息片段
info clients
info stats
1.1、触发机制
1.1.1、RDB持久化是吧当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程可以是自动触发,也可以是手动触发
1.1.2、手动触发
save #阻塞当前Redis服务器,直到RDB过程完成为止,注意如果内存使用较多的话阻塞的时间会比较长
注意:线上环境不建议使用
bgsave #Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束
注意:阻塞只发生在fork阶段,此时需要一定量的内存去支持子线程
1.1.3、自动触发
#这里是配置文件
save m n #表示m秒内存在n次修改时,自动触发bgsave
1.2、流程说明
info stat|grep latest_fork_usec
lastsave #最后一次备份时间
info |grep rdb_last_save_time
1.3、RDB文件的处理
1.3.1、保存
dbfilename #通过配置执行文件名
config set dir
config set dbfilename
1.3.2、压缩
config set rdbcompression {yes|no}
1.3.3、文件修复
redis-check-dump #使用改工具检测并获得报告
1.4、RDB的优缺点
以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的,可以联想mysql的bin-log
2.1、使用
#以下是配置文件
appendonly yes #默认不开启
appendfilename appendonly.aof #存储名称
2.2、执行流程
2.3、命令写入的问题
set hello world -> *3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n #是不是很熟悉,对的就是RESP协议
使用这种协议的好处:
为什么先写缓冲区?
这样可以在性能和安全性方面做出平衡
2.4、写入策略
2.5、重写机制
2.5.1、为什么可以变小
2.5.2、手动触发
bgrewriteaof
2.5.3、自动触发
根据以下配置参数触发
auto-aof-rewire-min-size
auto-aof-rewire-percentage
触发算法
自动触发时机=aof_current_size>auto-aof-rewrite-min-
size&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage
2.5.4、重写流程
2.6、重启加载
2.7、文件校验
3.1、fork操作
3.1.1、问题定位
info stats|grep lastest_fork_usec #获取最近一次fork时间
3.1.2、如何改善
3.2、子进程开销监控与优化
3.3、AOF追加阻塞
Redis使用另一条线程每秒执行fsync同步硬盘。当系统繁忙时,会造成主线程阻塞
3.3.1、刷盘流程
3.3.2、存在的问题
3.3.3、阻塞问题定位
1.1、建立复制
slave of mastetHost masterPort #配置文件
./redis-server --slaveof
slaveof
info replication
1.2、断开复制
slaveof no one
1.2.1、切主流程
注意:切主后从节点会清空之前所有的数据,线上人工操作时小心slaveof在错误的节点上执行或指向错误的主节点
3.1、复制过程
3.2、同步数据使用了什么机制?
3.3、全量复制
3.3.1、流程
3.3.2、时间消耗
3.4、部分复制
你可以理解为是一个实时同步的过程咯
3.5、心跳的维持
主从节点在建立复制后,它们之间维护着长连接并彼此发送心跳命令
3.6、异步复制
主节点不但负责数据读写,还负责把写命令同步给从节点。写命令的发送过程是异步完成,也就是说主节点自身处理完命令后直接返回给客户端,并不等待从节点复制完成
4.1、读写分离
4.2、主从配置不一致
建议全量复制主从配置
4.3、全量复制
1.1、API或数据结构使用不合理
发现慢查询
showlog get {n}
注意点
1.2、CPU饱和的问题
./redis-cli -h {ip} -p {port} --stat #统计redis使用情况
info commandstats #命令分析
1.3、持久化相关阻塞
1.3.1、fork阻塞
fork操作发生在RDB和AOF重写时
info stats|grep lastest_fork_use #查看最近一次fork耗时
1.3.2、AOF刷屏阻塞
文件刷盘的方式一般采用每秒一次,后台线程每秒对AOF文件做fsync操作
info persistence|grep aof_deplayed_fsync #刷盘时间统计
2.1、CPU竞争
2.2、内存交换
识别方法
cat /proc/4476/smaps|grep Swap
预防方法
echo 10 > /proc/sys/vm/swagppiness
2.3、网络问题
2.3.1、链接拒绝
2.3.2、网络延迟
./redis-cli-h {host} -p {port} --latency #持续进程延迟测试,分别统计最小值、最大值、平均值、采样次数
2.3.3、网卡软中断
info memory #查看内存使用情况
重点关注:used_memory_rss、used_memory以及它们的比值mem_fragmemtation_ratio
内存消耗=自身内存+对象内存+缓冲内存+内存碎片 #自身内存消耗很小可以忽略不计
2.1、对象内存
2.2、缓冲内存
客户端缓冲:指的是所有接入到Redis服务器TCP连接的输入输出缓冲,这部分无法控制,最大空间为1G,如果超过将断开链接
普通客户端:一般普通客户端的内存消耗可以忽略不计,大量慢链接客户端接入时将会消耗大量内存
从客户端:主节点会为每个从节点单独建立一条链接用于命令复制
订阅客户端:当订阅服务器的消息生产快于消费速度时,输出缓冲区会产生积压造成输出缓冲区空间溢出
复制积压缓冲区:用于实现部分复制功能
AOF缓冲区:用于在Redis重写期间保存最近的写入命令、内存消耗小
2.3、内存碎片
正常的碎片率(mem_fragmentation_ratio)在1.03左右
某些场景会出现高内存碎片问题
解决方案
2.4、子进程内存消耗
子进程消耗主要指执行AOF/RDB重写时Redis创建的子进程内存消耗。
如果父进程有大量写命令,会加重内存拷贝量,从而造成过度内存消耗。
解决方案
3.1、设置内存上限
目的
注意点
config set maxmemory #动态调整内存上限 可用于清理过期键
3.2、内存回收策略
3.2.1、删除过期键对象
3.2.2、内存溢出控制策略
当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy限制
运维提醒
因为需要主从
为了高可用,需要切主的动作
当主节点出现故障时,哨兵能自动完成故障发现和故障转移
2.1、故障转移逻辑
2.2、哨兵功能点描述
sentinel masters #展示所有被监控的主节点状态
sentinel slaves #展示从节点状态
sentinel sentinels {master name} #展示监控主节点信息
sentinel get-master-addr-by-nmae {master name} #返回主节点的地址端口
sentinel reset {pattern} #重置
sentinel failover #强制故障转移
Sentinel ckquorum #检测当前可达节点
sentinel flushconfig #强刷配置
sentinel monitor #动态配置监控主节点
5.1、Redis Sentinel客户端
Sentinel节点集合具备了监控、通知、自动故障转移、配置提供者若干功能,也就是说实际上最了解主节点信息的就是Sentinel节点集合,而各个主节点可以通过进行标识的,所以,无论是哪种编程语言的客户端,如果需要正确地连接Redis Sentinel,必须有Sentinel节点集合和masterName两个参数
5.2、实现原理
6.1、三个定时监控任务
每10s通过info向主节点和从节点获取哨兵自身拓扑
每2s向被监控节点告知自身的信息
每1s,向主节点、从节点发送一次ping做心跳检测,确认是否可达
6.2、下线定义
6.3、领导选举
每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,求将自己设置为领导者
收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝
如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者
如果此过程没有选举出领导者,将进入下一次选举
选举的过程非常快,基本上谁先完成客观下线,谁就是领导者
6.4、故障转移
7.1、在日志中可体现工作细节
7.2、节点运维
sentinel failover #节点下线
slaveof #从节点上线
集群:这里的意思指的是分布式数据库,当数据存储时,随机分布存储到各个数据库节点
1.1、数据分布理论
分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点点,每个节点负责整体数据的一个子集
1.2、常见的哈希分区规则
1.3、Redis数据分区
Redis集群采用虚拟槽分区,所有的键根据哈希函数映射到0~16383个槽中。每一个节点负责维护一部分槽以及槽所映射的键值数据
特点
1.4、集群功能限制
通信流程
Gossip消息协议:主要职责就是信息交换。信息交换的载体就是节点彼此发送Gossip消息,了解这些消息有助于我们理解集群如何完成信息交换
伸缩原理:集群伸缩=槽和数据在节点之间的移动
扩容集群
收缩集群
当发起一个key获取操作时,客户端慧自动完成请求重定向操作
故障发现
故障恢复流程:
7.1、集群自身会保证其完整性:为了保证集群完整性,默认情况下当集群16438个槽任何一个没有指派到节点时整个集群不可用
7.2、带宽消耗
主要消耗
合理规划
规避广播
集群倾斜
集群读写分离
Redis集群提供了手动故障转移功能:指定从节点发起转移流程,主从节点角色进行切换,从节点变为新的主节点对外提供服务,旧的主节点变为它的从节点
指的使应用Redis集群时,常需要把单机Redis数据迁移到集群环境
注意点
Redis说白了就是一个缓存服务,所以对于缓存相关问题凸显的相当重要,要清晰的认识到缓存,储备缓存的相关知识,才能用好Redis这九把刀。
缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中。穿透场景如下
会出现穿透的原因
解决方案
无底洞指的是更多的节点不代表更高的性能,所谓的无底洞就是说投入越多不一定产出越多。但是分布式又是不可避免的。
5.1、原因是什么?
通常来说添加节点使得Memcache集群性能应该更强了,但事实并非如此。键值数据库由于通常采用哈希函数将key映射到各个节点上,造成key的分布与业务无关,但是由于数据量和访问
量的持续增长,造成需要添加大量节点做水平扩容,导致键值分布到更多的节点上,所以无论是Memcache还是Redis的分布式,批量操作通常需要从不同节点上获取,相比于单机批量操作只涉及一次网络操作,分布式批量操作会涉及多次网络时间
5.2、解决方案
缓存雪崩是因为缓存因为某些原因不能提供服务,于是所有的请求都会到达存储层,存储层压力爆增。
解决方案
热点Key存在的问题
解决方案
1.1、内存分配控制
vm.overcommit_memory=1 #linux系统配置
cat /proc/sys/vm/overcommit_memory #查看当前配置
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf #设置
建议设置为1,是为了让fork操作能够在低内存下也执行成功
合理分配内存才是优化内存的关键
1.2、swappiness
swap对操作系统来说比较重要,当物理内存不够时,可以将一部分内存页进行swap操作,已解燃眉之急。
但。
swap空间由硬盘提供,将会很慢。
在Linux中, 并不是要等到所有物理内存都使用完才使用swap,系统参数swappiness会决定操作系统使用swap的倾向程度。取值范围为0到100,值越大,说明使用swap的概率越大。
echo {bestvalue} > /proc/sys/vm/swappiness
echo vm.swappiness={bestvalue} >> /etc/sysctl.conf
#设置方法
free #查看总体内存使用情况
/proc/{pid}/smaps #查看某条进程使用情况
1.3、OOM killer
OOM killer 进程会为每个用户进程设置一个权值,这个权值越高,被杀的概率越高
cat /proc/{pid}/oom_score #查看
echo -17 > /proc/${process_id}/oom_adj #配置
运维提示
1.4、ulimit
限制进程打开最大文件数
ulimit -Sn {max-open-files} #设置
bind 0.0.0.0