Redis 脑图
2021-08-19 15:02:06 92 举报AI智能生成
Redis 脑图
redis
模版推荐
作者其他创作
大纲/内容
Redis的安装
打开阿里云把文件copy到software里面<br>
cd /usr/local<br>mkdir software<br>tar -zxvf xxx.tar.gz
安装gcc-c++
yum install gcc-c++ <br>
编译依赖
cd usr/local/software/redis-5.0.10/deps<br>make hiredis lua jemalloc linenoise<br>
编译Redis
cd /usr/local/software/redis-5.0.10<br>make<br>
安装编译之后的文件
mkdir -p /usr/local/redis<br>make install PREFIX=/usr/local/redis (将redis 安装在该目录里面)<br>
验证安装是否成功
cd /usr/local/redis/bin<br>ls
将解压后的redis配置文件copy到/usr/local/redis文件夹下<br>
修改daemonize为yes 后台运行<br>
修改bind 为0.0.0.0
单线程
这里的单线程只是在网络IO和键值对读写上是单线程,其他处理还是会使用多线程,例如AOF
redis里面是单线程,想支持并发连接怎么办<br>
想使用一个单线程去支持高并发,那么线程就不能阻塞
让一个线程去管理多个连接
<b><font color="#c41230">io多路复用技术</font></b>
redis是单线程,为什么这么快
redis是单进程单线程模型,避免了线程的切换,可以避免锁,
redis的大量操作是在操作内存
io多路复用<br>
缺点
无法发挥多核cpu的性能<br>
可以通过多开Redis实例完善<br>
Redis是菲关系型数据库,数据与数据之间没有联系
为什么需要多线程
平衡计算机硬件之间的差异
比如cpu要到磁盘读取数据,cpu就需要等待磁盘将数据<br>放入内存 ,这段时间很长,多线程的话CPU就不需要等待<br>,直接去处理其他的请求,等到磁盘的数据到内存中,再来处理<br>该数据
相关命令
key的相关命令<br>
<b><font color="#c41230">keys *</font></b>
获取当前库的所有key<br>
<b><font color="#c41230">del k</font></b><br>
删除key
<b><font color="#c41230">exists k</font></b><br>
判断是否存在key
db的相关命令<br>
<b><font color="#c41230">select index</font></b>
切换库
<b><font color="#c41230">dbsize</font></b><br>
查看当前库key的数量
<b><font color="#c41230">flushdb</font></b>
清空当前库
<b><font color="#0076b3">flushall</font></b>
清空所有库0-15(不安全)
Redis的数据类型
<b>String</b>
<b><font color="#c41230">可做简单的k-v缓存,实现计数器、分布式锁、session共享、分布式ID生成(自增)</font></b>
<b><font color="#c41230">redis底层是C,为什么<br>不用c字符串而用sds</font></b>
获取长度
C字符串并不记录自身长度,想获取长度只能遍历
sds直接获取len即可
内存分配
c字符串每次长度变化都会对数组进行内存重新分配,比较耗时
对sds内容进行修改或者需要扩展时,sds有空间预分配和惰性空间释放
缓冲区安全
C字符串不记录自身长度,不会自动进行边界检查,所以会增加溢出的风险
sds先检查空间是否满足修改所需的要求,如果不满足就先扩容再进行修改
二进制安全
C字符串是以空字符串(\0)结尾,所以字符串中不能包含空字符串,只能保存文本数据
既能保存文本数据,也能保存二进制数据(通过长度判断结束,不受影响)
<b><font color="#c41230">相关命令</font></b>
<b><font color="#c41230">set k v</font></b>
<b>存放一个k-v对</b>
<b><font color="#c41230">get k</font></b>
<b>获得k对应v</b>
<b><font color="#c41230">mset k1 v1 k2 v2 ...</font></b>
<b>存放多个k-v</b>
<b><font color="#c41230">mget k1 k2 k3 ...</font></b>
<b>获得多个v</b>
<b><u style=""><font color="#c41230" style="">setnx k v</font></u></b>
<b><font color="#c41230">当库中有该K时不存。当库中没有改K时存放<br>非常重要(做分布式锁)</font></b>
<b><font color="#c41230">getset k v</font></b>
<b><font color="#000000">获取值之后 修改该K的V</font></b>
<b><font color="#c41230">insc k1</font></b>
<b>该k1对应的v的值++(v必须是Integer类型)</b>
<b><font color="#c41230">desc k1</font></b>
<b><font color="#000000">该k1对应的v的值--(v必须是Integer类型)</font></b>
<b><font color="#c41230">inscby k1 步长</font></b>
<b>设置每次走的步长</b>
<b><font color="#c41230">descby k1 步长</font></b>
<b>设置每次走的步长</b>
<b>list</b>
list是一个双向链表
应用
实现高性能的分页
实现栈或队列:例如到货通知、邮件发送、秒杀、保存待抢购的商品列表
底层实现
压缩列表(ziplist)
当列表对象痛死满足两个条件时,<br>列表对象使用ziplist进行存储
列表对象保存的元素数量小于512个
列表对象保存的所有字符串元素长度都小于64个字节
他将所有的元素紧挨着存储,分配的是一块连续的内存
快速列表(quicklist)
由于普通链表指针比较浪费空间且会加重内存碎片化,所以优化为quicklist
特点
将多个ziplist使用双向指针串起来(链表+ziplist)
既满足了快速的插入删除性能,又不会出现太大的空间冗余
相关命令
<b><font color="#c41230">lpush k v</font></b><br>
<b>从左边放</b>
<b><font color="#c41230">rpush k v</font></b><br>
<b>从右边放</b>
<b><font color="#c41230">lpop k </font></b>
<b>从左边取第一个</b>
<b><font color="#c41230">rpop k</font></b><br>
<b>从右边取第一个</b>
<b><font color="#c41230">blpop k timeout</font></b>
<b>从左边取,没取到的话阻塞timeout时间</b>
<font color="#c41230"><b>brpop k timeout</b></font>
<b>从右边取,没取到的话阻塞timeout时间</b>
<b><font color="#c41230">lrange k 0 -1</font></b>
<b>查看队列</b>
<b>-1代表倒数第一个</b><br>
<b>-2 代表倒数第二个</b>
<b><font color="#c41230">实现分页</font></b>
<b>page size</b>
<b>(page-1)* size</b>
<b>page*size-1</b>
<b><font color="#c41230">llen k</font></b>
查看该队列的长度<br>
<b><font color="#c41230">lrem k count value</font></b>
<b>count = 0 ,移除队列里面所有与value 值相同的value</b><br>
<b>count > 0 , 从表头开始搜索,删除数据value的值,删除的个数为count个</b><br>
<b>count< 0 ,从表尾开始搜索,删除数据为value的值,删除的个数为 count的绝对值个</b><br>
<b>hash</b>
<b><font color="#c41230">map 适合存储对象?</font></b>
<b>因为对象的属性和值,我们可以认为是一个map集合里面的数据!</b>
<b>相比于json串,可单独修改对象的字段</b><br>
<b>可以快速定位,存储的信息需要被频繁的修改可用hash存储,比如<font color="#c41230">实现购物车</font></b>
相关命令
<b><font color="#c41230">hset k field value</font></b>
<b>存</b>
<b><font color="#c41230">hget k filed</font></b>
<b>取</b>
<b><font color="#c41230">hmset k field value field value</font></b>
<b>存多个</b>
<b><font color="#c41230">hmget k field filed</font></b><br>
<b>取多个</b>
<b><font color="#c41230">hgetall k</font></b>
<b>取得所有的k-v</b>
<b><font color="#c41230">hkeys k</font></b><br>
<b>只取key</b>
<b><font color="#c41230"> hvals k</font></b>
<b>只取value</b>
<b>set</b>
无序唯一<br>
命令
<b><font color="#c41230">sadd k v</font></b>
<b><font color="#c41230">在k的set 集合里面添加一个v,该v 不能重复</font></b><br>
<b><font color="#c41230">spop k</font></b><br>
<b>随机弹出一个</b>
<b><font color="#c41230">smembers k</font></b><br>
<b>K的set集合的所有数据</b><br>
<b><font color="#c41230">scard k</font></b>
<b>K的set集合的长度</b>
<b><font color="#c41230">sdiff k1 k2 (k1-k2)</font></b><br>
<b>减集</b><br>
<b><font color="#c41230">sinter k1 k2</font></b><br>
<b>交集</b>
<b><font color="#c41230">sunion k1 k2</font></b><br>
<b>并集</b>
<b>zset</b>
<b>写数据带分数,实现排行榜</b>
相关命令
<b><font color="#c41230">zadd k 分数 成员</font></b><br>
<b>添加 可批量添加</b>
<b><font color="#c41230">zrange k start end</font></b>
<b>排行 (从低到高)</b>
<b><font color="#c41230">zreveage k start end</font></b>
<b>排行 (从高到低)</b>
<b><font color="#c41230">zrangebyscore k 分数的最小值 分数的最大值</font></b>
<b>指定分数区间排行(从低到高)</b><br>
Redis事务
概念
Redis事务的本质是一组命令的集合。事务支持一次命令执行多个命令,一个事务中所有的命令都会被序列化
特点
一次性
顺序性
排他性
没有隔离级别的概念
批量操作在事务提交前放入魂村队列,并不会被实际运行
不保证原子性
Redis中的单条命令是原子性执行的,但事务不保证原子性,且没有回滚
事务中的任意命令执行失败,其余的命令仍会被执行
三个阶段
开始事务
命令入队
执行事务
相关命令
<b><font color="#c41230">watch k1 k2</font></b>
监视key,如果事务在被执行前,被监视的key被改动,则事务执行失败
实现乐观锁
<b><font color="#c41230">multi</font></b>
标记事务的开始
<b><font color="#c41230">exec</font></b>
执行事务
<b><font color="#c41230">discard</font></b>
放弃事务
<b><font color="#c41230">unwatch</font></b>
取消watch对所有key的监控
相关问题
如果在事务队列中出现<b><font color="#c41230">编译异常(语法错误</font></b>),则执行exec命令,所有的命令都不会执行
如果在事务队列中出现<b><font color="#c41230">运行时异常</font></b>,则执行exec命令,错误命令抛出异常,其他命令正常执行
Redis设置maxmemory-policy的方式<br>超过最大的容量后会怎么做<br>
<b>volatile-lru</b><br>
<b>从设置了过期时间的key集合中删除最近没有使用的key</b><br>
<b><font color="#c41230">allkeys-lru</font></b><br>
<b>通过lru算法删除最近没有使用的key</b>
<b><font color="#381e11">volatile-lfu</font></b>
<b>从设置了过期时间的key集合中删除最近使用频率最少的key</b>
<b>allkeys-lfu</b><br>
<b>在所有key的集合中 删除最近使用频率最少的key</b>
<b>volatile-random</b><br>
<b>在设置了过期时间的key的集合中随机删除一个key</b>
<b>allkeys-random</b>
<b>在所有key的集合中随机删除一个key</b>
<b>volatile-ttl</b><br>
<b>在设置了过期时间的key集合中删除即将过期的key</b><br>
<b>noeviction<font color="#c41230">(Redis默认)</font></b><br>
<b>当超过最大容量,不会删除任何key,返回一个错误</b>
LRU(least recently use)算法
最近没有使用算法
<b><font color="#c41230">不需要额外的存储空间,使用一个双向连表就能实现</font></b><br>
<b>如果一个数据在最近一段时间<font color="#c41230">没有被访问到</font>,那么可以认为在将来它被访问的可能性也很小。<br>因此,当空间满时,最久没有访问的数据最先被置换(淘汰)</b>
LFU(least recently use)算法
最近使用频率最少的key
<b><font color="#c41230">需要一个能记录key 使用次数的空间,使用ZSet 这样的结构就能实现</font></b><br>
<b>如果一个数据在最近一段时间<font color="#c41230">很少被访问到</font>,那么可以认为在将来它被访问的可能<br>性也很小。因此,当空间满时,最小频率访问的数据最先被淘汰</b><br>
<b>过期删除策略</b>
一个key 设置过期时间后<b><font color="#c41230">(expire k seconds)</font></b>,能自动的删除<br>一个操作将在未来的某个确定时间发生。<br>
应用场景
订单的过期取消 (下了一个订单,超过30min没有支付,则自动取消)<br>
key的过期删除(给key设置了一个过期时间,到达这个过期时间后,key能自动的删除)<br>
三种策略
<b>定时删除</b><br>
<b>定时删除的原理:<font color="#c41230">给每一个设置了过期时间的key分配一个线程,<br>该线程睡眠了过期时间后,起来删除该key</font></b>
<b>缺点:当过期的key特别多时,创建的线程也会非常多,特别消耗cpu资源</b><br>
<b>定期删除</b>
<b>定期删除原理:<font color="#c41230">专门有一个线程监视所有设置了过期时间的key的时间,<br>如果过期,将该key删除</font></b>
<b>缺点:实时性差一点</b><br>
<b>惰性删除</b>
<b>惰性删除原理:<font color="#c41230">当用户访问该key 时,会判断该key 是否过期了<br>如果过期了就删除该key,给用户返回null,如果没有过期就返回value</font></b>
<b>缺点:如果用户一直不访问该key,它就一直不会删除,<br>会一直占用内存</b><br>
<b><font color="#c41230">定期删除+惰性删除结合(Redis默认)</font></b>
可以互相解决缺点
<b>Redis的持久化</b>
RDB
原理
redis会单独创建(fork)一个与当前进程一模一样的子进程来进行持久化<br>将数据写入到一个临时文件中,待持久化结束后替换上次持久化好的文件
相当于两个rendis进程,这期间<br>主线程不参与持久化,保证redis的<br>高性能
触发
客户端执行shutdown命令
配置文件中有快照配置,例如:save 900 1 (15分钟内有一次修改)
执行save或bgsave命令
save命令会阻塞主线程,一般不用
bgsava会fork子进程异步持久化
执行flushall命令
特点
优点
恢复的时候比较快,适合大规模的数据恢复
缺点
如遇突然宕机,丢失的数据比较多
如果生成的快照文件比较大会影响redis的性能
AOF
原理
将所有的写命令追加到AOF缓冲区中,根据对应的写入策略向硬盘进行同步操作
由主线程完成
随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的
fork子进程来进行
触发
需手动开启:appendonly yes
线上开启 config set appendonly yes
开启后redis会保留一块内存供缓存使用,默认是1M
aof和rdb同时开启时,只保留save 900 1 减少fork子进程的次数(优化点)
写入策略:appendsync
everysec:每秒同步一次,效率高,可能会丢失1秒的数据【<b>默认也推荐使用</b>】
no:等到缓冲区满了才写入磁盘,次数少,效率高,不安全
追求效率
always:每次发生数据变更时立即同步到磁盘,效率低,安全
追求安全
重写机制<br>bgrewriteaof
默认配置
auto-rewrite-min-size 64M
aof文件大于64M时重写
<b>由于重写会fork子进程,为了减少重写次数,<br>这里建议配置5GB以上(优化点)</b>
auto-aof-rewirte-percentage 100<br>
指超过优化后,第二次优化文件大小大于第一次优化文件后大小一倍时开始重写
重写后的文件为什么会变小
进程内已超时的数据不再写入文件,而且多条写命令可以合并为一条
新的AOF文件只保留最终数据的写入命令(去掉了修改命令)
特点
优点
相比于RDB,丢失的数据少,不过建议与RDB同时开启
缺点
恢复慢,恢复不稳定,容易bug
<b>redis启动后持久化文件的加载流程</b>
先判断是否开启了AOF,如果存在AOF文件,则直接加载AOF文件
如果找不到AOF文件,则直接启动,不会加载RDB文件
如果没有开启AOF,则会加载RDB文件
生产环境建议AOF和RDB同时使用,RDB做灾难备份<br>
主从复制
概念
<b>主机数据更新后根据配置和策略,自动同步到备机的master/slave机制,<br>Master以写为主,Slave以读为从</b>
配从不配主
在从机上输入命令<b>SLAVEOF 【主机的ip】 【主机的端口号】</b>
主从复制的一些问题<br>
从机是从头开始复制还是从切入点开始复制
从头复制
从机是否可以写 set?
不能 从机只能读get
主机shutdown后,从机是上位还是原地待命<br>
原定待命,不会变成主机
主机重新启动后,从机是否能够顺利复制<br>
可以
从机shutdown后,情况如何<br>
从机挂了之后,会丢失之前的主从关系,需要重新设置一次主从关系才行<br>
在配置文件里面可以永久的设置主从关系(slaveof),挂了重启再启动,主从关系还会保持
记住命令<b><font color="#c41230">info replication</font></b>
查看本机的复制信息
主从复制数据同步的原理<br>
前提
Master和Slave都会维护一个offset和run id ,Slave每秒都会上报自己的offset给master
Master记录在backlog(针对增量复制)中,这样才能知道双方数据是否一致
Slave发送run id 和offset到Master,Master根据情况返回信息(增量/全量)
全量复制
触发时机
Slave从机第一次启动时
Master重启时
复制过程
1.Slave启动时回向Master发送SYNC指令(请求同步)
2.Master收到后通过bgsave保存快照,同时将后续的命令存到缓存中
3.Master将RDB发给Slave,Slave收到文件后先写入到本地磁盘,然后在从本地磁盘加载到内存中
4.最后maser会将内存中的写命令同步给Slave,Slave收到后再执行一遍
增量复制
Master根据Slave发送的同步请求中的offset
在backlog中查询部分丢失的数据,发送给Slave
过期key的处理
Slave不会处理过期key,只会等待Master的过期通知
哨兵模式
概念
<b>哨兵是一个分布式系统,监控主从架构中的节点通过 <span style="color: rgb(196, 18, 48);">自动故障转移 </span><font color="#381e11">保证集群的高可用</font></b>
主要功能
监控
监控主节点从节点是否正常运行
自动故障转移
当确认主节点宕机后,在从节点中选一个座位主节点,将<br>其他从节点连接到新的主节点上通知客户端最新的地址
工作原理
发现Master宕机
哨兵从服务器列表中挑选Master
先过滤掉不在线和响应慢的服务器
然后过滤掉与原Master断开时间最久的
最后比较优先级priority
如果两个服务器优先级一致,那么回去查看从服务器<br>中数据的offset,offset说明数据最新,选出offset<br>大的服务器为Master
新Master诞生
哨兵向选举出的新Master发送指令,断开与旧Master的连接
把新Master的ip地址同步到其他Slave节点
以前的Master如果重连了,那么以前的Master会变成新Master的Slave
主从复制的缺点
由于所有的写操作都是现在Master上操作,然后同步更新到Slave上<br>所以从Master同步到Slave上机器有一定的延迟,当系统很繁忙的时候,<br>延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重<br>
Redis集群
中心化
意思是所有的节点都要有一个主节点
缺点
中心挂了,服务就挂了
中心处理数据的能力有限,不能把节点性能发挥到最大
特点
就是一个路由作用
去中心化
让每个主机都拥有转发能力
创建集群时,集群节点直接要相互通信,<br>使用 redis-server的端口+10000 = 新的集群的监听端口<br>
哈希槽 Redis集群时怎么样set一个key
执行流程
redis的使用
idea工程
评论
0 条评论
下一页