1. 什么是Redis
Redis本质上是一个Key-Value的内存数据库,定期通过异步操作把数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。 Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
2. Redis支持的数据类型
String(字符串):
格式: set key value
String是二进制安全的,意思是Redis的String可以包含任何数据。
String是Redis最基本的数据类型,一个键最大能存储512MB。
Hash(哈希)
格式: hmset name key1 value1 key2 value2
Hash是一个键值(key-value)对集合。
Hash是一个String的field和value的映射表,Hash特别适合用于存储对象。
List(列表)
格式: lpush name value
格式: rpush name value
List是简单的字符串列表,按照插入顺序排序。可以添加元素到列表的头部或者尾部。
Set(集合)
格式: sadd name value
Set是String类型的无序集合。
Set是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
Zset(有序集合)
格式: zadd namescore value
zset每个元素都关联一个double的分数,通过分数来为集合中的成员进行排序。
zset的成员是唯一的,但分数(score)却可以重复。
3. Redis的淘汰策略
no-eviction:返回错误,当内存达到限制。
allkeys-lru: 尝试回收最少使用的键(LRU)。
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键,但仅限于在过期集合的键。
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键。
4. Redis的持久化机制
4.1 RDB
RDB持久化方式在指定的时间间隔对数据进行快照存储。在默认情况下, Redis 将数据库快照保存在名字为dump.rdb的二进制文件中。
工作方式:当 Redis 需要保存 dump.rdb 文件时
1. Redis 调用fork(),创建一个子进程。
2. 进程将数据集写入到一个临时 RDB 文件中。
3. 当子进程完成新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
4.2 AOF
每当 Redis 执行一个改变数据集的命令时, 这个命令就会被追加到 AOF 文件的末尾。当 Redis 重新启时,程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
AOF 持久化方案提供 3 种策略:
everysec(默认):表示每秒执行一次fsync 同步策略,效率上同 RDB持久化差不多。
always: 每个写命令都会调用fsync 进行数据同步,最安全但影响性能。
no: 表示 Redis 从不执行fsync,数据将完全由内核控制写入磁盘。对于Linux 系统来说,每 30 秒写入一次。
AOF重写:
作用:减少磁盘占用量;加速数据恢复。
重写配置:
auto-aof-rewrite-min-size //触发AOF文件执行重写的最小尺寸
auto-aof-rewrite-percentage //触发AOF文件执行重写的增长率
4.3 RDB和AOF的对比
一般来说,如果想数据安全性,应该同时使用两种持久化功能。如果可以承受数分钟以内的数据丢失,可以只使用 RDB 持久化。有很多用户都只使用 AOF 持久化,但并不推荐这种方式,因为定时生成 RDB 快照非常便于进行数据库备份,并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
5. Redis架构模式
5.1 单机模式
特点:简单
问题:
1. 内存容量有限。
2. 处理能力有限。
3. 无法保证高可用。
5.2 主从模式
Redis 的复制(replication)功能允许用户根据一个Redis 服务器来创建任意多个该服务器的复制品,被复制的服务器为主服务器(master),复制创建出来的服务器为从服务器(slave)。只要主从服务器之间的网络连接正常,主服务器会将数据更新同步给从服务器,从而一直保证主从服务器的数据相同。
特点:
1. master/slave 角色。
2. master/slave 数据相同。
3. 降低 master 读压力在转交从库
问题:
1. 无法保证高可用。
2. 没有解决 master 写的压力。
5.3 哨兵模式
Redis sentinel 是一个分布式系统中监控Redis主从服务器,并在主服务器下线时自动进行故障转移。
特点:
1. 保证高可用。
2. 监控各个节点。
3. 自动故障迁移。
问题:
1. 主从模式,切换需要时间丢数据。
2. 没有解决 master 写的压力
5.4 集群模式
从Redis 3.0之后版本支持集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
特点:
1. 无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。
2. 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
3. 可扩展性,节点可动态添加或删除。
4. 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。
缺点:
1. 资源隔离性较差,容易出现相互影响的情况。
2. 数据通过异步复制,不保证数据的强一致性。
6. 缓存相关问题
6.1 缓存雪崩
设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期
解决办法:
大多数系统设计者考虑用加锁或者队列的方式保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就是缓存失效时间分散开。
6.2 缓存穿透
指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空。
解决办法:
最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空,把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
7. Redis为什么是单线程
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了,Redis利用队列技术将并发访问变为串行访问。
8. 单线程的Redis为什么快
1. 纯内存操作
2. 单线程操作,避免了频繁的上下文切换
3. 采用了非阻塞I/O多路复用机制
9. Redis适合场景
会话缓存
最常用的一种使用Redis的情景是会话缓存(session cache)。
队列
Reids在内存存储引擎领域的一大优点是提供list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。
排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单。
发布/订阅
发布/订阅的使用场景确实非常多,可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!