Redis
简介
Redis(Remote Dictionary Server),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可给予内存亦可持久化的日志型、Key-Value数据库,并提供多语言的API。
Redis支持存储的value类型很多,包括string,list,set,zset和hash。这些数据类型都支持push/pop、add/remove及取交集并集和差集操作,且这些操作满足原子性。为了保证效率,数据都是缓存在内存中,redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
Redis持久化方式
Redis的数据都存放在内存中,因此,若不进行持久化配置,在redis重启后数据就会全部丢失,因此需要开启redis的持久化功能,将数据保存到磁盘中。Redis支持两种模式的持久化:RDB和AOF。
RDB方式
RDB方案是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储到.rdb文件中。
优点 | 缺点 |
---|---|
Redis只包含一个文件,方便备份管理 | 高可用性不如AOF方法(数据可能丢失) |
有利于灾难恢复(只有一个恢复文件) | 数据量过大时,其fork出来的持久化子进程可能导致服务器停止服务。 |
性能最大化(只需fork一个进程,后续由进程完成持久化工作) | |
数据极大时启动速率比AOF更高 |
AOF方式(Append Only File)
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。AOF存放的是具体的命令,数据存放在.aof文件中。
优点 | 缺点 |
---|---|
数据安全性更高 | 相同数量的数据集,AOF文件要大于RDB文件 |
写入操作采用的是append模式,写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。 | AOF的运行速率一般比RDB慢 |
rewrite机制保证数据安全性 | |
文件易于理解,可读性高 |
对比
RDB | AOF | |
---|---|---|
保存内容 | 二进制数据文件 | Redis命令 |
数据恢复速度 | 快照恢复,速度快 | 命令多,速度慢 |
数据恢复完整性 | 可能丢数据 | 比RDB高 |
Q&A
- 如何最大化保证恢复数据的速度及数据完整性?
混合持久化。以RDB的方式全量持久化数据,保证数据恢复的速度;并以增量的方式持久化修改命令,保证数据的完整性。最终以RDB和AOF共存形式写入.AOF文件
Redis集群架构模式
单机模式
**定义:**缓存服务中只有一台redis对应用提供服务。
**缺点:**宕机时应用无法使用,且吞吐量低。
主从模式
**定义:**缓存服务中有一台主和多台从服务器,主服务器提供写,从服务器提供读,实现读写分离。缓解单台Redis压力。
**缺点:**主节点宕机需要手动设置新的住服务器。
哨兵模式
**定义:**除有主从Redis服务器对外提供他服务外,还有哨兵用于监控主从redis的状态,当主服务器宕机时可以从从服务器中自动选取新的主节点。
**缺点:**只有一台服务器用于写服务,大量写操作时主服务器成为性能瓶颈。
集群模式
**定义:**通过多个独立的主从Redis对外提供服务,解决可用和吞吐量问题。应用通过Hash选取具体使用哪个主从(有16384个哈希槽分散在主从节点上)
Redis主从同步机制
Redis提供了一系列主从同步机制,用于保证Redis集群的高可用性。一般来说,Redis在进行主从同步有两个步骤:
**1.同步:**将从服务器的数据库状态更新成主服务器当前的数据库状态;
**2.命令传播:**当主服务器数据库状态修改后,主从服务器数据库状态不一致,此时需要让主从数据同步到一致;
(注:在Redis2.8之前,主从复制一定会执行上述两个步骤)
同步
同步是从服务器的主动过程。从服务器通过sync命令实现对主服务器的同步操作。首先从服务器对主服务器发送sync(即synchronization)命令,主服务器在收到sync命令后,主服务器执行bgsave命令生成rdb文件,并在缓冲区中记录从现在开始执行的写命令(生成rdb文件比较耗时,需要记录期间的数据变化操作,避免同步后数据仍不一致)。
主服务器执行完bgsave指令后,将生成的.rdb文件发送给从服务器,用于让从服务器更新数据。接着主服务器把缓冲区记录的写命令发送给从服务器,从服务器执行完写命令后,即实现主从服务器的数据一致。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-raI2zJl4-1643348231088)(C:\Users\123\AppData\Roaming\Typora\typora-user-images\image-20211025165353793.png)]
命令传播
命令传播是主服务器主动的过程。经过同步操作后,主从数据库状态已经保持一致性。然而当主服务器再次发生写操作时,主从服务器的状态又会不一致。为了保证数据一致性,主服务器就需要向从服务器执行命令传播操作,把主服务器上刚执行的写命令发送给从服务器执行。(*若直接进行同步操作,由于生成rdb文件消耗的硬件资源极多,而且由于从服务器中包含大部分数据,浪费网络资源,该策略不适用于生产)
Redis2.8之后对主从同步机制进行了优化。主从同步问题主要应用于两种场景:1.初次复制:从服务器第一次连接主服务器,此时采用传统同步方式即可;2.断线后重连:处于命令传播阶段的从服务器由于网络终端复制后,从服务器重新连接。
在第二种情况时,redis采用psync进行同步。psync具有完整重同步和部分重同步两种模式。完全冲同步与sync相同,这里不在赘述。部分重同步通过三部分实现:
- 主从服务器的复制偏移
- 主服务器的复制积压缓冲区
- 服务器的运行id
**复制偏移:**执行复制的主从服务器都会分别维护各自的复制偏移量,在主服务器向从服务器发送n个字节数据时,主服务器把自己的复制偏移增加n。当从服务器收到这n个字节后,从服务器把自己的复制偏移也自增n。当同步中断时,从服务器检测到自身的复制偏移与主服务器不同,则此时清楚发生了断线重连,需要重新进行数据同步。
**复制积压缓冲区:**有了复制偏移,如何确定什么时候进行全同步,什么时候进行部分同步呢。这依赖于复制积压缓冲区。在Redis中,复制积压缓冲区是一个维护在主服务器的固定长度的先进先出的队列,其默认大小为1MB。当从服务器相助服务器发送psync命令时,需要带上自己的复制偏移量,这样主服务器可以通过检查从服务器的复制偏移+1的数据是否在缓冲区中,若存在,则可以进行部分同步,否则进行全同步。
**服务器运行id:**当从服务器断线重连后,如何保证其重新连接的主服务器就是原本的主服务器呢。此时需要依赖服务器运行id。当从服务器进行初步复制时,主服务器会把自己的运行id发送给从服务器。当从服务器断线重连后,会把记录的运行id发送给主服务器。若运行id相同,证明主服务器未发生改变;反之则表示主服务器发生了改变。
Redis分布式锁
性质:
- 互斥性:同一时刻,只有一个客户端可以获取锁;
- 安全性:锁的获取和释放是同一个客户端;
- 可用性:高可用的分布式锁系统及避免产生死锁;
实现:
思路一:setnx+expire加锁:弊端:盖房啊不保证原子性,当在setnx后expire前服务器宕机,容易导致锁无法释放。
思路二:使用SET原子方法加锁(Redis2.6.12后版本支持):SET key value NX EX (NX表示SET IF NOT Exist,后者表示添加过期时间),该方法把set和设置过期时间统一到一个原子操作。
思路三:比较删除解锁:DEL KEY前判断是否为加锁的客户端,通过添加客户端的唯一id到value中,每次删除前比较,一致再删除解锁,保证安全性。
Redis缓存穿透
缓存数据:缓存热点数据,可以使响应更快,减轻数据库负担。
问题:当缓存不可用时,请求会到数据库中,导致数据库压力过大。
缓存穿透
用户访问一个本身不存在的缓存时,请求落在DB中。
解决方案:1.使用布隆过滤器判断缓存是否存在;
2.为请求设置一个值为null的缓存,设置较短的过期时间;
缓存击穿
用户请求时发现缓存过期失效
解决方案:1.热点数据的缓存永不过期;
2.采用分布式锁,缓存失效后只有一个线程更新并写入数据库;
缓存雪崩
大面积的缓存击穿或服务不可用
解决方案:1.采用哨兵或集群提高架构可用性;
2.采用和缓存击穿一样的方式;
3.错开缓存数据的过期时间点,防止大面积失效;
补充知识
Redis与Memcached
对比项 | Redis | Memcached |
---|---|---|
延时 | 内存数据库,亚毫秒级延时 | 内存数据库,亚毫秒级延迟 |
易用性 | 语法简单,易用性强 | 语法简单,易用性强 |
分布式存储 | 支持集群方式水平扩展 | 支持分布式存储 |
多语言客户端 | Java,C,Python等30余种 | Java,C,Python等10余种 |
线程/进程 | 单核单线程;单线程通信,避免不必要的上下文切换,采用非阻塞IO(IO多路复用),减少多客户端连接时的资源损耗 | 支持多线程,可扩展;可通过增加CPU数量,提升Memcached性能;在key的值较大的场景中,性能优势较明显。 |
持久化存储 | 支持,有AOF和RBD两种方式 | 开源的Memcached不支持持久化 |
数据结构 | 支持哈希、列表、集合、有序集合等复杂的数据结构。有更多的应用场景 | 支持简单的字符串。 |
Lua脚本支持 | 支持 | 不支持 |
快照备份 | 支持;快照定期产生,因此不能保证数据100%不丢失;Redis会fork一个子进程用于生成快照,当数据较多时,可能产生Redis服务短暂中断。 | 不支持 |
数据迁移 | 支持;可通过RDB快照恢复,或者AOF文件回放的方式,将数据备份并迁移到新的Redis实例上 | 不支持 |
Key的大小 | Key的值最大可以有1G | 1M |
多数据库 | Redis单机和主备支持多个数据库,默认256个DB;Proxy集群和Cluster集群只支持一个数据库,为DB0 | 不支持 |
Redis的CAP性
C:Consistency(一致性)
A:Availability(可用性)
P:Partition(分区性,指系统间能够容忍节点之间的网络通信的故障,对于集群来说,分区容错性不可避免)
在单机版的Redis中,每个Master之间是没有任何通信的,所以我们一般在Jedis客户端或者Codis这样的代理中做Pre-sharding。按照CAP理论来说,单机版的Redis属于保证CP(Consistency & Partition-Tolerancy)而牺牲A(Availability),也就说Redis能够保证所有用户看到相同的数据(一致性,因为Redis不自动冗余数据)和网络通信出问题时,暂时隔离开的子系统能继续运行(分区容忍性,因为Master之间没有直接关系,不需要通信),但是不保证某些结点故障时,所有请求都能被响应(可用性,某个Master结点挂了的话,那么它上面分片的数据就无法访问了)。
有了Cluster功能后,Redis从一个单纯的NoSQL内存数据库变成了分布式NoSQL数据库,CAP模型也从CP变成了AP。也就是说,通过自动分片和冗余数据,Redis具有了真正的分布式能力,某个结点挂了的话,因为数据在其他结点上有备份,所以其他结点顶上来就可以继续提供服务,保证了Availability。然而,也正因为这一点,Redis无法保证曾经的强一致性了。这也是CAP理论要求的,三者只能取其二。
参考文献
Redis面试经:https://www.zhihu.com/topic/19557280/top-answers
百度百科:https://baike.baidu.com/item/Redis/6549233?fr=aladdin
Redis与Memcached:https://segmentfault.com/a/1190000023217491
https://support.huaweicloud.com/productdesc-dcs/RedisAndMemcachedChoose.html
Redis的CAP性:https://www.jianshu.com/p/c01e81eaa9ab