Redis学习记录:总结与扩展

  本文将个人在Redis数据库学习中记录的知识点进行了总结,方便日后的复习以及对明年春招实习的准备。

一、基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Redis是什么:
Redis是一种NoSql,即Not-Only-Sql。是一种"高性能键值对数据库",是对常规数据库的一种补充。

Redis的存在需求:
当一些数据需要被频繁操作时,不断的从常规sql中读取/更改数据会大大提高磁盘IO次数,消耗大量时间。
此时就需要一个中间件对热点数据进行储存与操作,从而提高操作效率。

Redis的思路:
通过'在内存进行储存'的思路,降低磁盘的IO次数。
通过'不储存关系,仅储存数据'的思路,去除数据间的关系,从而减少内存消耗。

Redis的优点:
1.可扩容,可伸缩
2.大数据下高性能
3.灵活的数据类型
4.高可用

Redis的速度快的原因:
1.内存操作
2.IO多路复用机制,减少了阻塞
3.单线程避免了线程切换的开销和竞争问题
4.C语言编写,与操作系统交互,命令执行快

Redis的使用场景:
1.对热点数据进行储存,提高热点数据的操作效率。
2.排行榜等实时变化性强的数据存储。
3.具有时效性的数据存储,方便删除与更新。
4....
总之,我认为Redis相当于一个缓存区,对于操作频率高的数据可以放在该缓存区里,方便客户端进行操作。

二、数据类型

1. 基础数据类型

① string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
string类型 最大纯数值范围为long long 512M 
'讲究整体性,更新少时用'

基础操作:
· set key value 添加修改键值对
· get key 根据键 获取值
· del key 根据键 删除键值对
· strlen key 根据键 获取值字符个数
· append key value 追加内容到原始信息尾部,不存在则新建

· mset key1 value1 key2 value2... 添加/修改多个键值对
· mget key1 key2... 根据键 获取多个值

设置数值增加指定的值: #实现按次案例 加一个负数即为减
· incr key
· incrby key increment
· incrbyfloat key increment

设置数值减少指定的值:
· decr key
· decrby key increment

设置数值具有指定的生命周期: #实现按时案例
· setex key seconds value
· psetex key milliseconds value

② hash

hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
hash类型 
'一个空间保存多个键值对数据'

基础操作:
· hset key field value 添加/修改 键/字段/
· hget key field 获取值
· hgetall key 获取全部值
· hdel key field1... 删除

· hmset key field1 value1 field2 value2... 多个添加/修改 键/字段/
· hmget key field1 field2... 获取多个值
· hexists key field 获取表中是否存在指定字段
· hlen key 获取字段数量

· hkeys key 查看所有字段名
· hvals key 查看所有字段值

· hsetnx key field value 添加 键/字段/值,有就不改,无则添加

设置数值增加指定的值: #实现按次案例 加一个负数即为减
· hincrby key field increment
· hincrbyfloat key field increment

注意:
1.value只能是字符串/数值,不能嵌套数据结构。
2.每个hash最多 2^32-1 个键值对
3.不可滥用,不可以全面作为对象列表使用
4.'hgetall'在field很多时,效率低,存在瓶颈
5.hash更新方便

③ list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
list类型 底层为双向链表 实现消息队列
'一个空间储存多个数据,且体现顺序'

· lpush key value1 value2... 从左 添加/修改数据
· rpush key value1 value2... 从右 添加/修改数据

· lrange key start stop 取start到stop的数据 #下标从0开始 -1为倒数第一个数据
· lindex key index 依据index索引取数据
· llen key 获取长度

· lpop key 从左 获取并移除数据 #类似java的pop
· rpop key 从右 获取并移除数据

# time单位为秒 当list内为空时,不会立刻返回,直到时间归零返回空,或者有人入队返回数据
· blpop key... time 从左 在规定的时间内获取并移除数据
· brpop key... time 从右 在规定的时间内获取并移除数据

· lrem key count value 移除指定数据 #count为移除数量,value为要移除的值

注意:
1.每个list最多 2^32-1 个元素
2.可以实现栈/队列
3.'-1'表示的是倒数第一
4.可以分页 #'lrange'取指定个数据

④ set

set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
set类型 hash结构 hash中field储存值 hash中value为空
'储存大量数据,查询效率高'

基础操作:
· sadd key member1 member2... 添加/修改 键/
· smembers key 获取全部数据
· srem key member1 member2... 删除数据
· scard key 获取集合数据总量
· sismember key member 判断集合中是否包含指定数据

# 可以实现随机推荐
· srandmember key [count] 随机获取集合中指定数量的数据
· spop key [count] 随机获取集合中指定数量的数据,并将其移出集合

# 可以实现相关推送、共同好友等
求集合的交、并、差集:
· sinter key1 [key2]
· sunion key1 [key2]
· sdiff key1 [key2]

# 数据合并
求集合的交、并、差集并储存到指定的集合中:
· sinterstore destination key1 [key2]
· sunionstore destination key1 [key2]
· sdiffstore destination key1 [key2]

# 数据合并
将指定数据从原始集合中移动到目标集合中:
smove source destination member

注意:
1.不会重复
2.与hash的储存空间相同,但是无法启用hash中储存值的空间 #全为空
3.去重可以实现记录访问数据
4.由上可以实现黑/白名单 #ID/IP/设备

⑤ sorted_set

sorted_set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
sorted_set类型 在set的基础上增加了score模块 可以根据score进行排序
'可排序的set'

基础操作:
· zadd key score1 member1 score2 member2... 添加/修改 键//
· zrem key member1 member2... 删除数据

依据排名获取全部数据: #withscores为值带分
· zrange key start stop [withscores] 小到大
· zrevrange key start stop [withscores] 大到小

依据分数获取全部数据: #withscores为值带分
· zrangebyscore key min max [withscores] 小到大
· zrevrangebyscore key max min [withscores] 大到小

按条件删除数据:
· zremrangebyrank key start stop 按从大到小的排名
· zremrangebyscore key min max 按分数

获取集合数据数量:
· zcard key
· zcount key min max

# 数据合并
求集合的交、并集并储存到指定的集合中:
· zinterstore destination numkeys key1 [key2] #后续参数为score最终结果:求和/最大/最小
· zunionstore destination numkeys key1 [key2]

获取数据对应的索引排名 #下标0开始
· zrank key member
· zrevrank key member

score值获取与修改:
· zscore key member
· zincrby key increment member #increment为加值

注意:
1.set的特性,反复添加会覆盖
2.long long为范围,64位,小数则为double,可能丢失精度
3.可以实现:时间队列、优先队列、VIP、权重

2. 高级数据类型

① Bitmaps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Bitmaps类型 
'状态统计'

· getbit key offset 获取指定key对应偏移量上的bit值
· setbit key offset value 设置指定key对应偏移量上的bit值 #只能是01

· bitop op destkey key1 key2... 进行'操作'结果保存到destkey
op: andornot非 xor异或

# 一个字节有八个bit 后面的startend是字节
· bitcount key [start end] 统计指定key中1的数量

注意:
1.可以进行单日数据的统计 例如某电影今日是否被点播
2.可以进行并操作来对多日数据进行合并等等
3.可以通过统计1的数量来实现类似'统计今日电影被点播种数'的功能

② HyperLogLog

1
2
3
4
5
6
7
8
9
10
11
12
HyperLogLog类型 底层为loglog算法 统计独立的元素个数
'基数统计'

· pfadd key element1 element2... 添加数据
· pfcount key1 key2... 统计数据
· pfmerge destkey sourcekey1 sourcekey2... 合并数据

注意:
1.HyperLogLog只进行基数统计,不记录具体数据
2.核心是基数统计算法,存在一定误差,大概是百分之0.81 #该算法中存储的都是近似值 所以有误差
3.该数据类型消耗内存最大为12K,相比set上M的内存消耗很小
4.HyperLogLog数据不断累积最大为12K,合并数据后结果消耗直接为最大的12K

③ GEO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GEO类型
'地理信息'

· geoadd key logitude latitude member [logitude latitude member] 添加坐标点
· geopos key member [member] 获取坐标点
· geodist key member1 member2 [unit] 计算距离 默认为m [unit]参数是m/km

根据坐标求范围内的数据:
· georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

根据点求范围内的数据:
· georadiusbymember key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

获取指定点对应的坐标hash值:
· geohash key member [member ...]

注意:
1.基本都是估算,存在误差,但是不追求高精度也够用
2.可以实现'附近的人'之类的功能

三、通用指令

1. key操作

基本操作:

1
2
3
· del key				删除指定的key
· exists key 获取key是否存在
· type key 获取key的类型

时效性控制:

1
2
3
4
5
6
7
8
9
10
11
12
为指定的key设置有效期: #前两个是秒/毫秒 后两个是时间戳
· expire key seconds
· pexpire key milliseconds
· expireat key timestamp
· pexpireat key milliseconds-timestamp

获取key的有效时间: #剩余秒/毫秒 xx为时效 -1为永久 -2为失效
· ttl key
· pttl key

切换key从时效性至永久
· persist key

查询模式:

1
2
3
keys pattern			查询key
pattern: *为所有 ?为匹配一个任意符号 []为匹配一个指定符号
eg: keys u[st]er:1 匹配一个以u开头,以er:1结尾,中间包含一个字母s/t的key

其他操作:

1
2
3
4
5
6
7
8
9
为key改名:
· rename key newkey #会覆盖
· renamenx key newkey #不会覆盖

对所有key排序:
· sort key #支持set list zset 只排序结果 不动原表

其他:
· help @generio

2. 数据库操作

1
2
3
4
5
6
7
8
9
10
11
12
切换数据库: #默认0 0-1516个 可以从配置文件里改总数
· select index

· quit 退出
· ping 测联通
· echo message 控制台日志

· move key db 数据移动

· dbsize 获取当前库中key的数量
· flushdb 删当前库
· flushall 删全部

四、Linux下Redis的配置相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
打开服务端:		· redis-server [--port 6379] 			
打开客户端: · redis-cli [-h ip] [-p host]

按配置文件打开服务端:
· redis-server redis-6379.conf

配置文件基础:
· port 端口 设置当前服务启动端口
· daemonize yes|no 若yes则以守护进程方式启动,日志不再打印到命令窗口
· logfile "xxx.log" 设置日志文件名,便于查阅
· dir "/.../redis/data" 设置当前服务文件保存位置 [日志/持久化等]
· bind 127.0.0.1 设置本机地址
· databases 16 设置库数量
· maxclients 0 设置最大连接数
· timeout 300 设置客户端限制最大时长 #0为关
· loglevel debug|verbase|notice|warning 设置日志级别 #默认是verbase 线上一般用notice简化日志
· include /path/server-host.conf 导入/加载指定conf 便于维护

注:
1.使用'ps -ef | grep redis-'命令即可筛选出redis相关进程
2.当以守护进程方式启动时,日志不再显示,可以通过上述ps命令找到进程,使用'kill -s 9 进程ID'关闭服务端

五、持久化

1
2
持久化主要分为两种: RDB与AOF
其中AOF是相对主流的持久化方案

1. RDB - 快照

相关:

1
2
3
4
5
6
7
8
9
首先,RDB的原理是生成一张当前redis的状态的快照(二进制压缩文件)。
恢复则是根据该快照进行全面的恢复。
快照的占用空间较少(因为进行了压缩),存取的速度相对较慢(全部保存),但恢复速度快(整体恢复)。

rdb保存分为两种: save 和 bgsave
· save是直接保存,当保存数据较多时,由于是单线程,会阻塞进程。
同步/阻塞/无额外内存/不启动新进程
· bgsave是后台保存,执行该命令后,redis新建子进程进行save保存,不会阻塞进程。但是CPU消耗会相对增大。
异步/不阻塞/有额外内存/启动新进程

配置文件相关:

1
2
3
4
5
6
7
8
9
10
11
12
13
· dbfilename dump-6379.rdb			设置本地数据库rdb名
· dir "/.../redis/data" 设置当前服务文件保存位置 [日志/持久化等]
· rdbcompression yes|no 储存本地库时是否压缩(LZF压缩) #通常开启 否则节省CPU运行时间 但是数据文件增大
· rdbchecksum yes|no 设置是否进行RDB文件格式校验(读/写时) #通常开启 否则节省约10%时间 但可能损坏数据

· stop-writes-on-bgsave-error yes|no 若bgsave出现错误,是否停止写入 #默认开启
如果为yes,redis会创建一个新的后台进程dump rdb。
假设创建快照需要20s时间,redis主进程在这20s内则会继续接受客户端命令。
若在这20s内创建快照出错,那么redis会拒绝新的写入。

# 满足在指定时间 second 中改变 changes 个 key 即会执行 bgsave
· save second changes 自动save #执行的是bgsave 根据业务量进行设置
在执行命令返回结果后,系统会对该命令进行判定,服务器成功执行一个数据库修改命令,则计数器+1

相关命令:

1
2
3
4
5
· save 					手动执行一次保存操作
· bgsave 手动执行一次后台保存操作

· debug reload 重启服务端并执行rdb
· shutdown save 关机并执行rdb

bgsave流程:

1
2
3
4
1.bgsave指令
2.发送指令至服务器
3.服务器调用fork函数生成子进程,并返回'Background saving started'
4.成功创建rdb文件后,返回成功消息至log日志文件

RDB优缺点:

1
2
3
4
5
6
7
8
9
优点:
1.效率高 使用压缩二进制保存
2.使用快照 进行全量复制
3.恢复快

缺点:
1.无法实时持久化 两次快照之间必定存在时间
2.牺牲了性能 快照时消耗较大
3.不同redis版本可能对rdb文件不兼容

2. AOF - 日志

相关:

1
2
3
4
5
6
7
AOF为记录数据产生的工程,即日志。
主要注重'实时性'

AOF有三种日志策略
· always(每次) 零误差,性能低
· everysec(每秒/默认) 准确性较高,性能较高,宕机丢失一秒数据 #每秒操作先存在aof缓存区中
· no(系统控制) 整体不可控,操作系统控制周期

配置文件相关:

1
2
3
4
5
6
7
8
9
10
11
12
13
· appendonly yes|no						是否开启aof #默认no
· appendfsync always|everysec|no 选择策略
· appendfilename appendonly-6379.aof 设置本地数据库aof名
· dir "/.../redis/data" 设置当前服务文件保存位置 [日志/持久化等]

· auto-aof-rewrite-min-size size 最小尺寸
· auto-aof-rewrite-percentage percentage 百分比
自动参数: #info查看
aof_current_size 当前大小
aof_base_size
条件: #满足则系统更新
1.aof_current_size > auto-aof-rewrite-min-size
2.(aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage percentage

AOF重写机制:

1
2
3
4
5
6
7
8
9
10
11
12
日志中大量重复命令存在会大大增加资源消耗,此时可以进行数据重写。

按照以下规律:
1.已超时数据不再写入文件
2.忽略无效命令 重写时使用进程内的数据生成 这样新的AOF文件只保存最终写入命令
3.对同一数据的多条命令合并为一条命令
注:在aof缓存区后,有aof重写缓存区提供数据

好处:
1.降低占用空间 利用率up
2.提高效率 降低时间 提高IO性能
3.恢复时间减少 提高恢复效率

相关命令:

1
· bgrewriteaof			手动重写 #和 bgsave 机制差不多

AOF流程:
流程
AOF与RDB对比:

项目 RDB AOF
占用空间 小(压缩) 大(重写)
存储速度
恢复速度
安全性 会丢失数据 依据策略不同
资源消耗 高/重量级 低/轻量级
启动优先 优先级低 优先级高
选择 呈现阶段有效性(如回档) 数据敏感
注:
1.灾难恢复一般选择RDB
2.可以进行双保险备份

六、事务

1
'一个队列中,一次性,顺序性,排他性的执行一系列命令'

事务

1. 基本指令

1
2
3
4
5
6
7
8
· multi				开启事务		执行后,后续指令加入事务
· exec 执行事务 执行事务,与multi成对使用
· discard 取消事务

注意:
1.出现语法错误后,销毁队列,所有不执行 #书写错误
2.出现命令执行错误后,对正确的命令执行,错误的不执行
3.redis事务执行后不会回滚,需要程序员自己备份

2. 锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
监视锁/乐观锁:
· watch key1 [key2] 添加监视锁
· unwatch 取消监视锁
如果在事务提交之前,监视的key进行了变动,则该事务失效

公共锁/悲观锁: #个人觉得类似线程锁
· setnx lock-key value 设置公共锁
# 返回设置成功 说明有控制权
# 返回设置失败 则说明不具有控制权 进行排队/等待
· del lock-key 删除公共锁

为公共锁添加时间限定: #时间到了自动解锁 防止死锁
· expire lock-key seconds
· pexpire lock-key milliseconds
注:一般都是微秒或毫秒级,具体需要测试 #推荐: 最大耗时 * 120% + 平均网络延迟 * 110%

七、删除策略

1. 数据删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
·定时删除 #时间换空间
优点:节约内存
缺点:CPU压力大

·惰性删除 "过期后,不做处理,下次访问时删除,不过期则返回" #空间换时间
优点:节约CPU
缺点:内存压力大

·定期删除 "随机抽查,重点检查" #每秒消耗固定的CPU资源 可以自由调整
1.读取配置server.hz的值,默认为0
2.每秒钟执行server.hz次serverCron()
3.serverCron()会调用databasesCron()获取数据
4.获取数据后,调用activeExpireCycle()随机抽取数据检测
5.若该数据超时,则删除
注意:
1.抽取判定数据的时长为250ms/server.hz
2.W取值为ACTIVE_EXPIRE_CYCLE_LOOKUPS_PRE_LOOP属性值,若本次删除数据 > W*25%,则再次执行删除过程
3.若本次删除数据 < W*25%,则检查下一个库
4.有参数current_db记录本次检测执行到哪个库,下次继续即可
5.通常使用定期删除和惰性删除
删除策略 优缺点 特性 总结
定时删除 节约内存,无占用 不分时段占用CPU资源,频度高 拿时间换空间
惰性删除 内存占用严重 延时执行,CPU利用率高 拿空间换时间
定期删除 内存定期随机清理 每秒花费固定的CPU资源维护内存 随机抽查,重点抽查

2. 数据逐出

1
2
3
4
5
6
7
8
9
10
主要依靠"逐出算法" #可能失效
执行命令前,调用freeMemoryIfNeeded()检测内存是否够多
不够则临时删除一些数据清理空间

info命令可以看缓存hit和miss的次数,根据业务进行调优

conf配置:
· maxmemory 最大可使用内存 #默认为0即不限 通常设置50%以上
· maxmemory-sampless 设置每次选取待删除数据个数 #默认为5
· maxmemory-policy 设置删除策略 #选项见下图

策略

八、Redis集群

1. 主从连接

理论基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
我们建立服务器的最终目标是:
1.高并发 2.高性能 3.高可用
其中,高可用的理论目标是全年宕机低于315秒。

对此,我们可以建立主从连接来提高可用性,即一台主服务器对应多台从服务器。
主服务器只进行写操作,从服务器只进行读操作,读写分离提高效率。
而当主服务器出现问题时,可以让一台从服务器接替成为主服务器,从而保证服务器集群正常运行。

主从连接的特点:
1.读写分离 提高效率。
2.负载均衡 基于主从结构,配合读写分离,slave分担master负载。根据需求改变slave的数量,分担负载,提高并发与吞吐。
3.故障恢复 由于主从服务器上的数据是一致的,当需要恢复数据时,直接复制另一台服务器上的数据即可。
4.数据冗余持久化 由于是集群形态,相同数据在多个服务器上,造成数据冗余,易于持久化。
5.高可用基础 主从连接是高可用服务器的基础。

主从连接的方式:

1
2
3
4
5
6
7
8
方法一: 客户端发送命令
· slaveof <masterip> <masterport>

方法二: 启动服务器参数
· redis-server -slaveof <masterip> <masterport>

方法三: 服务器配置 #主流
· slaveof <masterip> <masterport>

主从复制的三个阶段:

  • 阶段一:建立连接阶段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    1.slave 	-	发送'slaveof ip port',申请建立连接
    2.master - 接收到指令,发送信息响应对方
    3.slave - 保存master的IP和端口
    4.slave - 与master建立socket连接
    5.slave - ping
    6.master - pong
    7.slave - 发送指令
    8.master - 验证授权
    9.slave - 发送指令 port端口
    10.master - 保存端口号

    相关:
    # 从属客户端发送命令
    · slaveof <masterip> <masterport> 主从连接
    · slaveof no one 主从断开连接

    · requirepass <password> master配置文件设置密码
    · config set requirepass <password> master客户端发送命令设置密码
    · config get requirepass
    · auth <password> slave客户端发送命令设置密码
    · masterauth <password> slave配置文件设置密码
    · redis-cli -a <password> 启动客户端设置密码
  • 阶段二:数据同步阶段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    服务器运行ID (runid) # info Server 查看相关信息
    · 是每一台服务器每次运行的身份识别码,一台服务器多次运行可用生成多个运行id
    · id由40位字符组成,是一个随机的十六进制字符
    · 用于识别身份,进行操作时需要首先进行id比对

    # 以下为全量复制 复制大部分整体
    1.slave - 发送指令'psync2 <runid> <offset>' # ? -1 此时不知道runid 和 offset
    2.master - 接收指令,执行bgsave生成RDB文件,记录当前的复制偏移量
    3.master - 发送'+FULLRESYNC runid offset',通过socket发送RDB文件给slave,期间接收客户端命令,offset发生了变化
    4.slave - 收到'+FULLRESYNC',保存master的runid和offset,清空当前全部数据,通过socket接收RDB文件,恢复RDB数据
    # 以下为部分复制 复制发送RDB文件期间master接收到的命令
    5.slave - 发送指令'psync2 runid offset'
    6.master - 接收命令,判定runid是否匹配,判定offset是否在复制缓冲区内
    7.master - 如果runid或offset有一个不满足,执行全量复制 #执行步骤2
    7.master - 如果runid和offset校验通过,offset与本机offset相同,忽略
    7.master - 如果runid和offset校验通过,offset与本机offset不相同,发送'+CONTINUE offset'
    通过socket发送复制缓冲区中offset到本机offset的数据
    8.slave - 收到'+CONTINUE',保存master的offset,接收消息后,执行bgrewriteaof恢复数据

    相关: #配置文件
    · repl-backlog-size 1mb 设置复制缓冲区大小
    · slave-serve-stale-data yes|no slave在同步期间是否关闭对外服务

    · master一般占用内存的50%-70%,剩下的30%-50%给bgsave和缓冲区用
    · 同时请求复制,发送的RDB文件多,所以需要错峰请求
    · 或者采用拓扑结构,一主多从改成树状结构,但是一致性会变差
  • 阶段三:命令传播阶段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    1.master发送命令:ping	/	slave发送命令:replconf ack offset
    2.master - 接收命令,判定offset是否在复制缓冲区内
    3.master - 如果不在缓冲区,则执行全量复制
    3.master - 如果在缓冲区,且offsetoffset相同,忽略
    3.master - 如果在缓冲区,且offsetoffset不同,发送'+CONTINUE offset'
    通过socket发送复制缓冲区中offset到本机offset的数据
    4.slave - 收到'+CONTINUE',保存master的offset,接收消息后,执行bgrewriteaof恢复数据

    相关:
    # 心跳机制 master:ping slave:REPLCONF ACK
    · repl-ping-slave-period 10 master不断'ping'的周期,判断slave是否在线
    # 当slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步
    · min-slaves-to-write 2 #保障数据稳定性
    · min-slaves-max-lag 10 #发送 REPLCONF ACK 命令 获取当前数量与延迟

    闪断闪联: 忽略
    短时间中断: 部分复制
    长时间终端: 全量复制
    主从复制常见问题:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

2. 哨兵

思路:

1
2
3
4
5
6
7
1.宕机master下线
2.找一个slave成为新master
3.通知所有slave连接新master
4.启动新的master与slave
5.全量复制/部分复制

其中观测下线服务器以及推选新master的服务器,就被称为哨兵。

结构与作用:

1
2
3
4
5
6
7
8
'分布式系统,监控全部'
· 监控 检查master和slave是否正常
· 通知 出问题时,通知哨兵和客户端
· 自动故障转移 断开master和slave连接,推选新的master,其他slave连接到新master,告知客户端新地址

注:
1.哨兵也是redis服务器,只是不提供数据
2.哨兵数量通常为单数,便于投票选举

哨兵基础配置文件与启动:

1
2
3
4
5
6
7
8
9
· port 26379											服务端口 通常最前加2
· dir "/.../redis/data" 哨兵工作信息储存目录
· sentinel monitor mymaster 127.0.0.1 6379 2 监控的主服务器 当有2个哨兵觉得它挂了,它就算挂了 #数量一半+1
· sentinel down-after-milliseconds mymaster 30000 30秒连接无响应,该哨兵认定它挂了
· sentinel failover-timeout mymaster 180000 180秒未同步完成,认定同步超时
· sentinel parallel-syncs mymaster 1 重连了以后,一次一台进行同步

· redis-sentinel sentinel-port.conf 启动哨兵
启动顺序: 主--

哨兵工作原理:
在这里插入图片描述

监控阶段
在这里插入图片描述
在这里插入图片描述

3. 集群

简介:

1
2
3
4
5
6
7
8
9
当redis单机提供的服务OPS不足,或者单机内存容量不足时,可用采用集群的方式解决上述问题。

架构:
· 集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果

作用:
· 分散单台服务器的访问压力,实现负载均衡
· 分散单台服务器的存储压力,实现可扩展性
· 降低单台服务器宕机带来的业务灾难

设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
数据存储设计:
· 通过算法设计,计算出key应该保存的位置
· 将所有的存储空间计划切割成16384份,每台主机保存一部分 #槽
每份代表的是一个储存空间,不少一个key的保存空间
· 将key按照计算出的结结果放到对应的存储空间

· 当一台新的服务器加入集群时,每台服务器拿出一部分存储空间(槽)给新的服务器,即可将新的服务器加入集群
· 增强可扩展性

内部通讯设计:
· 每个服务器数据库互相通信,保存各个库里储存空间(槽)的编号数据
· 如果客户端访问的数据库中有想要的数据,直接返回 #一次命中,直接返回
· 如果客户端访问的数据库中没有想要的数据,根据编号数据,去新的库里找 #一次未命中,告知具体位置
# 这样可以保证最多查询两次,可以返回结果,提高查询效率

相关配置与命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
配置:
· clusetr-enabled yes|no 设置加入cluster,成为其中的节点
· cluster-config-file <filename.conf> cluster配置文件名,该文件自动生成,仅用于查找文件并查询文件内容
· cluster-node-timeout <milliseconds> 响应超时判定,判定该节点是否下线或切换为从节点 #一秒连接一次
· cluster-migration-barrier <count> master连接的slave最小数量

命令:
· cluster nodes 查看集群节点信息
· cluster replicate <master-id> 进入一个从节点redis,切换其主节点
· cluster meet ip:port 发现一个新节点,新增主节点
· cluster forget <id> 忽略一个没有solt的节点
· cluster failover 手动故障转移

集群构建启动:
· 先把服务端全部启动,按照上面的配置文件
· ./redis-trib.rb create --replicas 1 127.0.0.1:6379
# 1代表一个master连一个slave 后面写master的IP:PORT

集群操控:
· redis-cli -c 登陆集群的客户端
· redis-cli -c -p 6382

九、企业级解决方案

1. 缓存预热

情况

1
启动后迅速宕机

问题排查

1
2
1. 请求数量较高
2. 主从之间数据吞吐量较大,数据同步操作频度较高

解决方案

1
2
3
4
5
6
7
8
9
前置准备工作:
1. 日常例行统计数据访问记录,统计访问频度较高的热点数据
2. 利用LRU数据删除策略,构建数据留存队列 (例如: storm与kafka配合)
准备工作:
3. 将统计结果中的数据分类,设置级别,redis优先加载级别较高的热点数据
4. 利用分布式多服务器同时进行数据读取,提速数据加载过程
实施:
1. 使用脚本程序固定触发数据预热过程
2. 如果条件允许,使用了CDN(内容分发网络),效果会更好

总结

1
2
3
· 缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。
· 避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。
用户直接查询事先被预热的缓存数据。

2. 缓存雪崩

情况

1
2
3
4
5
6
7
8
9
10
1. 系统平稳运行过程中,忽然数据库连接量激增
2. 应用服务器无法及时处理请求
3. 大量408/500错误页面出现
4. 客户反复刷新页面获取数据
5. 数据库崩溃
6. 应用服务器崩溃
7. 重启应用服务器无效
8. Redis服务器崩溃
9. Redis集群崩溃
10. 重启数据库后再次被瞬间流量放倒

问题排查

1
2
3
4
5
6
7
8
9
10
1. 在一个较短的时间内,缓存中较多的key集中过期
2. 此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据
3. 数据库同时接收到大量的请求无法及时处理
4. Redis大量请求被积压,开始出现超时现象
5. 数据库流量激增,数据库崩溃
6. 重启后仍然面对缓存中无数据可用
7. Redis服务器资源被严重占用,Redis服务器崩溃
8. Redis集群呈现崩塌,集群瓦解
9. 应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃
10. 应用服务器/redis/数据库全部重启,效果不理想

解决方案(理论)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.更多的页面静态化处理
(减少请求)

2.构建多级缓存架构
(Nginx缓存+redis缓存+ehcache缓存)

3.检测Mysql严重耗时业务进行优化
(对数据库的瓶颈排查:例如超时查询、耗时较高事务等)

4.灾难预警机制
监控redis服务器性能指标:
· CPU占用、CPU使用率
· 内存容量
· 查询平均响应时间
· 线程数

5.限流、降级
(短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问)

解决方案(方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.LRU与LFU切换
(内存替换算法)

2.数据有效期策略调整
· 根据业务数据有效期进行分类错峰 eg:A类90分钟,B类80分钟,C类70分钟
· 过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量

3.超热数据使用永久key
(避免key过期)

4.定期维护(自动+人工)
(对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时)

5.加锁
(慎用!)

总结

1
2
3
· 缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。
· 如能够有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%),
配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。

3. 缓存击穿

情况

1
2
3
4
5
6
1. 系统平稳运行过程中
2. 数据库连接量瞬间激增
3. Redis服务器无大量key过期
4. Redis内存平稳,无波动
5. Redis服务器CPU正常
6. 数据库崩溃

问题排查/分析

1
2
3
4
5
6
1. Redis中某个key过期,该key访问量巨大
2. 多个数据请求从服务器直接压到Redis后,均未命中
3. Redis在短时间内发起了大量对数据库中同一数据的访问

· 单个key高热数据
· key过期

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.预先设定
· 以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长
· 注意: 购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势

2.现场调整
(监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key)

3.后台刷新数据
(启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失)

4.二级缓存
(设置不同的失效时间,保障不会被同时淘汰就行)

5.加锁
(分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!)

总结

1
2
3
4
· 缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中redis后,
发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力。
· 应对策略应该在业务数据分析与预方面进行,配合运行监控测试与即时调整策略,
毕竟单个key的过期监控难度较高,配合雪崩处理策略即可。

4. 缓存穿透

情况

1
2
3
4
5
6
7
1. 系统平稳运行过程中
2. 应用服务器流量随时间增量较大
3. Redis服务器命中率随时间逐步降低
4. Redis内存平稳,内存无压力
5. Redis服务器CPU占用激增
6. 数据库服务器压力激增
7. 数据库崩溃

问题排查/分析

1
2
3
4
5
6
7
1. Redis中大面积出现未命中
2. 出现非正常URL访问

· 获取的数据在数据库中也不存在,数据库查询未得到对应数据
· Redis获取到null数据未进行持久化,直接返回
· 下次此类数据到达重复上述过程
· 出现黑客攻击服务器

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.缓存null
(对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟)

2.白名单策略
· 提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。
当加载正常数据时,放行,加载异常数据时直接拦截(效率偏低)
· 使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)

3.实施监控
实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比
· 非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
· 活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象
根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)

4. key加密
问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验
例如每天随机分配60个加密串,挑选23个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问

总结

1
2
3
· 缓存击穿访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次均访问数据库,导致对数据库服务器造成压力。
· 通常此类数据的出现量是一个较低的值,当出现此类情况时要及时处理并报警。
应对策略应该在临时预案防范方面多做文章。无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。

5. 性能监控指标

在这里插入图片描述
benchmark

1
2
3
4
5
6
7
8
9
10
· 命令
redis-benchmark [-h] [-p] [-c] [-n <requests]> [-k]

· 范例1
redis-benchmark
说明:50个连接,10000次请求对应的性能

· 范例2
redis-benchmark -c 100 -n 5000
说明:100个连接,5000次请求对应的性能

monitor

1
2
3
· 命令
monitor
打印服务器调试信息

slowlog

1
2
3
4
5
6
7
8
9
· 命令
slowlog [operator]
· get:获取慢查询日志
· len:获取慢查询日志条目数
· reset:重置慢查询日志

· 相关配置
slowlog-log-slower-than 1000 #设置慢查询的时间下线,单位:微妙
slowlog-max-len 100 #设置慢查询命令对应的日志显示长度,单位:命令数