Redis脑图,这一篇就够了
2024-10-31 22:46:00 0 举报
AI智能生成
"Redis脑图,这一篇就够了"是一份详细的知识图谱,涵盖了Redis的核心内容,包括数据类型、基本操作、高级特性和优化技巧等。它以直观的图形方式呈现出各种概念之间的联系,使读者能够快速理解并掌握Redis的关键知识。这份脑图适用于Redis初学者和有经验的开发者,帮助他们系统地学习和回顾Redis的相关知识。通过这份脑图,读者可以对Redis有更深入的理解,提高开发效率。
作者其他创作
大纲/内容
可以用来批量发送命令到redis
pipeline
订阅者订阅一个channel,发布者将消息发送到一个channel,所有订阅这个channel的订阅者都能收到这条消息。
publish ooxx hellosubscribe ooxx ==> 订阅者是收不到hello的,因为消息publish之前,没有订阅先订阅之后就能收到:subscribe ooxxpublish ooxx hello
先订阅,才能收到publish的消息
pub/sub
事务的命令会被放在一个队列中。按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务中的命令要么全部被执行,要么全部都不执行。
将一组命令放在同一个事务中进行处理
redis不支持回滚1. 保持redis的简单性2. 大多数错误都是由于程序导致的
multi之后能输入多条命令输入exec开始执行命令
multiset k1 1get k1exec
撤销一个事务
discard
相当于CAS,监听某个key的值,如果发生了变化,那么整个事务就不会被执行
watch k1multiset k1 1set k2 2exec如果k1发生了改变,那么后面的整个事务就不会执行
watch
命令
事务
redis的一些扩展库
一个数据本来就不存在,在缓存中没有,在数据库中也没有。当查询这类数据的时候,会直接穿透缓存,请求直接打到数据库,这就叫缓存穿透。
使用了bitmap。每个元素通过一些映射函数,计算出一个值,映射到bitmap的某一位上,并将这一位设为1。(可能存在不同的元素对应bitmap中相同的位置,或者不存在的元素计算出来的位置,bitmap都是1)在查询某个元素的时候,也是通过映射函数,计算出对应的bitmap中的位置。如果对应的位置都是1就说明元素可能存在,则放行。如果有一位是0,则一定不存在。例子中,元素1、2是存在的元素,元素3是查询的元素
1. 如果不存在的元素穿透了2. 那么客户端需要增加redis中的key,value标记不存在。下次查询的时候直接返回不存在,客户端再去处理
1. 如果数据库增加了元素2. 需要完成元素对bloom的添加
概率解决问题,不可能100%阻挡恶意请求能达到99%。剩下的1%无法拦截成本低,速度快。
使用方法(架子):客户端实现bloom算法,自己承载bitmap客户端实现bloom算法,redis承载bitmap客户端啥也不干,redis承载bloom和bitmap(这种更好)
布隆过滤器用来解决缓存穿透
缓存穿透
modules
pipeline、消息订阅、事务、modules
缓存数据不重要缓存不是全量数据缓存应该随着访问变化,应该放热数据
1,key的有效期,读操作会将有效期延长(面试)?不对!!2,发生写,会剔除过期时间(设置的过期时间会被删掉,导致没有过期时间)3,倒计时,使用expire设置多少秒之后过期,且redis不能延长。4,定时, 使用expireat, 指定在某个时间过期
定期删除Redis设定每隔100ms随机抽取设置了过期时间的key,并对其进行检查,如果已经过期则删除。
惰性删除所谓惰性策略就是在客户端访问这个key的时候,redis对key的过期时间进行检查,如果过期了就立即删除
如果定期删除没有删掉一些key,而且以后没有访问过这些key,导致惰性删除也没有删掉这些key,这时候就需要内存淘汰机制。为什么需要内存淘汰机制?(面试)
redis如何清除过期的key?
key的有效期
redis能够占用的内存是有限的。内存多大呢?可以通过maxmemory <bytes> 进行设置
内存满了,怎么处理?==> 内存淘汰机制通过maxmemory-policy 设置, 默认是noeviction,达到了就报错LFU(Least Frequently Used)淘汰使用频率最少的LRU(least recently used) 淘汰最久没有使用的Random 随机删除ttl 淘汰将要过期的
redis内存淘汰机制
redis key的内存淘汰策略
查询本来就没有的数据,缓存中没有,数据库中也没有。
布隆过滤器
解决方案:1. 接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return。2. 布隆过滤器 ==> 缺点: 添加删除数据库中的数据不方便,都得更新布隆过滤器。可以使用布谷鸟过滤器,添加删除元素方便。
一条key过期导致缓存中的数据失效,数据库中有这个数据,导致针对这个key的大量请求直接打到数据库。
缓存击穿
大量的key同时过期,导致大量的并发请求直接打到数据库。和击穿的区别就是,击穿指的是一条key过期,导致并发查询这个key的请求打到数据库。雪崩是大量key同时过期。
解决方案:分两种情况:1. 和时点性无关:什么时候key过期都无所谓。这时候可以使用 随机key过期时间。2. 和时点性有关,比如必须在零点过期,更新缓存数据,这时候就不能用随机key过期时间了。==> 强依赖击穿方案,让第一个请求获得锁之后,将所有的数据都更新回来。
缓存雪崩
缓存常见问题一定是在高并发的情况下才会发生。
自己实现:1. setnx2. 设置临时key过期时间3. 多线程(守护线程)延长key过期时间
redisson 和 zookeeper 做分布式锁!
Redis如何实现分布式锁?
redis作为数据库/缓存的区别
时点性:保存的是数据在某个时间点上的副本
使用linux 父子进程 的时候:父进程的数据,子进程可不可以看得到?常规思想,进程是数据隔离的!进阶思想,父进程其实可以让子进程看到数据!linux中:export的环境变量,子进程的修改不会破坏父进程父进程的修改也不会破坏子进程
管道:1,衔接,前一个命令的输出作为后一个命令的输入2,管道会触发创建【子进程】echo $$ | moreecho $BASHPID | more$$ 高于 |
管道(不重要)
阻塞,执行命令的同时,redis不在对外提供服务
redis主进程负责生成rdb文件
关机维护的时候,用save
save
非阻塞,redis同时对外提供服务
fork会创建一个redis子进程,创建出的子进程负责备份数据。创建子进程的时候,不会将内存中的数据拷贝一份,父子进程共享内存数据。(速度快,占用空间小)
写操作都发生在redis父进程,只有真正发生写操作的时候,物理内存会先出现要写的值,然后将父进程的虚拟地址指向新的物理内存地址。(copy on write)redis子进程看不到修改,备份数据不受影响。
fork() + copy on write
save 900 1 // 距离上次生成快照,超过了900s,并且有1个key发生了改变,自动触发bgsavesave 300 10 // 同上save 60 10000 //同上dbfilename dump.rdb //生成的 rdb 文件名dir /var/lib/redis/6379 // rdb文件存储在哪
配置文件中可以配置bgsave规则(标识是save,但是做的是bgsave,不严谨)
还可以手动在redis-cli 输入bgsave,触发
正常运行的时候,用bgsave
bgsave(background save)
只能生成一个rdb文件,需要自己备份某个时间点的数据
丢失数据相对多一些。时点与时点之间窗口数据容易丢失。8得到一个rdb,9刚要做一个rdb,挂机了
记录的是二进制数据,恢复速度相对较快
弊端/优点
RDB (Redis data base)快照/副本
记录写操作到日志文件中
由于记录了所有的写操作,所以丢失数据少
如果同时开启了,恢复的时候只会使用AOF文件
4.0以后,AOF中会包含一个RDB全量,同时会记录新的写操作
RDB和AOF可以同时开启
在写文件的时候,会先将数据写入到内核的缓冲区中,然后内核再将缓冲区中的数据写入硬盘。下面的参数就可以设置什么时候让内核去将缓冲区中的数据写入磁盘。
appendfsync always // 每来一个写命令就同步到磁盘。(基本不丢失,但是速度慢)appendfsync everysec // 每秒同步一次。(数据丢失相对少,速度快。推荐使用)appendfsync no // 让操作系统决定什么时候同步到磁盘。(可能会丢失一个缓冲区的数据)
appendfsync everysec专门由一个线程同步数据到磁盘
no-appendfsync-on-rewrite no 在发生重写的时候,不去将缓冲区中的内容写入磁盘。(主要是为了避免发生IO争抢。重写的时候可能一个进程正在生产rdb文件,同步数据到磁盘也是一个线程,就会发生IO争抢)
redis是内存数据库,写AOF文件会触发IO,导致redis变慢。怎么解决?配置文件中有参数可以调节什么时候将数据同步(sync)到硬盘
删除抵消的命令合并重复的命令
最终AOF文件也是一个纯指令的日志文件
4.0以前重写
将老的数据RDB到aof文件中将增量的以指令的方式Append到AOF文件
最终AOF文件是一个混合体,包含RDB和指令。利用了RDB的快。同时利用了日志的全量。
aof-use-rdb-preamble yes 参数配置
4.0以后重写
fork + 管道父进程通过管道将新产生的写命令传给子进程,子进程写入AOF文件
重写AOF文件是fork一个子进程做的
aof最小达到64mb的时候,触发rewrite。当大小增长达到原来大小的一倍的时候,触发rewrite。
auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb
日志文件可能无限变大,用AOF文件恢复的时候会很慢怎么解决?
二进制文件,最开头是REDIS,其它显示都是乱码
bgsave
rdb
记录的是写命令*2 代表的是写命令有几个部分。2表示的是两个部分$6 代表的是下面的命令有几个字节。6表示是6个字节
恢复速度快,同时是全量数据
4.0以后,会先将aof中老的写命令变成rdb,也记录在aof文件中。aof文件变成了混合体
重写aofbgrewriteaof
AOF
用作缓存,丢失数据无所谓,因为后面还有数据库保存了所有的数据,可以只用rdb,redis启动的时候,保证缓存里面有一部分数据但是如果用作数据库就需要aof来保证数据的完整性
实操
AOF (Append only File)日志文件
redis的持久化RDB、fork、copyonwrite、AOF
关系型数据库:表很大,性能下降?如果表有索引,增删改变慢。因为需要维护索引查询速度呢?1. 1个或少量查询依然很快2. 并发大的时候会受硬盘带宽影响速度(很多查询命中不同的4K page)
折中方案:缓存。memcache和redis
memcache和redis的区别(面试):1. redis提供了多种数据结构,例如字符串,列表,集合,有序集合等,而memcache没有,只支持键值对存储2. redis支持持久化,AOF和RDB两种方式,而memcache不支持持久化,重启之后,数据会丢失3.redis支持主从复制和分片,分布式系统中更加可用;而memcache不支持分片,需要客户端代码自己支持4.redis是单线程,memcache是多线程
Redis是一个内存中key value的数据库,value有五种数据类型。memcache也是key value数据库,value没有数据类型
解决方案:HANA:内存级别的关系型数据库。所有的数据都放在内存中价格太贵。
安装(不重要)
Redis引入
Redis是单进程,单线程,单实例的,为什么很快?(面试)1.基于内存的,内存的操作比磁盘块2.单线程、使用了IO多路复用技术,是一个线程就能处理很多客户端的请求3.高效的数据结构,经过优化之后,这些结构的操作很快
NIO原理
字符串操作。截取,append等
抢购,秒杀,详情页,点赞,评论规避并发下,对数据库的事务操作完全由redis内存操作代替
数值操作。累加,减法等
Redis是按照字节来存储的,每个字节是8位。每一位都有一个索引,比如第一个字节是0-7,第二个字节是8-15,就组成了bitmap,可以通过bitmap来操作二进制位,效率高
Redis提供了setbit、setbit、bitcount、bitpos、bitop等方法来操作某个位
如果使用mysql,关系型数据库,就需要存储用户的id,ip,登录时间等信息,用户每登录一次就得记录一条数据,浪费空间。查询的时候也很慢
使用bitmap。一年有365天,可以用46个字节足以表示。46个字节就是368位,每一位代表一年中的一天,当用户在某天登录的时候,就将对应的位设置成1。效率高,而且统计的时候速度也快,都是二进制操作。
使用bitmap,天为key,用户为value。value的每一位表示的是该位对应的用户是否登录,1表示登录过 u1 u2 u3 20190101 0 1 0 0 0 0 1 020190102 1 1 0 1 0 1 0 120190101 这天登录的有u2,20190102登录的有u1,u2
2. 618做活动:送礼物 , 大库备货多少礼物假设京东有2亿用户,需要去掉僵尸用户,冷热用户/忠诚用户需要统计活跃用户!随即窗口比如说 1号~3号 要连续登录,同时要去重
使用例子(面试)
BitMap
比如说,一个字符串encoding是raw,执行+1操作,redsi会先取出这个字符串,转成int,然后加1,如果加成功了,还会将encoding变成int。下次就不用转了,直接使用。
公司中使用redis需要约定编码,一般使用UTF-8
Redis是二进制安全的。存储的时候存储的是字节流,每一个字符一个字节,是二进制,不会做出任何改变,客户端取的时候也是一个个字节取。只要知道了encoding,就能保证数据的正确性。
stringbyte存储
相当于HashMap。key-value的value是一个hashmap,value也有key-value
hset k1 age 18 ==> k1位key, age为hasamp中的key,18位hashmap中的valuehmset k2 age 18 name gqhget k1 agehmget k2 age name ==> 输出 18 gqhkeys k1 ==> age namehvalues k2 ==> 18 gq
还能进行数值计算 ==>HINCRBY 、 HINCRBYFLOAT场景:点赞,收藏,详情页
hash
底层是一个双向链表, key中有head和tail两个属性,分别为头尾指针,指向链表的头和尾。
lpush k1 a b c ==> 每次放元素的时候都是放在上一个元素的左边lpop ==> 从左边取先取出来的是c这不就是栈? ==> 后进先出rpush 和 rpop同理只要使用同向命令就可以实现栈
栈同向命令
lpush k2 a b crpop ==> 取右边,取出来的是a这不就是队列? ==> 先进先出rpush和lpop同理只要使用反向命令就可以实现队列
队列反向命令
lindex k1 2 ==> 根据索引取值(取k1中索引为2的位置的值)lset k1 2 3 ==> 根据索引设置值(设置k1中索引为2的位置的值为3)这不就是数组?
数组
blpop k1 0 ==> 等着k1有值,没有就阻塞,0表示一直阻塞如果大于0的数,例如10,就表示阻塞10s ,如果没有数据就停止blpop k1 0 ==> 第二个客户端也阻塞相同的keyrpush k1 13 ==> 上面的阻塞就会停止,拿出压入k1的数据13
阻塞队列, 单播(阻塞时间最长的那个先拿到值,结束阻塞)
ltrim k1 0 2 ==> 保留在指定区间内的元素,删除不在区间内的元素
redis具有正负索引,取元素可以用索引取lrange 0 -1 可以取出所有元素
其他命令
list可重复,有序(添加元素的顺序)
【无序】&&【随机性】放入的多少不同,元素存储的顺序不同去重
sadd k1 1 2 3 2 4 3 ==> 添加元素。最终1 2 3 4
sinter 、 sinterstore ==> 交集sinter k2 k3sintertore dest k2 k3 ==> 取交集放在key为dest的set中sunion 、 sunionstore ==> 并集sdiff ==> 差集
集合操作(用的多)
场景:抽奖。 人数小于礼物数。存的是人的名字。例如7个人抽20张购物卡srandmember k2 -20 ==> 一定生成含有重复中奖的20个元素
SRANDMEMBER key countcount如果为,正数:取出一个去重的结果集,尽量满足指定的正数(不能超过已有集,例如集合中有7个元素,指定的count为5,则取出5个不重复的元素;指定的count为10,则会取出7个元素,尽量接近指定的count)负数:取出一个带重复的结果集,一定满足你要的数量。 指定-5则取出5个,元素会重复;-10就取出10个带重复的结果集如果:0,不返回
年会抽奖,一等奖一个,二等奖多个使用spop k2第一次pop的就是一等奖,第二次就是二等奖第一个
spop 一次随机取出一个
随机事件
set
去重,按分值排序(有正序和顺序)
物理内存左小右大,不随命令发生变化。默认是正序存储zrangezrevrange
zadd k1 8 apple 2 banana 3 orangezrange k1 0 -1 ==> 默认正序输出(banana orange apple)zrange k1 0 -1 withscoreszrangebyscore k1 3 8 ==> 取出分值3和8的元素zscore k1 apple ==> 取出分值zrank k1 apple ==> 取出排名zincrby k1 2.5 banana ==> 将banana的分值增加2.5
weight:可以对每个key指定权重,计算的时候分值会乘以权重
聚合SUM、MIN、MAX分值求和、取最小值、取最大值
两个集合都有的元素怎么处理?权重/聚合指令
集合操作并集、交集
Skip List跳表
排序怎么实现的?增删改查的速度?(面试)
sorted-set(zset)
基本数据类型(指的是key-value中value的类型)
1.单点故障2.容量有限3.能够承受的压力有限
AFKX:全量,镜像Y:业务,功能Z:优先级,逻辑再拆分
X轴是redis的一个全量镜像,使用多个redis,能够解决单点故障的问题,同时采用读写分离的模式,读操作打到一个redis,写操作打到别的redis,能够解决一部分压力问题。但是解决不了容量有限的问题Y轴按业务功能再进行划分,不同功能的数据存储在不同的redis中,能够解决容量有限的问题。 X轴方向同时可以做一个备份,全量镜像。Z轴按优先级、逻辑再拆分,一个业务的数据拆分到两个redis里面。例如以前是1-1000的数据放在一个redis里面,可以拆分成1-500和501-1000两个redis
怎么解决
单机存在的问题
redis触发一个写命令之后,必须等后面的redis都写成功,返回OK之后,才能给客户端返回OK。如果有一个redis写入不成功,就会给客户端返回失败,就会破坏可用性强一致性一定会破坏可用性、
强一致性
容忍丢失一部分数据前面的redis立即给客户端返回OK,后面的redis异步写入数据
特点是低延迟和高性能
redis使用的是弱一致性
弱一致性
写操作同步阻塞放入消息队列中,后面的redis什么时候有时间再去消息队列里面取,然后写入数据。使用消息队列保证最终数据一致性但是,另一个客户端从后面的redis中取数据的时候,可能取到不一致的数据
最终一致性
使用AKF,数据变多,怎么解决一致性问题?
数据一致性
主备是有主节点,和多个备份节点,但是备份节点不参与业务,客户端只能访问主,主挂了,备份节点接替
主从复制是有主节点和从节点,客户端能够访问主节点也能够访问从节点
主备和主从
对主做高可用。监控主的状态。如何进行监控?从节点进行投票,一部分给出主节点OK,另一部分给出主节点不OK。给出OK节点的数量,达到 n/2 + 1,表示主节点OK给出不OK节点的数量,达到 n/2 + 1,表示主节点不OK
投票数:n/2 + 1
why?拿3台和4台来说,都只允许有一台挂掉,但是成本不一样,4台比3台更容易出现一台故障。4台比3台更容易出现风险。
从节点的数量建议是奇数台
主也是单点。怎么解决
命令: replicaof
从节点默认只能读
刚开始追随的时候(以前没有追随过主节点),从节点会先flush掉自己老的数据,主节点会生成rdb文件,然后从主节点生成的rdb文件中读取数据。追随过后,从节点挂了,然后又起来,并且没有开启AOF,从节点就不会flush掉自己老的数据,主节点也不会生成rdb文件。如果开启了AOF,挂了又起来,从节点会flush掉老的数据,主节点会生成rdb文件,同时最后还会进行rewrite,生成AOF文件
从节点挂了
主可以知道有哪些从节点连接了它
启动多个哨兵,用哨兵监控主节点的状态,当主挂了之后,哨兵会自动选举新的主节点出来
port 26379 // 哨兵的端口sentinel monitor mymaster 127.0.0.1 6381 2 // 监控哪个master,master端口号,2表示投票有效数量(哨兵配置文件写法)
redis-server 哨兵配置文件 --sentinel // 启动一个哨兵节点
redis-sentienl // 也可以启动一个哨兵节点
配置
哨兵节点能够通过发布订阅的方式知道主节点上有哪些其它哨兵节点。同时能够直接从master知道,现在有哪些从节点。
sentinel (哨兵)
主节点挂了
replica-serve-stale-data yes 是否从节点要从主节点同步完数据,才能对外提供服务replica-read-only yes 从节点是否是read only的repl-diskless-sync no 主节点直接将rdb文件直接通过网络IO发给从节点,而不是先在磁盘上生成一个rdb文件,再通过网络IO发给从节点。 如果网络快,建议使用这种方式repl-backlog-size 1mb #增量复制 redis里面维护了一个队列,当从节点挂了一会然后又起来了,这时候要去主节点复制数据,会给主节点一个offset偏移量,将偏移量以后的数据从队列都复制过来。这个参数指定的就是这个队列的大小。如果指定的过小,则有可能再挂掉再起来的时间,数据比较多,一下就写满了1M,找不到offser以后的数据了,就会触发一个全量rdb。如果指定的够大,而且写的数据较少,那么就能从队列里面拿到需要复制的数据,就不需要全量复制。min-replicas-to-write 3 master最少得有多少个健康的slave存活才能执行写命令min-replicas-max-lag 10 延迟小于min-replicas-max-lag秒的slave才认为是健康的slave
主从复制
一致性
可用性
分区容忍性
只能同时满足两个,不可能同时满足三个
CAP原则
弊端:取模的数必须固定影响分布式下的扩展性。添加或者删除节点,得rehash
hash取模:modula
消息队列,类似kafka
random
hashcrc16crc32fnv等常用的映射算法
没有取模运算data和node节点同时参与映射计算
抽象出来了一个环形hash环,node经过映射算法,映射到环上的某一个点上。有数据来了,也通过相同的映射算法,映射到环上的一个点上,顺时针找一个最近的物理点,然后将数据放入物理点对应的node。(环上的点是虚拟的,但是node对应的点是物理的。)
优点:你加节点,的确可以分担其他节点的压力,不会造成全局洗牌缺点:新增节点造成一小部分数据不能命中1,问题,击穿,压到mysql2,方案:没去取离我最近的2个物理节点更倾向于 作为缓存,而不是数据库用!!!!
虚拟节点,解决数据倾斜问题。一台物理node,映射到环上的多个点上。
ketama,一致性hash算法
sharding分片,客户端实现三种模式
代理实现:twitter官方的twemproxy。用的较多
加代理层。代理层实现分片逻辑,而不是客户端实现。客户端变得简单轻盈。分为三种方式:modula 、random 、ketama
代理服务器只有一台的时候,代理压力又比较大。这时候,就可以将代理做成集群,代理集群前面再加一个LVS。LVS做一个主备,主备直接靠keepalived进行监控,进行主备切换,同时keepalived去监控所有的代理服务器的健康状态,如果发现有的代理挂了,会让LVS将请求发到活着的服务器上。LVS提供一个VIP,无论多少个客户端,项目组,api都只需要去通过这个VIP去访问后面的服务。
如果分片逻辑在client实现,所有的client都会连接redis,导致redis压力过大。代理层实现分区。
客户端可以任意连接一台redis,然后redis会将转发给正确的redis。相当于redirect重定向。
查询路由。Redis Cluster实现分区的方式。
预分区
Hash算法新增节点,所有的数据需要rehash一致性hash算法新增节点,造成数据无法被请求到
redis容量有限的问题,可以在客户端代码里面进行控制,将数据按照业务逻辑放到不同的redis上。如果数据不能拆分怎么办?
sharding分片/分区
redis的集群:主从复制、CAP、PAXOS
Redis
收藏
0 条评论
回复 删除
下一页