Redis知识概念思维导图
2023-12-03 13:43:12 0 举报
AI智能生成
Redis相关知识梳理
作者其他创作
大纲/内容
秒级 10w 关系型数据库 一般是千级别就差不多了
单线程为什么还这么快
因为redis是基于内层模型的,寻址快(ns级),磁盘寻址是ms级,是磁盘寻址的10w 倍
单线程,避免了不必要的上下文切换和竞争条件,不用去考虑各种锁的问题
数据结构简单,操作也简单
epoll(eventpoll)使用多路I/O复用模型,非阻塞IO
IO 的发展进程
1、BIO (Blocking I/O) 阶段
每个连接都有文件描述fd,所有连接都会先到达内核,线程会read文件描述符
早期socket 在 read(fd) 的时候,如果fd没有读到,这时这个read就会一直阻塞等着,会浪费大量的CPU利用率
所以叫 Blocking I/O 即 BIO
早期socket 在 read(fd) 的时候,如果fd没有读到,这时这个read就会一直阻塞等着,会浪费大量的CPU利用率
所以叫 Blocking I/O 即 BIO
2、NIO (Nonblock I/O) 阶段
为了解决BIO问题,后来又出现了NIO,
通过一个线程来轮询这些 要调用的 fd ,比如 先去调用 a 文件描述符,如果没到,就会查看下一个 fd 是否能获取到,依次循环操作,
但是这样 如果有 1000 个fd,就要至少轮询调用 1000 次内核(成本太高),需要一次一次的去获取,资源浪费严重
后来对内核进行了改造,多线程处理(线程多了又会增加额外的成本,比如资源抢占,锁资源等的损耗)
但是这样 如果有 1000 个fd,就要至少轮询调用 1000 次内核(成本太高),需要一次一次的去获取,资源浪费严重
后来对内核进行了改造,多线程处理(线程多了又会增加额外的成本,比如资源抢占,锁资源等的损耗)
轮询是发生在用户空间,调用时在内核空间,出了调用内核资源浪费之外还有一个问题
3、NIO + select调用 阶段
通过对内核增加了一个系统调用select,减少用户空间访问内核空间的次数
用户空间直接调用 select ,select 会返回已经有的文件描述符(多个),然后根据select 查到的已有的 fd 去针对性的 read(fd)
减少了用户空间和内核空间的切换
这个阶段还有一个问题就是对这些 文件描述符的频繁拷贝,就显得比较累赘,后期又做了优化
4、NIO + select调用 + 共享区域 阶段
内核区域和用户区域共同维护了一块区域、共享空间
内核实现
通过mmap调用
主要是解决用户空间和内核空间频繁拷贝fd的问题
这样用户态和内核态就只需要维护一份 fd 的数据即可
epoll是Linux内核为处理大批量文件描述符而作了改进的poll
传统的select/poll的一个致命弱点就是当你拥有一个很大的socket集合,由于网络延时,任一时间只有部分的socket是“活跃”的
但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降
但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降
但是epoll不存在这个问题,它只会对“活跃”的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的
概念相关
客户端访问redis的时候,redis从socket中只拿到的是字节流,并没有拿字符流。只存字节,这样客户端和服务端在进行数据交换的时候,数据就不会被破坏,只要保证有统一的编解码就行
redis 存的值底层都是字节类型(没有数据类型)
语法
Redis 2.6.12 版本开始,
SET 命令的行为可以通过一系列参数来修改
SET 命令的行为可以通过一系列参数来修改
EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value
PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecondvalue
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value
XX :只在键已经存在时,才对键进行设置操作
NX 或 XX 可以和 EX 或者 PX 同时使用
如:SET key value EX seconds NX
EX 和 PX 可以同时出现,但是后面的选项会覆盖前面的选项
如: SET key value EX seconds PX milliseconds
存储类型
keys
遍历所有的key,弊端:数据量太大可能会产生卡顿
scan
语法
SCAN cursor [MATCH pattern] [COUNT count]
cursor:游标
pattern:模式
count:返回的元素个数,默认是10
scan的返回值有两个
一个是返回的游标
另一个是取到的key 的集合
String
SET key value
SETEX key seconds value (增加时效)
SET key value EX seconds (SETEX的另外一种写法)
SET key value XX 表示只有在KEY 存在的时候才可以设置
SETNX key value (key不存在时设置成功,成功返回true,否则返回false)
GETSET key value (设置新值,并返回旧值,减少一次I/O)
Bitmap
概念相关
value 只能是 0 和 1
二进制的每一位都是有索引的,也叫偏移量
示例:上面标识value,下面标识偏移量
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
语法
SETBIT key index 1[0] 将第index个位置设置成1 或者 0
GET key 获取的是ascii字符集
STRLEN 获取字节长度
setbit k1 9 1 后为 010000001 01000000 占两个字节 strlen k1 为 2
BITPOS key bit [start] [end]
bit 取值是0/1返回字符串里面第一个被设置为1或者0的bit位
返回的位的位置始终是从0开始的,即使使用了start来指定了一个开始字节也是这样
BITCOUNT key start end 统计范围区间二进制位 1 出现的次数
BITTOP (二进制按位逻辑操作)
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN 对一个或多个 key 求逻辑并,并将结果保存到 destkey
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN,对一个或多个 key 求逻辑或,并将结果保存到 destkey
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN,对一个或多个 key 求逻辑异或,并将结果保存到 destkey
BITOP NOT destkey srckey,对给定 key 求逻辑非,并将结果保存到 destkey
常见使用场景
场景1 : 统计某个用户登录天数
场景2:统计活跃用户数
List
LPUSH key value1 value2 ... 添加元素 value1 value2 到list 中,添加在左边
lpush k1 a b c d e f 从左向里面push 水元素 内存中是 f e d c b a 排在最后的最后压进去
RPUSH key value1 添加元素 value1 到list 中,添加在右边
rpush k1 a b c d e f 从右向里面依次压数据 最后内存中 abcdef
LRANGE startIndex endIndex 范围查找(endIndex是-1时,表示最后一个索引) -1 负向索引
LPOP key 移除并返回第一个元素
RPOP key 移除并返回最后一个元素
LSET key index value 将key的第index个元素更新为value
LREM key -2 a 移除key 对应list 中 两个a元素,从右向左 2个
LREM key 2 a 移除key 对应list 中 两个a元素,从左向右 2个
LINDEX key 2 取第二个元素
LLEN key 获取key对应list 的长度
LINSERT key BEFORE|AFTER pivot value 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后
LTRIM key 2 -2 删除key对应list 第二个之前和倒数第二个之后的数据
BLPOP key1 [key2 ] timeout (阻塞单播队列)移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOP key1 [key2 ] timeout (阻塞单播队列)移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
Set (去重,乱序)
SADD key value1 value2 ...... 向集合添加一个或多个成员
SCARD key 获取集合的成员数
SMEMBERS key 返回集合中的所有成员
SPOP key 移除并返回集合中的一个随机元素
SDIFF key1 [key2] 返回第一个集合与其他集合之间的差异
SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中
SINTER key1 [key2] 返回给定所有集合的交集
SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中
SISMEMBER key member 判断 member 元素是否是集合 key 的成员
SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合
SRANDMEMBER key [count] 返回集合中一个或多个随机数
SREM key member1 [member2] 移除集合中一个或多个成员
SUNION key1 [key2] 返回所有给定集合的并集
SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中
SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素
Sorted Set (对元素进行排序,而不是插入顺序)
底层实现
Skip List 跳表
新增一个元素的时候 随机造层(每次插入 会随机要不要造层)
排序原理
每个成员都分配了一个分数值(score),它用于在Sorted Sets中进行成员排序,从最小值到最大值
Sorted Sets中所有的成员都是唯一的
其分数(score)是可以重复的,即是说一个分数可能会对应多个值
如果多次添加相同的值到Sorted Sets上,redis会以最后一次的值分数为准
语法
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZCARD key 获取有序集合的成员数
ZCOUNT key min max 计算在有序集合中指定区间分数的成员数
ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment
ZINTERSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中
ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合指定区间内的成员
ZRANGEBYLEX key min max [LIMIT offset count] 通过字典区间返回有序集合的成员
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员
ZRANK key member 返回有序集合中指定成员的索引
ZREM key member [member ...] 移除有序集合中的一个或多个成员
ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员
ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员
ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员
ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到低
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序
ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
ZSCORE key member 返回有序集中,成员的分数值
ZUNIONSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的并集,并存储在新的 key 中
ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值)
Hash
适合用于存储对象
是一个 string 类型的 field(字段) 和 value(值) 的映射表
每个 hash 可以存储 232 - 1 键值对(40多亿)
语法
HSET key field value 将哈希表 key 中的字段 field 的值设为 value
HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值
HKEYS key 获取所有哈希表中的字段
HVALS key 获取哈希表中所有值
HLEN key 获取哈希表中字段的数量
HMGET key field1 [field2] 获取所有给定字段的值
HMSET key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中
HDEL key field1 [field2] 删除一个或多个哈希表字段
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在
HGET key field 获取存储在哈希表中指定字段的值
HGETALL key 获取在哈希表中指定 key 的所有字段和值
HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment
HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对
消息订阅
概念相关
只有消费端监听以后,别人推送消息才能看得到
SUBSCRIBE ooxx // 消费者监听命令
PUBLISH ooxx hello //向 ooxx 通道发送一条消息 hello
这样监听这个通道的消费者就能看到这条消息
PUBLISH ooxx hello //向 ooxx 通道发送一条消息 hello
这样监听这个通道的消费者就能看到这条消息
命令
PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道
PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态
PUBLISH channel message 将信息发送到指定的频道
PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道
SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息
UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道
拓展(假设用消息订阅做聊天软件,但不建议)
可以看实时消息
看实时消息可以通过 pub sub 实现
可以看历史消息
实现
架构图
查看3天内的热数据
可以利用sort set 进行规划,
用于维护3天的数据(score 可以设置成时间戳,便于排序),
有效期设置成3天
最后通过 zremrangebyscore username min max 获取数据
用于维护3天的数据(score 可以设置成时间戳,便于排序),
有效期设置成3天
最后通过 zremrangebyscore username min max 获取数据
查看3天之前的冷数据
去数据库查
全量数据一定是在数据库
管道 pipeline
一批命令压到一起,执行,减少client 和server 之间的通信次数
局限性
鉴于Pipepining发送命令的特性,Redis服务器是以队列来存储准备执行的命令,
而队列是存放在有限的内存中的,所以不宜一次性发送过多的命令。
如果需要大量的命令,可分批进行,效率不会相差太远滴,总好过内存溢出
而队列是存放在有限的内存中的,所以不宜一次性发送过多的命令。
如果需要大量的命令,可分批进行,效率不会相差太远滴,总好过内存溢出
由于pipeline的原理是收集需执行的命令,到最后才一次性执行。
所以无法在中途立即查得数据的结果(需待pipelining完毕后才能查得结果),
这样会使得无法立即查得数据进行条件判断
所以无法在中途立即查得数据的结果(需待pipelining完毕后才能查得结果),
这样会使得无法立即查得数据进行条件判断
事务
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
事务中的所有命令都会序列化、按顺序地执行。
事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
事务中的命令要么全部被执行,要么全部都不执行
语法
MULTI 标记一个事务块的开始
EXEC 执行所有事务块内的命令
DISCARD 取消事务,放弃执行事务块内的所有命令,EXEC 执行前调用
WATCH key [key ...] 监视一个(或多个) key ,
如果在事务执行之前这个(或这些) key 被其他命令所改动
那么事务将被打断
如果在事务执行之前这个(或这些) key 被其他命令所改动
那么事务将被打断
执行流程图片
可以通过WATCH达到Redis事务回滚的特性
在开启事务之前,对某一个key 进行 WATCH 操作
当在执行事务内部的命令的时候,
如果内部对这个key做了修改操作,
则这个事务队列中的命令都将不会执行
如果内部对这个key做了修改操作,
则这个事务队列中的命令都将不会执行
当然无论是否回滚,Redis都会取消执行事务前的WATCH命令
UNWATCH 取消 WATCH 命令对所有 key 的监视
注意的问题
事务在执行过程中,如果事务内的某一条命令发生错误,就会跳过这一条命令,执行后面的
EXEC执行完后,结果就是 除了这条错误命令失败,其它命令都成功了
EXEC执行完后,结果就是 除了这条错误命令失败,其它命令都成功了
如果有两个事物队列 A和B,如果A的MUTLI先到与B的,但是B的EXEC先到于A
这种情况是先执行 B 的事务队列, EXEC 先到先执行
这种情况是先执行 B 的事务队列, EXEC 先到先执行
为什么Redis 不支持回滚
Redis 的命令只会因为错误的语法而失败、
失败的命令是由编程错误而造成的,这些错误应该在开发过程中出现
失败的命令是由编程错误而造成的,这些错误应该在开发过程中出现
因为不需要对回滚进行支持,所以Redis 内部可以保持简单而且快速
执行错误是代码写得有问题,应该上线前发现,Redis为了保证快速高效,所以不支持回滚
分页问题(一般是缓存最新的热点数据)
数据万年不变或者变动很小的情况
直接事先分好页,将页码和对应的数据保存在redis中
数据量不是很多的情况
从redis 获取全量数据、内存进行分页处理
数据频繁改变的情况
用List存储(不可取)
通过 range 可以进行分页
弊端
redis是单线程的,但是放到业务里面就可能产生并发问题
当在高并发的情况下,线程A查询缓存为null的时候,会先去mysql获取,然后更新到redis
在A线程将数据添加到redis之前 B线程也从缓存中获取不到数据,这样也会从数据库中获取
这样A 和 B 都会想List中添加数据,List 数据会越来越多,可能会内存溢出,并且都是重复的,故不可取
当在高并发的情况下,线程A查询缓存为null的时候,会先去mysql获取,然后更新到redis
在A线程将数据添加到redis之前 B线程也从缓存中获取不到数据,这样也会从数据库中获取
这样A 和 B 都会想List中添加数据,List 数据会越来越多,可能会内存溢出,并且都是重复的,故不可取
基于Sort Set(ZSet)结构
特点:通过 Score 排序(自增值、时间戳)
Set 中数据不会重复
基于 Sort Set 和 Hash 结构
ZSet 中存 ID
Hash 中 ID 作为key 数据作为value
具体做法:先得到ID列表,然后根据ID,将数据从 Hash 结构中取出来
最后将没有取到Value的一批ID找出来,从数据库中获取数据即可
模糊查询问题
因为redis一般只缓存一部分热点数据,不会存全量的数据
所以在进行模糊查找的时候,很可能从redis缓存中获取不到全部的数据
所以一般不用redis处理大量的模糊查找需求
所以在进行模糊查找的时候,很可能从redis缓存中获取不到全部的数据
所以一般不用redis处理大量的模糊查找需求
硬要做的解决方案
查询率不高、数据量少
Mysql直接查找
查询率很高、数据量大
ES
查询率一般、数据量不是太多 (以商品名称为例)
1、将所有商品名称都作为key保存在Redis,value 为当前商品的 ID
2、模糊查询到数据后,客户端会选择某一条查看详情,再根据这一条数据的ID去查看详细信息
持久化
RDB (Redis Database)
快照/副本方式(持久化的是 save / bgsave 命令时那个时间点的快照数据)
是否开启rdb文件压缩配置
rdbcompression yes
触发方式
手动触发:执行save或者bgsave命令即可
自动触发:满足配置条件
save 900 1:表示 900 秒内如果至少有 1 个 key 值变化,则把数据持久化到硬盘
save 300 10:表示 300 秒内如果至少有 10 个 key 值变化,则把数据持久化到硬盘
save 60 10000:表示 60 秒内如果至少有 10000 个 key 值变化,则把数据持久化到硬盘
执行 shutdown时,如果没有开启aof,也会触发
save / bgsave
save
阻塞所有线程,直到持久化完成
服务器将不会再响应任何命令,直到持久化完成
bgsave
同样会阻塞线程,但是时间很短
只是fork一个子进程的时候阻塞,阻塞只发生在fork子进程的时候
子进程去完成持久化
正是因为子进程不会阻塞父进程,fork后没落盘的时候又做了更新
可能在fork子进程的时候和持久化的时候有些key的value可能会发生变化
可能在fork子进程的时候和持久化的时候有些key的value可能会发生变化
这样就违背了执行 save 命令那个时间点需要持久化的数据
怎么解决呢?-------- copy on write
copy on write(写时复制)
redis 内存中的数据保存的是一个虚拟地址,真实数据是计算机物理内存
linux 系统调用中有一个系统调用的fork()
fork() is implemented using copy-on-write
fork() is implemented using copy-on-write
创建子进程的时候会把原来父进程的虚拟内存拷贝一份,然后子进程的虚拟内存再指向物理内存
这个fork其实玩的是引用,速度很快,空间很小
所以在进行写数据到磁盘的时候,会根据这个引用找到物理内存中的数据
将这些数据进行持久化操作
将这些数据进行持久化操作
具有时点性
就是因为时点性,可能会丢失大量的数据
第一次持久化后,内存又产生了大量的未吃就好的数据,
这些数据还没有初始化的时候服务宕机,这些数据都会丢
这些数据还没有初始化的时候服务宕机,这些数据都会丢
优点
最灾难数据恢复有用,因为文件数据紧凑,更适合做备份文件
以二进制的方式保存到磁盘,文件传输块
与AOF相比,RDB 更容易启动
缺点
因为保存的是某一个时间点的数据,如果服务宕机,丢失的是一段时间的数据
如果数据量大,fork又是子线程,这样硬件设备不行的情况下,可能会影响主线程的调用(阻塞或延时)
AOF(Apend Only File)
会把每一个操作都记录下来,和Mysql中的Binlog 很像,
RDB只是持久化当前时间点的最终数据,不记录过程
RDB只是持久化当前时间点的最终数据,不记录过程
Redis 并不会将数据直接写入磁盘,而是写入cache,然后通过一定的策略flush 到磁盘
AOF 默认是关闭的 ---- appendonly no
no:redis 会先向缓冲区buffer写,写满后自动flush到磁盘,可能会丢失一个buffer的数据
always:每操作一次就做一次flush,数据可靠,最多丢失一条数据
everysec:每隔 1 秒flush 一次数据到磁盘,可能丢失 1 秒钟的数据
优点
丢失数据相对来说比 RDB 少
AOF文件过大的时候,会进行rewrite操作,不会影响客户的读写
缺点
因为会记录每个操作的过程,最后生成的文件体积比较大,恢复慢
AOF 是以文本的方式写入磁盘的,文件传输不及RDB的方式
做冷备份的时候,没有RDB方便
redis日志重写
重写目的:尽可能保证日志文件足够小
保留有效的日志,剔除没用的日志,如:set k1 2, set k1 3, set k1 4 最后有效的只是k1 4 其余的都剔除
保留有效的日志,剔除没用的日志,如:set k1 2, set k1 3, set k1 4 最后有效的只是k1 4 其余的都剔除
4.0 前
删除抵消命令,合并重复命令
4.0 后
将老的RDB load 到AOF 中,将增量以指令的方式 Append 到AOF文件中
AOF 的重写和RDB的快照一样,都需要 fork 一个子线程
rewrite 触发时机
4.0 前:执行bgrewrite 命令时
4.0 后:执行bgrewriteaof 命令时
4.0 后:执行bgrewriteaof 命令时
收到指令后,如果正在持久化操作,等待结束后 进行rewrite
redis.conf中,有一个配置大小的阈值,超过就会触发
启AOF的时,调用startAppendOnly函数会触发rewrite
混合持久化方式(Redis4.0后新增的方式)
如果RDB和AOF都开启了,只会用AOF恢复
数据恢复的时候,AOF会包含全量的RDB,让后Append 数据到AOF文件
AKF、主存复制、高可用
AKF
X、Y、Z 不同维度的扩展
主从复制
客户端可以访问主,也可以访问备
一般主负责写,从负责读(主也可以读)
一般主负责写,从负责读(主也可以读)
默认是异步的复制、特点是低延迟和高性能
默认情况下从是禁止写操作的
主从复制的实现方式
命令
REPLICAOF ip port
120.0.0.1:6380>REPLICAOF 120.0.0.1 6379 6380做6379的从
执行命令的时候,会把当前redis本身的数据剔除,这样才能保证同步
如果主挂了,恢复后还会追随主中的数据
如果主挂了,可以通过命令的方式将自己设置成主
REPLIACOF NO ONE:关闭当前服务器的复制并转变为主服务器
但是原来一起的兄弟从节点不会追从新的主redis
这点很容易理解,因为同步是通过 REPLICAOF ip port 配置的
这点很容易理解,因为同步是通过 REPLICAOF ip port 配置的
配置
replicaof <masterip> <masterport> 指定追随的主redis
masterauth <master-password> 主redis 的密码
replica-serve-stale-data yes
如果redis刚启动,要同步主的好几个G的数据的时候,要删除自己的数据(可能要一点时间),
这个配置表示在这个过冲中要不要把老数据返回给正在访问的客户端(yes标识允许)
这个配置表示在这个过冲中要不要把老数据返回给正在访问的客户端(yes标识允许)
replica-read-only yes 从redis 指支持读,不支持写
repl-diskless-sync no
先走磁盘,然后从的数据从磁盘同步,yes 表示直接通过网络IO同步到从redis
repl-backlog-size 1mb
增量复制
配置环形缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要完全复制master的数据,
如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。
如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。
min-replicas-to-write n
当master 的健康的slave数量 小于 n 的时候就禁止master写入操作, n=0 表示关闭该功能
min-replicas-max-lag n
延迟小于 n 秒的slave才认为是健康的slave
更多配置参考:https://www.cnblogs.com/guanshan/p/guan2019-10-09_001.html
高可用
客户端只能访问主
主机挂了,备机上,增强可用性
主机挂了,备机上,增强可用性
Sentinel 哨兵机制
单主,多从,多Sentinel(最好单数的节点)
一主、二从、三哨兵
定时检测主从关系,发现master挂了之后,就会选举一个slave作为leader,以发布订阅的方式通知其中的一个从节点切换为主,
Client 通过链接 Sentinel 节点来访问 主节点或者从节点
Sentinel 会检测 主节点的可用性
Redis Cluster 官方集群方案
多主、多从的方式(主要是分担主节点的压力)
Redis Cluster中有一个16384长度的槽的概念
这个槽是一个虚拟的槽,并不是真正存在的
正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽
数据一致性
强一致
同步处理主、从、备里面的数据
如果在写从的过程中出问题,就判定整个程序失败
会阻塞,或产生其它不应该产生的错误
弱一致
异步处理
可能会产生数据丢失
最终一致
通过MQ等组件实现,写入的时候,同步将数据交给MQ等组件,
MQ在慢慢将数据同步给其他redis
MQ在慢慢将数据同步给其他redis
脑裂问题
主、从、哨兵产生的脑裂问题
产生脑裂的额原因:sentinel 由于网络原因和Master断开连接了,将其中的一个Slave 提升为了 Master节点,
如果Client还在基于原来的Master写入数据,新的Master就同步不到这些数据了,
当Sentinel和原来的Master的通信恢复之后,就会把原来的Master节点降为Slave,再从新的Master同步数据,导致数据丢失的过程
如果Client还在基于原来的Master写入数据,新的Master就同步不到这些数据了,
当Sentinel和原来的Master的通信恢复之后,就会把原来的Master节点降为Slave,再从新的Master同步数据,导致数据丢失的过程
解决方案(避免数据丢失)
min-replicas-to-write X
min-replicas-max-lag Y
min-replicas-max-lag Y
Slave 节点小于 X,同步延时超过 Y 秒,原客户端就会拒绝客户端写入的操作(因为有一个Slave 被 Sentinel设置成主了,所以小于了X)
redis中的异步复制情况下的数据丢失问题也能使用这两个参数
不能为了技术而技术,Redis的主要用途是缓存、如果一定要保证一致性,考虑用其他的技术方案或者手段
集群模式下产生的脑裂问题(麻烦)
击穿、穿透、雪崩
击穿
数据库有数据,缓存中过期了(少量的key或一个key过期)
大量的并发来访问这个key的前提下(一定是高并发的情况)
不管是不是高并发的情况下,只需要有一个请求去访问数据库即可
所以只需要解决高并发情况下,
一个资源同事被多个请求访问的时候,只有一个可以访问数据库即可
一个资源同事被多个请求访问的时候,只有一个可以访问数据库即可
通过setnx去解决
类似一把锁,但不是锁
类似一把锁,但不是锁
1、大量请求过来的时候,取到的都是null的时候
2、这些线程都去做一个操作 setnx 同一个key
如果第一个setnx 服务挂了之后,就会产生死锁
可以通过设置一个过期时间来控制
可以通过设置一个过期时间来控制
如果加锁超时怎么办?——可以开启一个线程,用来检测和延长锁的时间
3、如果setnx 返回成功,就去DB获取数据,
setnx 返回false,就sleep一段时间,sleep结束后再去获取数据(乐观锁)
setnx 返回false,就sleep一段时间,sleep结束后再去获取数据(乐观锁)
穿透
接收查询的的数据本身在数据库中不存在
解决方案:布隆过滤器,后面有介绍
布隆过滤器的缺点是不能删除,可以用布谷鸟过滤器
穿透获取不到数据后,可以在Redis 中设置一个空 key
雪崩
和击穿很像,但是是大量的key同时过期,大量请求涌入数据库
解决方案
时点无关性的数据
解决方案
随机过期时间,尽量不要把过期时间设置到同一个点
时点性强的数据
比如 某些业务在 0 点整以后 取值一定要发生变化的情况
解决方案
1、业务逻辑判断,0点延时,即到0点的时候,先不要把流量引进来,系统先更新数据,更新完后再处理业务
2、强依赖击穿方案,通过锁控制
回收策略
内存有限、应该淘汰冷数据
当Redis 的maxmemory 限制达到限制时,
就可以通过maxmemory-policy配置回收策略
主要有以下配置:
就可以通过maxmemory-policy配置回收策略
主要有以下配置:
noeviction:返回错误
作为数据库用时一定用这个,作为缓存时一般不用
allkeys-lru:尝试回收最少使用的键(LRU)—— Least Recently Used 最近最少使用
volatile-lru:尝试回收最少使用且在过期集合中的键(LRU)
allkeys-random:随机回收
太随意 不建议用
volatile-random:随机回收过期集合中的键
太随意 不建议用
volatile-ttl:回收过期集合中的键,且优先回收TTL较短的键
时间复杂度较高,不建议用
Redis 如何淘汰过期的keys(过期判定原理)
有两种方式
主动
Client尝试访问它时,key会被发现,并主动过期
但是这样肯定是不行的,因为有些keys可能永远不能被访问的,造成空间的浪费
所以要定时随机测试设置keys的过期时间(被动处理)
所以要定时随机测试设置keys的过期时间(被动处理)
被动
Redis会每秒10次的去探测
测试随机的20个keys 进行相关的过期检测,并删除所有已经过期的keys
如果有多余25%的keys过期,就再随机20个keys做同样的操作,依次类推
这是一个频繁的概率算法
布隆过滤器
Redisbloom
redis 可以扩展一些库在 官网 redis.io 的module 中
如 redisbloom 布隆过滤器(下载解压得到io 文件)
如 redisbloom 布隆过滤器(下载解压得到io 文件)
在redis 启动的时候 通过- - loadmodule 加载 下载好的对应的库 xxxx.so 文件
redis-server —loadmodule xxxx.so /etc/redis/redis.cnf ——启动 /也可以配置在配置文件
redis-server —loadmodule xxxx.so /etc/redis/redis.cnf ——启动 /也可以配置在配置文件
这样redis 会多一些功能(命令),如:BF.ADD
BF.ADD k1 aaaa // 新增
BF.EXISTS k1 abc //返回1 存在
BF.EXISTS k1 abcd // 返回0 不存在
BF.ADD k1 aaaa // 新增
BF.EXISTS k1 abc //返回1 存在
BF.EXISTS k1 abcd // 返回0 不存在
使用场景(避免穿透)
原理:维护了一个bitmap
首先将要查找的字段的所有内容(如商品名称)调用 BF.ADD 这样就自动维护好了对应的bitmap
然后再查询的时候通过 BF.EXISTS 判断是否有值,这样就可以避免大量的击穿请求了
请求的可能被误标记,但是一定概率会大量减少放行:穿透,而且成本低
数据库中新增了数据一定要维护布隆过滤器
如果数据库中没有,已经穿透了一次,可以将当前搜索的key 缓存到数据库中,对应的值为 error
过程
映射流程
步骤
1、缓存中获取不到数据后,通过布隆过滤器来判断数据库中有没有,避免大量击穿
2、商品新增的时候通过多个映射函数维护 bitmap
3、多个映射函数的作用是降低穿透率,不考虑性能和内存的情况下越多越好
4、client 查找的时候,查找的内容用同样的映射
5、如果有一个映射到 0 ,就说明这个数据肯定不存在,如果都映射到1,则放行,可以去数据库查
实现方案
客户端实现bloom算法,自己承载bitmap,客户端过滤,redis只做缓存
客户端实现bloom算法,redis 缓存数据和 维护 bitmap
客户端啥也不敢,都交给 redis
一般都是这种,最省事
0 条评论
下一页