Key的过期策略
- 定时删除 timer 让定时器在键的过期时间来临时,立即执行对键的删除操作
- 惰性删除 在取出键时才对键进行过期检查,如果发现过期了就会被删除
- 主动定期删除 是定时删除策略和惰性删除策略的一个折中
Redis为什么快
- 内存存储
- 单线程实现(Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销)
- 非阻塞IO
- 简单的数据结构(Redis有诸多可以直接应用的优化数据结构的实现,应用层可以直接使用原生的数据结构提升性能)
- 文件事件处理器
Redis 核心数据结构都有哪些?String类型都有哪些特点?
- Redis主要支持五种数据类型:
-
string (Binary-safe二进制安全)可以包含任何数据(jpg图片或者序列化的对象)(SET stringkey value)
-
hash(字段和值都是字符串)(hmset hashkey filedkey1 filedvalue1 filedkey2 filedvalue2 filedkey3 filedvalue3 。。。)
-
list(linked lists根据插入顺序排序的字符串元素的集合 value可重复)(LPUSH listkey value)
-
set(collections of unique未排序的字符串元素的集合)(SADD setkey value)
-
sorted set(collections of unique zset:有序集合 每个字符串元素都与一个称为score的浮点值相关联可检索一系列元素 前10名或后10名)(ZADD zsetkey score value)(查询在线用户(按过期时间排序))
- zset底层存储结构
- zset 由两个结构ziplist skiplist组成,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist
- 有序集合保存的元素数量小于128个
- 有序集合保存的所有元素的长度小于64字节
- 当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值
- 当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict(key/value,key为元素,value为分值)来保存元素和分值的映射关系
- zset 由两个结构ziplist skiplist组成,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist
- zset底层存储结构
-
Bit arrays (or simply bitmaps)可以使用特殊命令像位数组一样处理字符串值:您可以设置和清除单个位,计数所有设置为1的位,找到第一个设置或未设置的位(仅使用512 MB内存就可以记住40亿用户的一位信息)
-
Redis值类型中 hash 和 string 又什么区别呢?
- 常用于存储一个对象(提供了直接存储map数据的接口)
- 用string存缺点
- value值需要开销(序列化/反序列化)
- 修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题
Redis 事务
- Redis命令在事务执行时可能会失败,但仍会继续执行剩余命令而不是Rollback
- 命令进入了一个队列 只有EXEC被调用时,排队的命令才会被执行 调用DISCARD可以清除事务中的commands队列并退出事务
- 事务执行过程中如果不出现语法上的错误(入队列并没有执行只能检测出语法错误),事务中的一个操作执行失败而另一个操作是可以执行成功的
- 基本命令使用
- multi 开启事务(不能嵌套使用 会报错)
- 命令入列(返回 QUEUED字符串)
- exec 提交事务 discard 放弃事务(清空队列)
- watch(乐观锁 CAS 在事务的过程中,某个监控项被修改了,那么整个事务就会终止执行)
Jedis jedis = new Jedis("localhost");
Transaction transaction = jedis.multi();
transaction.lpush("key", "11");
transaction.lpush("key", "22");
transaction.lpush("key", "33");
List<Object> list = transaction.exec();
Redis 持久化是如何工作的?Redis在持久化过程中和可以对外服务么?如果可以的话,Redis又是如何处理新命令的?
可以对外服务 父进程处理新命令
SAVE 直接调用rdbSave,阻塞Redis主进程看,直到保存完成为止.在主进程阻塞期间,服务器不能处理客户端的任何请求
不管是使用哪种持久化,RDB持久化或AOF重写,主进程都会fork出一个子进程,在子进程里完成rdb文件的生成或aof的重写。fork操作对于操作系统来说属于比较重的操作。fork阶段,redis会阻塞一段时间(毫秒级)
-
RDB
- redis根据配置检查是否需要生成rdb快照文件
- 如果需要快照生成,就fork一个子进程出来
- 子进程将数据dump到临时的rdb快照文件中,也就是dump.rdb
- 完成rdb快照文件的生成之后,会替换掉之前的老快照
- conf文件配置(save设置多个,就会有多个快照检查点,每到一个检查点,就会check一下 生成文件)
-
# save "" save 900 1 //900s内有一个更改 save 300 10 save 60 10000
-
-
AOF
-
AOF的持久化默认是关闭的 RDB的持久化默认是打开的(conf文件中配置打开)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hH4lVPnH-1600689816295)(C:\Users\90766\Desktop\1599551566(1)].jpg) -
redis每接收到一条写命令,就会以追加的形式写入日志文件中,当然是先写入os cache,然后每隔一定时间再fsync一下(conf文件中配置AOF的fsync策略 建议(默认)策略是fsync每秒执行一次)
mysql #每次写入一条数据执行一次fsync,非常非常慢,非常安全 # appendfsync always #每隔一秒执行一次fsync,这个是最常用(在2.4中可能与快照速度一样快)如果发生灾难,您可能会丢失1秒的数据 appendfsync everysec #从不fsync,只需将数据交给操作系统即可。更快,更不安全的方法。通常,Linux使用此配置每30秒刷新一次数据,但这取决于内核的精确调整 # appendfsync no
-
rewrite策略 AOF会自动在后台每隔一定时间做rewrite操作(重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合(如日志里已经存放了针对100w数据的写日志;redis删掉了90w数据,还剩10w数据;这样redis会基于内存中当前的10w数据构建一套最新的日志,覆盖之前的老日志,确保AOF日志不会过大,保证和redis内存数据量一致))
mysql #当前的aof文件超过上一次重写后aof文件的1倍触发rewite auto-aof-rewrite-percentage 100 #当前的aof文件最小size auto-aof-rewrite-min-size 64mb
``` * rewrite过程 * 当redis检测到需要rewrite的时候,redis fork一个子进程 * 子进程开始往一个新的临时的AOF文件中写入日志 * 主进程在内存中写入日志,同时新的日志也继续写入旧的AOF文件中 * 子进程写完之后,主进程将内存中的新日志再次追加到新的AOF文件中 * 用新的日志文件替换掉旧的日志文件(这个过程中如果发生宕机等意外会导致AOF文件损坏 用redis-check-aof工具对比文件可以轻松修复它) ```
-
-
同时有RDB文件和AOF日志,redis重启的时,会优先使用AOF进行数据恢复,因其日志更完整
-
Redis> = 2.4可以确保避免在RDB快照操作正在进行时触发AOF重写,或者避免在AOF重写正在进行时允许BGSAVE
-
AOF和RDB怎么选择
- 注重数据安全性 应同时使用两种持久性方法
- 在发生灾难的情况下仍然可以承受几分钟的数据丢失,则只需单独使用RDB
- 单独使用AOF,不建议 因为不时地使用RDB快照对于进行数据库备份、加快重启速度以及在AOF引擎出现错误时都是一个好主意
Redis 持久化方式Rdb和Aof的优缺点
- Rdb优势
- RDB最大限度地提高了Redis的性能(Redis fork 子进程出去rdb快照文件生成)
- 与AOF相比,RDB恢复快
- 占用磁盘空间较小
- Rdb缺点
- 丢数据(没到执行RDB快照时间 Redis在没有正确关闭情况下停止工作 shutdown正常关闭时会触发(bgsave) 丢失这个时间范围内的数据)
- 可能导致Redis停止为客户端服务几毫秒(fork子进程 数据集很大CPU性能不佳fork很耗时可能导致Redis停止为客户端服务几毫秒甚至一秒钟 AOF还需要fork 但可以调整要重写日志的频率,而无需权衡持久性)
*AOF优势(参考RDB优缺点) - 可以使用不同的fsync策略 (使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作。)但是您只能损失一秒钟的写入时间)
- AOF日志是仅追加的日志,因此,如果断电,则不会出现定位失败或损坏问题。即使由于某种原因(磁盘已满或其他原因)以half-written命令结束日志,redis-check-aof工具也可以轻松修复它
- 在重写触发事件范围内发生错误操作(FLUSHALL或FLUSHDB命令)仍然可以保存数据集,只需停止服务器,删除最新命令并再次重新启动Redis(调大AOF重写参数auto-aof-rewrite-percentage和auto-aof-rewrite-minsize, 让Redis不能产生AOF自动重写)
- AOF缺点
- 占用磁盘空间较大(对于同一数据集,AOF文件通常大于等效的RDB文件)
- 恢复慢(根据最优的fsync策略,AOF可能比RDB慢)
- 有未知没有重现的错误(过去,我们在特定命令中遇到过罕见的错误)
Redis 内存不够时,如何处理新的请求?
取决于内存淘汰策略
Redis 是如何处理过期数据(淘汰策略)?
- noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)
-
allkeys-lru:从所有key中使用LRU算法进行淘汰
-
volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰
-
allkeys-random:从所有key中随机淘汰数据
-
volatile-random:从设置了过期时间的key中随机淘汰
-
volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key(4.0之后)
-
allkeys-lfu:在所有的key中使用LFU算法淘汰数据(4.0之后)
-
volatile-ttl:在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰
当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误
Redis 内存淘汰算法 LRU/ LFU 可以讲一下原理么?
- LRU(Least Recently Used 最近最少使用),是一种缓存置换算法。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据。
核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉
弊端(LFU解决) 假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。 - LFU(Least Frequently Used) 核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来
Redis 位图应用场景及实现原理可以讲一下么?
-
原理
*位图不是实际的数据类型,而是在String类型上定义的一组面向位的操作 由于字符串是二进制安全Blob,并且最大长度为512 MB,因此它们适合设置多达2 32个不同的位 时间复杂度都是O(1) -
位操作分为两类
- 固定时间的单个位操作(如将一个位设置为1或0或获取其值)
- 计算给定位范围内设置的位的数量(例如,人口计数)
-
应用场景
- 位图计数
- 1.我们需要记录用户一年的签到记录
- 2.用户在线状态
- 3.统计几天用户的活跃度
- 位图计数
-
使用
- setbit key offset value(命令的第一个参数是位数,第二个参数是要设置位的值,value即1或0。如果寻址的位超出当前字符串长度,该命令将自动放大字符串 偏移量较大时可能有较大耗时)
- getbit key 10(GETBIT只是返回指定索引处的位的值。超出范围的位(寻址超出存储在目标键中的字符串长度的位)始终被视为零)
- 统计命令 bitcount bittop
-
理解
- 下面记录了 22这个userid在 20160502 访问了pagehelloword
- setbit pagehelloword::20160502 22 1
- setbit pagehelloword::20160502 23 1
- setbit pagehelloword::20160502 24 1
- setbit pagehelloword::20160502 25 1
- setbit pagehelloword::20160502 26 1
- bitcount pagehelloword::20160502 返回5
Redis 的缓存穿透/缓存雪崩/缓存重建?
-
缓存穿透 查数据在Redis中没查到就会向持久层数据库去查数据,很多这种操作时给持久层数据库非常大的压力,这情况就叫缓存穿透(是指在缓存和数据库中都查询不到)**缓存穿透解决方案 **
-
1、使用布隆过滤器进行过滤
将所有存在的数据哈希到位数组(bit array)中,如果访问的不存在,则会被拦截掉,从而保证不会对持久层进行查询。
优点
不需要存储元素本身(数据保密)
Hash函数相互之间没有关系,方便由硬件并行实现(多个hash函数来降低冲突)
布隆过滤器存储空间和插入/查询时间复杂度都是常数阶
使用位数组,占用数据空间小
缺点
有误算
不支持删除操作
应用场景
网页URL 去重
垃圾邮件识别
黑名单
集合元素重复的判断
缓存穿透 -
2、如果查询的数据不存在,则也对这个数据进行缓存(缓存为空)。
-
-
缓存雪崩 指缓存层本身出现了问题,比如:缓存层宕机,或者缓存数据在同一时间过期。那么所有的查询请求都会指向数据库。这种情况我们称之为缓存雪崩。
**缓存雪崩解决方案 **
-
1、搭建Redis集群,保证Redis提供正常的服务。
-
2、给数据设置不同的过期时间。
-
3、限流访问
-
-
缓存击穿 是指一个热点key,大并发集中对这个key进行访问,当这个key在失效的瞬间,仍然有大批量的访问这个key,但是由于这个key已经失效,所有的请求就都会直接向持久层数据库进行访问
**缓存击穿解决方案 **- 1、热点数据设置为永不过期
- 2、根据业务去维护热点数据的过期时间
Redis 单线程和Redis6.0 的多线程是如何工作的?
-
单线程高速执行
- 多命令进入队列 逐个执行( (socket 读)、解析、执行、内容返回这个过程为单线程 其他淘汰算法 无用连接的释放 大key的删除等是多线程)
- 避免了线程切换开销
- 非阻塞IO(Redis采用epoll I/O多路复用技术 相当于nio中的selector)
- 基于内存速度快
- CPU不是Redis的瓶颈 瓶颈是机器内存或网络带宽(但是不能对cpu多核更好的利用)
-
reids6(改动较大 主要列举)
- ACL(用户权限管理 分配可以操作的指令和key)
- SSL
- IO多线程支持
Redis日志
redis在默认情况下,是不会生成日志文件的,所以需要配置(conf文件中配置logfile“”里面填充路径即可)