- redis是什么 redis是c语言开发的一个开源的高性能键值对的内存数据库,一个内存中的数据结构存储系统,他可以用做数据库,缓存,消息中间件.支持多种类型的数据结构,如字符串,列表,哈希,集合,有序集合,bitmaps,Hyperloglogs,geo(地理空间) 字符串,是redis的基本类型,一个key对应一个value,最大可以存储512M,常用的命令有set,get,mget等,应用场景微博数,粉丝数 列表,是一个字符串列表,按照插入的顺序排序,是一个双向链表,支持反向查找和遍历,常用命令lpush,rpush,lpop,rpop,lrange等命令,使用场景关注列表,好友列表等 哈希,和我们java中的hashmap类似,hash特别适合存储对象常用命令,hget,hset,hgetall等,应用场景可以存储用户的信息商品的信息等, set,是一种无序集合,支持去重操作,但没有顺序,常用命令,sadd,spop,smemebers,sunion操作,方便集合之间的操作,引用场景,判断某个元素是否存在set集合中 zset,和set类似,且有序,使用一个额外的参数score为成员排序,内部使用hashmap和跳跃表实现存储和有序,HaspMap存放成员到score的映射,而跳跃表存放所有的成员,使用跳表实现比较高的查询效率,常用命令zadd,zrange,zrem,zcard等待,使用场景用分数进行成员的从小到大的排序 bitmaps(位图),实际上就是字符串,但是他可以对字符串的位进行操作,可以把位图当做一个以位为单位的数组,数组中只有0或1,如[00111010010010...],数组的下表就是偏移量,最大长度是512M,即2^32个比特币,常用命令setbit ,getbit,bitcount,使用场景,如用户的打卡,网站的浏览次数等等,(如果我们使用String记录用户的打卡,当用户量非常大的时候,就会占用内存,但是使用位图,一年365天相当于365bit,1字节=8bit,也就46字节而已,比如setbit user1 0 1表示user1第一天打卡成功,setbit user1 1 0表示user1第二天打卡失败) Hyperloglogs,提供一种不太准确的基数统计算法(统计一批数据中的不重复的个数),常用命令pfadd,pfcount,pfmerge等等,使用场景网页的UV(用户访问网站的次数) geospatial(地理空间),把指定的地理空间位置添加到指定的key中,如geoadd city 116.40 39.9 北京(geoadd key longitude latitude member),常用命令geoadd,geopos,geodist,georadius使用场景,计算两点之间的距离,查看指定位置附近范围的人.
- redis雪崩,缓存穿透,击穿是什么意思,如何解决 redis雪崩,我们认知的缓存都是存储热点数据,我们查询数据,先去缓存查找,如果缓存不存在,就直接到数据库查找,但是有一种情况我们要注意,比如,我们把缓存的key的有效时间是晚上0点,然而再0点正好有一次大促销活动,假设0点的请求量为8000请求/秒,同时所有的key失效了,因此8000个请求直接落到了数据库,此时数据库还没有反映过来,就直接过了,这就是雪崩。 如果遇到这种情况,我们可以给redis存储的数据设置一个随机事件,而不是设置同一时刻,这样就可以保证不会大面积失效,当然如果redis是集群,可以把热点数据分布在不同的redis库中也能避免全部失效,最暴力的就是不设置过期时间,更新操作在修改缓存就可以了 缓存穿透,其实就是缓存和数据库都没有数据,此时有大量的请求,获取不存在的数据,如我们数据的id都是大于1的值,但是黑客使用id=-1的请求不断的攻击,导致数据库压力过大,严重的时候会导致数据挂掉 如果遇到这种情况,我可以给请求入参添加校验,只有合法的参数才能请求服务,也可以利用布隆过滤器,当不存在的时候就必要查询请求了。 缓存击穿,是指一个大热点key,百万级用户不断请求,但是在某一刻失效了,也会导致大量请求到数据库,导致数据库挂掉, 如果遇到这种情况,我也可以设置缓存不过期,或者使用分布式锁。
- redis是单线程吗,为什么这么快
resi是单线程,但是官方声称redis的QPS可达100000 ,为什么是单线程还这么快,如下原因
- redis基于内存,所有数据都在内存中,类似hashMap,时间复杂度为O(1)
- 数据结构简单,对数据操作也简单
- 使用单线程,避免了不必要的上下文切换和竞争条件不存在多线程的CPU切换,也不存在加锁释放锁操作。
- 使用多路复用IO模型,非阻塞IO.
- redis和memcached有什么区别
- redis可以持久化,而memcached数据全部在内存中
- redis支持多种数据结构,memcached支持key-value
- 使用的协议不一样,memcached的客户端通过TCP连接服务器通信,redis使用RESP协议通信,是专门为Redis设计的通讯协议,redis直接构建自己的VM机制,因为一般的系统调用系统函数,会浪费一定的时间去移动和请求.
- value的大小,redis可以达到1GB,而memcached只有1MB
- Redis的淘汰策略有哪些 策略 描述 volatile-lru 从已设置过期时间的KV集中,优先对最近最少使用的淘汰 volatile-ttl 从已设置过期时间的KV集中,优先对剩余时间短的数据进行淘汰 volatile-random 从已设置过期时间的KV集中,随机选择数据淘汰 volatile-lfu 从已设置过期时间KV集中,优先选择最不经常的数据进行淘汰 allkeys-lru 从所有KV集中,优先对最近最少的数据进行淘汰 allkeys-random 从所有KV集中,随机选择数据淘汰 allkeys-lfu 从所有kV集中,最不长用的数据进行淘汰 noeviction 不淘汰,直到超过最大内存,返回错误信息
- redis的持久化有几种
redis的持久化操作有两种,一种是RDB,RDB是以快照形式直接把内存的数据保存在一个dump文件中,而另外一种AOF,是把每一次修改命令存储在文件里,redis默认是RDB持久化方式,但是在重启的的时候,他会优先使用AOF,因为AOF保存的数据比RDB更加完成,
RDB触发的方式
- 手动触发,使用save,bgsave命令
- save会阻塞redis服务器进行直到RDB文件创建完毕之后,否则服务器在这期间一直阻塞,而bgsave是会fork一个子进程,有子进程负责创建RDB文件,而父进程会继续处理请求。
- 自动触发,通过配置文件的命令save m n,满足下面任何一个条件如
- save 900 1,到900秒内的时候,如果redis至少发生一次变化
- save 300 10,到300秒内,至少发生10变化
- save 69 10000,到10000秒内,知道发生10000变化
- 其他原因如主从复制场景,从节点执行全量复制操作,或执行shutdown命令时,都会执行RDB持久化
- 手动触发,使用save,bgsave命令
- redis中bgsave执行流程
- AOF运行原理
开启AOF,需要在配置文件中配置appendonly yes,而AOF执行流程包括下面三种
- 命令追加,将redis的写命令追加到缓冲区aof_buf
- 文件写入和文件同步,根据不同的同步策略将aof_buf的内容同步到磁盘
- 文件重写,定期重写AOF文件,达到压缩的目的
命令追加
redis是将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次写命令直接写入磁盘,导致硬盘IO成为redis负载的瓶颈
文件写入和文件同步
为了提高文件写入的效率,在现在操作系统中,当用户调用write函数将数据写入文件的时候,操作系统通常将数据暂存到一个内存缓冲区里,当缓冲区慢了或者超过了指定大小后,才会将内存缓存区中的数据写入到内存,这样虽然提高了效率,但是存在安全问题,比如计算机停机,内存缓存区的数据就会丢失,因此操作系统提供了文件同步的同步函数fsysnc,fdatasync函数,可以强制把操作系统立刻将缓存区中的数据写入到硬盘里,从而保证数据的安全性,文件同步策略的策略由参数appendfsync控制
代码语言:javascript复制appendfsync always,
//命令写入aof_buf后立刻调用系统fsync操作同步到AOF
appendfsync no
//命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,
//此时的同步有操作系统负责,通常同步周期30s
appendfsync everysec
//命令写入aof_buf后调用系统write操作,write完成后线程返回,fsync同步文件操作由专门
//的线程每秒同步一次
文件重写
文件重写是指定期重写AOF文件,减少AOF文件的体积,重写就是把redis中的数据转成写命令,同步到AOF文件,不会对旧的AOF文件进行任何读取写入操作。具体的操作流程
- RDB和AOF的优缺点 命令 RDB AOF 启动优先级 低 高 体积小大 恢复速度 快慢 数据安全性 丢数据 根据策略决定 轻重 重 轻
- 如何选择RDB和AOF持久化策略
两种持久化策略持久化都是有性能代价的,RDB持久化,在使用bgsave的时候会阻塞主进程,子进程向硬盘写数据会带来Io压力,AOF持久化,向硬盘写数据的频率越大,IO压力更大,甚至导致AOF阻塞问题,
而在实际场景,根据数据量,应用数据安全的要求,预算限制等不同情况,会有各种各样的持久化策略,我们可以单独用一种策略,也可以两种混合使用.
如果数据完全对丢失数据对业务没有任何影响,就可以不进行任何持久化如果在单机情况,如我们自己练习,如何可以接受丢失数据,可以使用RDB,如果只能接受秒级别的数据丢失,可以选择AOF
如果是我们的生产环境,主从环境,一般我们可以master关闭持久化,可以在slave关闭RDB,开启AOF,然后关闭自动重写,添加定时任务,定时调用bgrewriteaof
上面只是简单的举例,所以如果你可以接受几分钟你的数据丢失,那么可以使用RDB,否则可以使用AOF持久化,但是AOF每一条命令追加磁盘,会降低redis性能,对于数据备份和灾难恢复,定时生成RDB快照非常便于进行数据库备份,但是RDB的回复数据集的速度比AOF恢复速度快,如果混合持久化,AOF重写的时候就直接把RDB的内容写到AOF文件开头,这样做的好处是可以结合RDB和AOF的优点,快速加载同时避免丢失过多的数据,当然AOF里面的RDB部分是压缩格式不再是AOF格式,可读性差.
- redis主从复制过程
主从模式结合哨兵模式能够解决掉单点模式,提高redis可用性,从节点仅提供读服务,而主节点提供些操作,对于读多写少的情况,可以从节点的数量,从而提高响应效率,而主节点和从节点直接的复制过程如下
- 从节点执行slave masteip masterPort.保存主节点信息
- 从节点的定时任务发现主节点信息,建立和主节点的socker链接
- 从节点发送ping信号,主节点返回pong,两边就能互相通讯
- 连接建立以后,主节点将所有数据发送给从节点进行数据同步
- 主节点把当前的数据同步给从节点后,便完成了复制的建立过程,接下来,主节点就会持续把写命令发送给从节点,保证主从数据一致性。
- 主从复制的原理
redis2.8之前使用的sync runid offset同步命令,redis2.8之后使用的psync runid offset命令,连个都是同步的命令,但是sync仅支持全量同步,而psync支持全量同步和增量同步,
runid:每个redis节点都有一个唯一的id,且每次重启都是重新生成
offset:主节点和从节点各自维护自己的主从复制的偏移量offset,当主节点写入命令时候,会记录offset=offset 命令字节长度,从节点在收到主节点发送的命令后,也会记录自己的offset,并把自己的offset同步给主节点,这样主节点保存自己的和从节点的offset,通过对比就可以知道数据是否一致
repl_backlog_size:保存在主节点的一个固定长度的先进先出的队列,默认大小是1MB
- 主节点发送数据给从节点过程中,主节点还会进行一些写操作,这个时候数据就会写入复制缓存区中,从节点完成主节点数据同步后,主节点将缓存继续发送给从节点,用于部分复制
- 主节点不但会把写命令发送给从节点,而且会写入复制缓冲区,用户复制命令丢失的数据补救
全量复制的过程和部分复制过程如下图
全量复制的主要步
- 从节点发送psync ? -1命令,第一次发送,不知道主节点的runid因此使用?,且默认offset=-1
- 主节点发现是第一次复制,返回FULLRESYNC runid offset,runid是主节点的runid,offset是主节点的offset
- 从节点接收到主节点的信息,保存到info中
- 主节点发送FULLRESYNC后,启动bgsave命令,生成RDB文件进行持久化
- 主节点发送RDB文件给从节点,到从节点加载数据完成期间,主节点会把写命令放到缓冲区中
- 从节点清理自己的数据库数据
- 从节点加载RDB文件,将数据保存在自己的数据库中
- 如果从节点开启了AOF,从节点异步重写AOF文件
增量复制过程如下
- 主从复制会有什么问题
- 一旦出节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还要使用命令使所有的从节点去复制新的主节点,整个过程需要人工干预
- 主节点的写能力受到单机的限制
- 原生复制在复制中断后,从界定会发起psync,此时如果同步不成功,则进行全量复制,期间会导致短暂的停顿
- 如何解决主从复制的问题 当然我可以使用哨兵模式,下图就是就是哨兵模式
如图,是哨兵的架构图,哨兵的主要功能就是主从节点存活检测,主从运行情况检测,自动故障转移,主从切换,可以执行以下四个任务
- 监控,不断检查主服务器和从服务器是否正常运行
- 通知,当redis服务器出现问题,哨兵通过api通知管理员发出通知
- 自动故障转移,当主节点不能正常工作的时候,sentinal就会开启一次自动故障转移操作,会把其中一个从节点晋升为新的主机节,且会把其他从节点重新指向新的主节点,不需要任何人为干预
- 配置提供者,在哨兵模式下,客户端在初始化的时候链接的是sentine节点,从中获取主节点的信息
- 哨兵原理
上图就是一张哨兵的架构图,我接着说他是如何工作的
- 每个sentinel节点都需要定期执行向所有主从服务器以及其他sentinel实例发送ping命令
- 但是当主节点最后一次回复ping命令超过了设置的时间(down-after-milliseconds参数指定的值),那么就会认为这个节点主观下线
- 如果一个主服务器被标记为主观下线,此时全部的sentinel节点都会每秒一次的频率确认主服务器的确是否进入主观下线状态
- 此时大多数sentinel确认主服务器为下线,那个这个主服务器标记为客观下线
- 正常情况下,每个sentinel会以每10秒一次的频率向已知的所有主从服务器发送info命令,但当一个主服务被标记为客观下线时,sentinel就会把info命令的频率改为每秒一次
- sentinel和其他sentinel协商客观下线的主节点状态,如果处于sdown状态则重新投票选出新的主节点,将剩余的节点指向新的节点进行数据复制
- 但是如果没有足够sentinel认为主服务下线时候,主服务器的客观下线状态就会移除,同时当主服务器的ping命令返回有效的恢复时候,就会移除主观下线。