redis基础
简述redis
Redis是一个Key-Value类型的NoSQL内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。
因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
redis的另一个特色是支持保存多种数据结构,并且redis单个value的最大限制是1GB,因此redis可以实现很多有用的功能,比如用list来做FIFO的双向链表或者实现一个轻量级的高性能的消息队列服务。
redis的主要缺点是数据写在硬盘当中,因此数据容量受物理内存限制,不适用海量数据的高性能读写,所以redis适合场景是较小数据量的高性能操作和运算上。
Redis为什么这么快?
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
Redis的常用场景
1、排行榜/计数器
可以用string做自增自减(incr/incrby)运算,实现计数器功能,常用的如短信发送次数、接口防刷等;
排行榜使用redis的sort set 是有序不重复的。
2、会话缓存
可以用redis来存储多台服务器的会话信息,这样一个用户可以请求多台应用服务,更容易实现高可用型;
3、分布式锁
通过redis自带的SETNX命令实现分布式锁;
4、队列
因为redis支持List和set,可以实现消息队列;
5、其他
可以通过set实现交集、并集等操作,实现共同粉丝或好友。
Redis 的数据类型?
Redis 支持五种数据类型: string( 字符串),hash( 哈希), list( 列表), set( 集合) 及 zsetsorted set: 有序集合)。
- string:redis 中字符串 value 最大可为512M。可以用来做一些计数功能的缓存(也是实际工作中最常见的)。 常规计数:微博数,粉丝数等。
- list:简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。可以实现一个简单消息队列功能,做基于redis的分页功能等。(另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。)
- set:是一个字符串类型的无序集合。可以用来进行全局去重等。(比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程)
- sorted set:是一个字符串类型的有序集合,给每一个元素一个固定的分数score来保持顺序。可以用来做排行榜应用或者进行范围查找等。
- hash:键值对集合,是一个字符串类型的 Key和 Value 的映射表,也就是说其存储的Value是一个键值对(Key- Value)hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。
我们实际项目中比较常用的是 string,hash。 如果你是 Redis 的高级用户,还需要加上下面几种数据结构 HyperLogLog、Geo、Pub/Sub。
如果你说还玩过 Redis Module,像 BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。
redis 过期键的删除策略
-
定时删除:为每个设置过期的key创建个定时器,到过期时间就会立即清除。该策略可以立即请求过期数据,对内存很好,但会占用大量的cpu资源取处理定时器,影响缓存的响应时间和吞吐量。
-
惰性删除:只有在访问一个key时,才会判断该key是否过期,过期则删除。该策略可以节省CPU资源,但是对内存很不友好,可能会出现大量过期的key,未被删除,占用大量内存。
-
定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。多长时间检查、一次删除多少过期键,是可以改变的。该策略会扫描expires字典(expires字典会保存所有设置了过期时间的key)。
redis 内存淘汰机制
redis 提供 6种数据淘汰策略:
-
noeviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
-
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的,LRU算法)。
-
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,任意选择数据淘汰。
-
volatile-lru:当内存不足以容纳新写入数据时,利用LRU算法在设置过期时间的key中,移除最少使用的key (LRU:最近使用 Least Recently Used )。
-
volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
-
volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
redis 持久化机制
Redis提供了两种不同的持久化方法可以将数据存储在磁盘中,一种叫快照RDB
,另一种叫只追加文件AOF
。
RDB(默认):在指定的时间间隔内将内存中的数据集快照写入磁盘(Snapshot),它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork
)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB
方式要比AOF
方式更加的高效。
RDB
的缺点是最后一次持久化后的数据可能丢失。
Fork
的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
Rdb
保存的是dump.rdb
文件。
优势
适合大规模的数据恢复
对数据完整性和一致性要求不高
劣势
在一定间隔时间做一次备份,所以如果redis
意外down
掉的话,就会丢失最后一次快照后的所有修改。
AOF:是以日志的形式来记录每个写操作,将Redis
执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis
启动之初会读取该文件重新构建数据,换言之,redis
重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF
保存的是appendonly.aof
文件。
AOF重写
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时, Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。
原理:AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据, 每条记录有一条的set语句。重写aof文件的操作,并没有读取旧的aof文件, 而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
优势
- 每修改同步:appendfsync always
同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
- 每秒同步:appendfsync everysec
异步操作,每秒记录,如果一秒内宕机,有数据丢失
- 不同步:appendfsync no
从不同步
劣势
- 相同数据集的数据而言aof
文件要远大于rdb
文件,恢复速度慢于rdb
- aof
运行效率要慢于rdb
,每秒同步策略效率较好,不同步效率和rdb
相同
Redis 4.0 对于持久化机制的优化
redis4.0相对与3.X版本其中一个比较大的变化是4.0添加了新的混合持久化方式。
混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,
写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。
简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。
优点
混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点
兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差。
一个字符串类型的值能存储最大容量?
512M