Redis简介
Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存、MQ、分布式锁,支持事务。
它支持多种类型的数据结构,如字符串(Strings),散列(Hash),列表(List),集合(Set),有序集合(Sorted Set或者是ZSet)与范围查询,Bitmaps,Hyperloglogs 和地理空间(Geospatial)索引半径查询。其中常见的数据结构类型有:String、List、Set、Hash、ZSet这5种。
Redis 内置了复制(Replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(Transactions) 和不同级别的磁盘持久化(Persistence),并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(High Availability)。
数据库的工作模式按存储方式可分为:硬盘数据库和内存数据库。Redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度极快。
(1)硬盘数据库的工作模式:
(2)内存数据库的工作模式:
一、Redis的持久化
redis与memcached相比,不同在于redis可支持持久化,且数据结构丰富:string, list, set, zset, hash等多种数据结构。
1.1、前言
Redis的所有数据都存储到内存中,然后不定期的以异步的方式存储到磁盘上(也称为半持久化模式),也可以把每一次数据变化都写入到一个aof(append only file)文件中(称为全持久化模式)。
由于redis中的数据都存在内存中,如果没有配置持久化,redis重启后数据就全丢了。于是需要开启redis的持久化功能,将数据保持到磁盘上。这样当redis重启后,就可以从磁盘上的文件恢复数据到内存。
1.2、Redis的持久化方式
1.2.1、redis支持两种方式持久化内存数据到磁盘:
- RDB:将redis内存中的数据,全量定时dump到磁盘文件上。
- AOF:将redis的操作日志以文件追加的方式写入磁盘文件。AOF提供了三种数据同步策略:每秒同步、每修改同步、不同步。
1.2.2、二者区别:
RDB:定时将redis内存中的全量数据写入磁盘,实际操作过程是fork一个子进程,先将redis的全部内存数据写入一个临时文件,写入成功后,再替换之前的文件,然后用二进制压缩存储。
AOF:以记录日志的方式记录redis每一个写、删操作,查询操作不记录。
1.2.3、二者优缺点
1、RDB
优势:
- Redis的备份文件只有一个,灾难恢复时方便
缺点:
- 备份RDB文件时,需要花费时间长,一旦此时宕机,未写入磁盘的数据将丢失
- RDB是fork一个子进程来协助完成数据集备份的,因此当数据集很大时,服务器可能会停服几百秒~1分钟。
2、AOF
优势:
- 相比RDB而言,具备更好的数据安全性:以每秒同步为例,如果某时redis宕机,那么丢失的只是没来得及同步的1秒的数据。且这种append操作不会破坏原来已备份的数据文件。
缺点:
- AOF文件要大于RDB文件,RDB文件在恢复大数据集时的速度要快于AOF文件
1.2.4、二者如何选择
选择AOF:宁愿牺牲一些性能,来换取更高的缓存一致性
选择RDB:在写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再执行RDB。
1.3、常用配置
1.3.1、RDB持久化配置
redis会将数据集dump到dump.rdb文件中,我们可以通过配置文件来修改RDB的dump频率:在打开6379.conf文件后,搜索save,可以看见下面的配置信息:
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
1.3.2、AOF持久化配置
在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
二、Redis的主从同步
2.1、为什么需要主从同步
在分布式系统设计中,要避免单点问题,也就是说即便是Redis的某台机器挂了,也不能影响到业务的正常使用。
2.2、主从同步的过程
1、增量同步:也叫指令同步,从库按照顺序,重放在主库中进行的指令。就好比象棋的复盘。
Redis会把指令存放在一个环形队列当中,为什么要放一个环形队列呢?因为内存的容量毕竟有限,如果备机一直起不来,不可能把所有的内存都去存指令,也就是说,如果备机一直未同步,指令可能会被覆盖掉。
如果指令被覆盖掉了,那么肯定增量同步就不行了,这个时候只好寄出我们的杀器,全量同步。
2、全量同步:又称快照同步。
快照,就跟照片一样,记录着在某一个时间点所有键值状态。在Redis中,获取一个快照的命令并不难,我们可以手动创建一个快照,也可以通过配置,后台根据相关规则创建快照。在Redis中,我们可以通过save命令,就可以创建快照,因为Redis是单进程的,使用save命令会影响性能,所以通常我们会使用bgsave。
在redis.conf中,有着自动保存的配置,上述是Redis的默认配置,说的是在900秒内,如果有1个key变更,或者300秒内,有300个key变更,或者60秒内,有1个key变更,就会后台出发生成快照功能。
三、Redis单线程与高性能
3.1、Redis为什么可以支持高性能?
官方给出的数据是可以达到10万+的QPS(每秒内查询次数)
横轴是连接数,纵轴是QPS。
Redis为什么可以支持高性能呢?
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
以上几点都比较好理解,下边我们针对多路 I/O 复用模型进行简单的探讨:
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。
3.2、Redis为什么是单线程?
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)。
但是,我们使用单线程的方式是无法发挥多核CPU 性能,不过我们可以通过在单机开多个Redis 实例来完善!