1. Redis基础
1.1 Redis介绍
Redis是一个开源的使用C语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的API。
本质是客户端-服务端应用软件程序。
特点是使用简单,性能强悍、功能应用场景丰富。
1.2 通用命令
命令 | 含义 |
---|---|
del key | 该命令用于在key存在时删除key |
dump key | 序列号给定的key,并返回被序列化的值 |
exists key | 检查给定的key是否存在 |
expire key seconds | 为给定key设置过期时间,以秒计 |
ttl key | 以秒为单位,返回给定key的剩余生存时间(TTL Time to live) |
type key | 返回key所存储的值的类型 |
key * | 返回所有的key列表 |
1.3 常用数据结构
备注:在官网中有每个命令的讲解*https://redis.io/commands***
redis是内存型key-value日志型数据库,数据类型是相对于value而言。
string 字符串类型
hash hash类型、key-value hashmap类型
list 列表类型、数组
set 无序列表
zset 有序列表
1.3.1 String
String 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字。
使用场景:微博数,粉丝数
常用命令:
命令 | 含义 |
---|---|
get | 获取指定的key |
set | 设置指定key的值 |
incr | 将key中存储的数字值增一(非数值型数据会报(error) ERR value is not an integer or out of range) |
decr | 将key中存储的数字值减一(限制操作数值数据) |
mget | 获取所有(一个或者多个)给定key的值 |
set语法:
#设置时可以指定过期时间、NX不存在即设置 XX 一直设置
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
1.3.2 List
List跟java中列表集合一样,底层是链表实现;
使用场景:微博的关注列表、粉丝列表
常用命令:
命令 | 含义 |
---|---|
Lpush | 将一个或多个值插入到列表头部 |
Rpush | 在列表中添加一个或多个值 |
Lpop | 移出并获取列表的第一个元素 |
Rpop | 移除列表的最后一个元素,返回值为移除的元素 |
Lrange | 获取所有(一个或多个)给的key的值 |
1.3.3 Set
Set就是一个集合,集合的概念就是一堆不重复值的组合。利用Redis提供的set数据结构,可以存储一些集合性的数据。
使用场景:实现如共同关注、共同爱好、二度好友
常用命令:
命令 | 含义 |
---|---|
sadd | 添加一个或多个成员 |
spop | 移除并返回集合中的一个随机元素 |
smembers | 返回集合中的所有成员 |
sunion | 返回所有给定集合的并集 |
1.3.4 Sorted Set
Sorted set 的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级score的参数来为成员排序,并且是插入有序的,即自动排序。
使用场景:排行榜、按照用户投票和时间排序
常用命令:
命令 | 含义 |
---|---|
zadd | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
zrange | 通过索引区间返回有序集合中指定区间内的成员 可以附带withscores返回分数 |
zrem | 移除有序集合中的一个或多个成员 |
zcard | 获取有序集合的成员数 |
1.3.5 Hash
Hash是一个String类型的field和value的映射表
使用场景:存储部分变更数据,如用户信息
常用命令
hget | 获取存储在哈希表中指定字段的值 |
---|---|
hset | 将哈希表key中的字段field的值设为value |
hgetall | 获取在哈希表中指定key的所有字段和值 |
1.3.6 GEO
GEO 3.2版本开始对GEO (地理位置)的支持
使用场景:LBS应用开发
常用命令:
geoadd | 增加地理位置的坐标,可以批量添加地理位置 |
---|---|
geodist | 获取两个地理位置的距离 |
geohash | 获取某个地理位置的geohash值 |
geopos | 获取指定位置的坐标,可以批量获取多个地理位置的坐标 |
georadius | 根据给定地理位置坐标获取指定范围内的地理位置集合(注意该命令的中心点由输入的经度和纬度决定) |
georadiusbymember | 根据给定成员的位置获取指定范围内的位置信息集合(注意该命令的中心点是由给定的位置元素决定) |
1.3.7 Stream
Stream5.0 版本开始的新结构 流
使用场景:消费者生产者场景,类似MQ
常用命令:
xadd | 往指定的流中添加消息 |
---|---|
xlen | stream流中的消息数量 |
xdel | 删除流中的消息 |
xrange | 返回流中满足给定id范围的消息 |
xread | 从一个或者多个流中读取消息 |
xinfo | 检索关于流和关联的消费者组的不同的信息 |
1.4 安装运行
redis.conf配置文件注释:参考redis配置文件注解
2. 持久化
2.1 介绍
redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存在磁盘上,当redis重启后,可以从磁盘中恢复数据。
2.2 持久化方式
2.2.1 RDB持久化
1、RDB持久化能够在指定的时间间隔对你的数据进行快照存储。
2、客户端直接通过命令BGSAVE或者SAVE来创建一个内存快照:
BGSAVE调用fork来创建一个子进程,子进程负责将快照写入磁盘,而父进程仍然继续处理命令。
SAVE 执行SAVE命令过程中,不再响应其他命令
3、在redis.conf中调整save配置选项,当在规定的时间内,redis发生了写操作的个数满足条件会触发发生 BGSAVE命令
#在900s之内至少一次写操作
save 900 1
#300s之内至少发生10次写操作
save 300 10
#60s之内发生至少10000次写操作
save 60 10000
4、RDB优点和缺点
优点 | 缺点 |
---|---|
对性能影响最小 | 同步时丢失数据 |
RDB文件进行数据恢复比使用AOF要快很多 | 如果数据集非常大且CPU不够强(比如单核CPU),redis在fork子进程时可能会消耗相对较长的时间,影响redis对外提供服务的能力 |
2.2.2 AOF 持久化
(append only file)
AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会更新执行这些命令来恢复原始的数据
相关参数:
开启AOF持久化: appendonly yes
AOF策略调整:
#每次有数据修改发生时都会写入AOF文件
appendfsync always
#每秒同步一次,该策略为AOF的缺省策略
appendfsync everysec
#从不同步,高效但是数据不会被持久化
appendfsync no
AOF方式的缺点和优点:
优点 | 缺点 |
---|---|
最安全 | 文件体积大 |
容灾 | 性能消耗比RDB高 |
易读,可修改 | 数据恢复数据比RDB慢 |
3. 内存管理
3.1 不同数据类型的大小限制
- String类型:一个String类型的value最大可以存储512M
- List类型:List的元素个数最多为2^32-1个,也就是4294967295个
- Set类型:元素个数最多为2^32-1个,也就是4294967295个
- Hash类型: 键值对个数最多为2^32-1个,也就是4294967295个
#最大内存控制
maxmemory 最大内存阈值
maxmemory-policy 到达阈值的执行策略
3.2 内存压缩
#配置字段最多512个
hash-max-zipmap-entries 512
#配置value最大为64字节
hash-max-zipmap-value 64
#配置元素个数最多512个
list-max-ziplist-entries 512
#配置value最大为64字节
list-max-ziplist-value 64
#配置元素个数最多512个
set-max-intset-entries 512
#配置元素个数最多128个
zset-max-ziplist-entries 128
#配置value最大为64字节
zset-max-ziplist-value 64
备注:大小超出压缩范围,溢出后Redis将自动将其转换为正常大小
3.3 过期数据的处理策略
3.3.1主动处理
redis主动触发检测key是否过期,每秒执行10次,过程如下:
- 从具有相关过期的密钥集合中测试20个随机密钥
- 删除找到的所有密钥已过期
- 如果超过25%的密钥已过期,请从步骤1重新开始
3.3.2 被动处理
每次访问key时,发现超时后被动处理,清理掉
3.4 数据恢复阶段过期数据的处理策略
3.4.1 RDB方式
过期的key不会被持久化到文件中。
载入时过期的key,会通过redis的主动和被动方式清理掉。
3.4.2 AOF方式
当redis使用AOF方式持久化时,每次遇到过期的key redis会追加一条del命令到AOF文件,也就是说只要我们顺序载入执行AOF命令文件就会删除过期的键。
注意:过期数据的计算和计算机本身的时间是有直接联系的。
3.5 内存回收策略
配置文件中设置:maxmemory-policy noeviction
动态调整:config set maxmemory-policy noeviction
回收策略 | 说明 |
---|---|
noeviction | 客户端尝试执行会让更多内存被使用的命令直接报错 |
allkeys-lru | 在所有key里执行LRU算法 |
volatile-lru | 在所有已经过期的key里执行LRU算法 |
volatile-lfu | 使用过期集在密钥中使用近似LFU进行驱逐 |
allkeys-lfu | 使用近似LFU逐出任何键 |
allkeys-random | 在所有key里随机回收 |
volatile-random | 在已经过期的key里随机回收 |
volatile-ttl | 回收已经过期的key,并且优先回收存活时间(TTL)较短的键 |
3.5.1 LRU算法
LRU(Least recently used,最近最少使用):根据数据的访问历史记录来进行淘汰数据
- 核心思想:如果数据最近被访问过,那么将来被访问的几率也更高
- 注意:redis的lru算法并非完整的实现,完整的lru实现是因为需要太多的内存
- 方法:通过对少量keys进行取样(50%),然后回收其中一个最好的key
- 配置方式: maxmemory-samples 5
3.5.2 LFU算法
LFU(Least Frequently Used) 根据数据的历史访问频率来淘汰数据
- 核心思想:如果数据过去被访问多次,那么将来被访问的频率也更高
- redis实现的是近似的实现,每次对key进行访问时,用基于概率的对数计数器来记录访问次数,同时这个计数器会随时间推移而减小
- morris counter算法依据:
https://en.wikipedia.org/wiki/Approximate_counting_algorithm
- 启用LFU算法后,可以使用热点数据分析功能。redis-cli –hotkeys
4. 主从复制
4.1 解决问题及场景
使用主从复制原因:
- redis-server单点故障;
- 单节点QPS有限;
主从复制应用场景分析:
- 读写分离场景,规避redis单机瓶颈
- 故障切换,master出问题后还有slave节点可以使用
4.2 搭建主从复制
主Redis server以普通模式启动,主要是从服务器的启动方式:命令行、配置文件
4.2.1 命令行方式
#连接需要实现从节点的redis,执行下面的命令
slaveof [ip] [port]
4.2.2 配置文件方式
#配置文件中增加
slaveof [ip] [port]
#从服务器是否只读(默认yes)
slave-read-only yes
退出主从集群的方式:
slaveof no one
新版本中: slaveof 将由 replicaof 替换
命令行中: info replication 可以查看集群信息
4.3 主从复制核心知识
- redis默认使用异步复制,slave和master之间异步地确认处理的数据量
- 一个master可以拥有多个slave
- slave可以接受其他slave的连接,slave可以有下级sub slave
- 主从同步过程在master侧是非阻塞的
- slave初次同步需要删除旧数据,加载新数据,会阻塞到来的连接请求
4.4 主从复制应用场景
- 主从复制可以用来支持读写分离
- slave服务器设定为只读,可以用在数据安全的场景下
- 可以使用主从复制来避免master持久化造成的开销。master关闭持久化,slave配置为不定期保存或是启用AOF。(注意:重新启动的master程序将从一个空数据集开始,如果一个slave试图与它同步,那么这个slave也会被清空,该问题可以使用将一个从节点设置为主节点后再启动需要重新启动的原主节点,或者保存从节点的持久化文件复制到主节点重启解决)
4.5 主从复制的注意事项
4.5.1 读写分离场景
- 数据复制延时导致读到过期数据或者读不到数据(网络原因、slave阻塞)
- 从节点故障(多个client如何迁移)
4.5.2 全量复制情况下
- 第一次建立主从关系或者runid不匹配会导致全量复制
- 故障转移的时候会出现全量复制
4.5.3 复制风暴
- master故障重启,如果slave节点较多,所有slave都要复制,对服务器的性能,网络的压力都有很大影响
- 一个机器部署了多个master
4.5.4 写能力有限
主从复制还是只有一台master,提供的写服务能力有限
4.5.5 master故障情况下
- 如果是master无持久化,slave开启持久化来保留数据的场景,建议不要配置redis自动重启
- 启动redis自动重启,master启动后,无备份数据,可能导致集群数据丢失情况
4.5.6 带有效期的key
- slave不会让key过期,而是等待master让key过期
- 在lua脚本执行期间,不执行任何key过期操作
5. Redis哨兵高可用机制
5.1 哨兵(Sentinel)机制核心作用
slaveof no one
客户端获取主服务器命令:sentinel get-master-addr-by-name masterName
5.2 核心运作流程
5.2.1 服务发现和健康检查流程
- 搭建redis主从集群
- 启动哨兵(客户端通过哨兵发现redis实例信息)
- 哨兵通过连接master发现主从集群内的所有实例信息
- 哨兵监控redis实例的监控状况
5.2.2故障切换流程
- 哨兵一旦发现master不能正常提供服务,则通知给其他哨兵
- 当一定数量的哨兵都认为master挂了
- 选举一个哨兵作为故障转移的执行者
- 执行者在slave中选取一个作为新的master
- 将其他slave重新设定为新master的从属
5.3 七大核心概念
- 哨兵如何知道redis主从信息(自动发现机制)
- 什么是master主观下线
- 什么是客观下线
- 哨兵之间如何通信(哨兵之间的主动发现)
- 哪个哨兵负责故障转移(哨兵领导选举机制)
- slave选举机制
- 最终主从切换的过程
5.4 哨兵启动和配置
启动命令: redis-server /path/to/sentinel.conf –sentinel
配置文件启动时指定,运行过程中会自动变更,记录哨兵的检测结果
#配置文件在sentinel运行期间是会被动态修改的
#sentinel如果重启时,就可以根据这个配置来恢复其之前所监控的redis集群的状态
#绑定IP
bind 0.0.0.0
#默认是yes,没指定密码或者指定ip的情况下,外网无法访问
protected-mode no
#哨兵的端口,客户端通过这个端口来发现redis
port 26380
#哨兵自己的ip,手动设定也可自动发现,用于与其他哨兵通信
sentinel announce-ip
#临时文件夹
dir /tmp
#sentinel监控的master的名字叫做mymaster 地址为 ip port 两个及以上的哨兵认定为死亡,才认为是真正的死亡
sentinel monitor mymaster ip port 2
#发送心跳PING来确定master是否存活
#如果master在一定时间范围内不回应ping或者回复了一个错误信息,那这个sentinel会主观(单方面)认为这个master已经不可用了
sentinel down-after-milliseconds mymaster 1000
#如果在该时间(ms)内未能完成failover操作,则认为该failover失败
sentinel failover-timeout mymaster 3000
#指定了在执行故障转移时,最多可以有多少个从redis实例在同步新的主实例,在从redis实例较多的情况下,这个数字越小,同步的时间越长,完成故障转移需要的时间就越长
sentinel parallel-syncs mymaster 1
5.5 哨兵如何知道主从信息
哨兵配置文件中,保存着主从集群中master的信息,可以通过info命令,进行主从信息的自动发现
5.6 主观下线sdown
主观下线:单个哨兵自身认为redis实例已经不能提供服务
检测机制:哨兵向redis发送ping请求,+PONG、-LOADING、-MASTERDOWN这三种情况视为正常,其他回复均为无效。
对应配置文件的配置项为:sentinel down-after-milliseconds mymaster 1000
5.7 客观下线odown
客观下线:一定数量值的哨兵认为master已经下线;
检测机制: 当哨兵主观认为master下线后,则会通过sentinel is-master-down-by addr 命令询问其他哨兵是否认为master已下线,如果达成共识(达到quorum个数),就会认为master节点客观下线,开始故障转移流程
对应配置文件的配置项: sentinel monitor mymaster ip port 2
5.8 哨兵之间如何通信
哨兵之间的自动发现
哨兵之间通过命令进行通信
哨兵之间通过订阅发布进行通信
备注:可要通过在命令行客户端中订阅相关的主题,查看哨兵之间的通信信息
查看相关主题:pubsub channels
订阅主体:subscribe 主题名称
5.9 哨兵领导选举机制
基于Raft算法实现的选举机制,流程简述如下:
- 拉票阶段: 每个哨兵节点希望自己成为领导者选举前每个sentinel都会随机睡眠一段时间后才开始拉票,避免了大家都同时触发
- sentinel节点收到拉票命令后,如果没有收到或同意过其他sentinel节点的请求,就同意该sentinel节点的请求(每个sentinel只持有一个同意票数)
- 如果sentinel节点发现自己的票数超过一半的数值,那么它将成为领导者,去执行故障转移
- 投票结束后,如果超过failover-timeout的时间内,没有进行实际的故障转移操作,则重新拉票选举
备注:以了解raft协议为主。
http://thesecretlivesofdata.com
http://thesecretlivesofdata.com/raft/
5.10 slave选举方案
从服务器实例中,按下面顺序依次筛选
1.slave节点状态,非S_DOWN、O_DOWN、DISCONNECTED
判断规则:(down-after-milliseconds*10)+milliseconds_since_master_is_in_SDOWN_state
SENTINEL slaves mymaster
2.优先级
redis.conf 中的一个配置项:slave-priority 值越小,优先级越高
3.数据同步情况
Replication offset processed
4.最小的run id
run id比较顺序:字典顺序,ASCII码
5.11 最终主从切换的过程
1、针对即将成为master的slave节点,将其撤出主从集群
自动执行: slaveof NO ONE
2、针对其他slave节点,使他们成为新master的从属
自动执行:slaveof new_master_host new_master_port
5.12 哨兵服务部署方案
6. Redis集群分片存储
6.1 为什么要分片存储
示例:公司用户量3千万,用户基本信息缓存在redis中,需要内存10G,如何设计Redis的缓存架构?
1、3千万用户,各种业务场景对用户信息的访问量很大(单台redis示例的读写瓶颈凸显)
2、单redis示例管理10G内存,必然影响处理效率
3、redis的内存需求可能超过机器的最大内存
故此,引出分片存储的技术。
6.2 官方集群方案
redis cluster是redis的分布式集群解决方案,在3.0版本推出后有效解决了redis分布式方面的需求,实现了数据在多个redis节点之间自动分片、故障自动转移、扩容机制等功能。
???图
6.3 搭建集群
- 准备6个独立的redis服务
- 通过redis-cli工具创建集群
- 检验集群
- 故障转移测试
- 集群扩容
- 集群节点删除
集群搭建参考redis集群搭建
6.4 集群常见问题
增加了slot槽的计算,是否比单机性能差?
共16384个槽,slots槽计算方式是公开的,HASH_SLOT=CRC16(KEY) mod 16384;
为了避免每次都需要服务器计算重定向,优秀的java客户端都实现了本地计算,并且缓存服务器slots分配,有变动时再更新本地内容,从而避免了多次重定向带来的性能消耗。
Redis集群大小,可以装多少数据?
理论上可以做到16384个槽,每个槽对应一个实例,但是redis官方建议最大1000个实例;
集群节点之间是怎么通信的?
每个redis集群节点都有一个额外的TCP端口,每个节点使用TCP连接与每个其他节点连接。检测和故障转移这些步骤基本和哨兵模式类似;
ask和moved重定向的区别?
重定向包括两种情况:
- 若确定slot不属于当前节点,redis会返回moved
- 若当前redis节点正在处理slot迁移,则代表此处请求对应的key暂时不在此节点,返回ask,告诉客户端本次请求重定向
数据倾斜和访问倾斜的问题
倾斜导致集群中部分节点数据多,压力大。解决方案分为前期和后期:
- 前期是业务层面提前预测,哪些key是热点,在设计的过程中规避
- 后期是slot迁移,尽量将压力分摊(slot调整有自动rebalance、reshard和手动)
slot手动迁移怎么做?
节点之间会交换信息,传递的消息包括槽的信息,带来带宽的消耗
避免使用大的一个集群,可以分为多个集群
pub/sub发布订阅机制
对集群内任意一个节点执行publish发布消息,这个消息会在集群中进行传播,其他节点接收到发布的消息
读写分离
- redis-cluster 默认所有从节点上的读写,都会重定向到key对接槽的主节点上
- 可以通过readonly设置当前连接可读,通过readwrite取消当前连接的可读状态
注意:主从节点仍存在数据不一致的问题
6.5 非官方集群方案
codis A Redis Cluster Solution:豌豆荚团队开源产品
twemproxy:推特开源产品
7. Redis监控
7.1 Monitor命令
monitor 是一个调试命令,返回服务器处理的每个命令。对于发现程序的错误非常有用。出于安全考虑,某些特殊管理命令CONFIG不会记录到Monitor输出。
注意: 运行一个monitor命令能够降低50%的吞吐量,运行多个会降低更多;
7.2 info命令
info命令以一种易于理解和阅读的格式,返回关于redis服务器的各种信息和统计数值;
info命令 | 返回信息 |
---|---|
server | 服务器一般信息 |
clients | 客户端的连接部分 |
memory | 内存消耗相关信息 |
persistence | 持久化相关信息 |
stats | 一般统计 |
replication | 主从复制信息 |
cpu | 统计cpu的消耗 |
commandstats | redis命令统计 |
cluster | 集群信息 |
keyspace | 数据库相关统计 |
注:可以通过section返回部分信息
参考info命令详解文档
7.3 图形化监控工具-RedisLive
参考redisLive安装文档