引 言
Redis是什么?
即Remote Dictionary Server,即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis能干嘛?
- 内存存储和持久化:Redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
- 支持最新N个数据的操作:类似Top N的概念
- 设置过期时间的功能
- 发布订阅消息系统
- 定时器、计数器
Redis为什么这么快?
-
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
-
数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的
-
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
-
使用多路I/O复用模型,非阻塞IO
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的I/O事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。 这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路I/O复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量 -
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
Redis下载地址:redis.io/
最新版本5.0.8
Redis的安装
这里我们用Docker来安装Redis
// 拉取镜像
docker pull redis
// 运行镜像
docker run -di --name="redis" -p 6379:6379 redis
这样我们的redis就运行起来了
Redis的五大数据类型
- String(字符串)
- List(列表)
- Set(集合)
- Hash(哈希,类似java中的Map)
- Zset(有序集合)
这里就不对这五大数据类型进行详细介绍了
Redis的持久化
RDB
在指定的时间间隔内将内存的数据集快照写入磁盘,也就是行话说的Snapshot快照,它回复时将快照文件直接读到内存里(RDB保存的文件是dump.rdb)
Redis会单独创建(fork)一个子进程来进行持久化,会将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
如果需要进行大规模的数据恢复,且对于数据恢复的完整性不是很敏感,那RDB方式要比AOF方式更加的高效。RDB缺点是最后一次持久化后的数据可能会丢失
什么是Fork?
Fork的作用是复制一个与当前进程一样的进程,新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
如何触发RDB?
- save或bgsave命令
- flushall命令(会产生空的dump.rdb文件,无意义)
AOF
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只允许追加文件但不可改写文件,Redis启动时会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次来完成数据的恢复工作(AOF保存的文件是appendonly.aof) 如果AOF文件被写坏了怎么办? 执行Redis-check-aof --fix进行修复
Rewrit重写机制
AOF采用文件追加的方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后rename),遍历新进程的内存中数据,每条记录有一条的Set语句,重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
触发机制:Redis会记录上次重写时的AOF文件大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
优点:支持灵活的同步机制
--appendfsync always 同步操作,每次发生数据变更会立即记录到磁盘
--appendfsync everysec 异步操作,每秒记录
-- appendfsync no 从不同步
缺点:相同数据集的AOF文件要远大于RDB文件,恢复速度慢于RDB
如果两种持久化策略同时存在,Redis优先加载AOF文件
Redis事务
三个阶段:
- 开启:以multi开启一个事务
- 入队:将多个命令入列到事务中,不会立即执行,放到等待队列里
- 执行:由Exec命令触发事务
三大特性:
- 单独的隔离操作
- 没有隔离级别的概念
- 不保证原子性:Redis同一个事物里如果有一条命令执行失败,其后的命令仍可执行,不会回滚
Redis主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点 默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点 主从复制的作用主要包括:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量
- 高可用:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础
开启主从复制
主从复制的开启,完全是在从节点发起的;不需要我们在主节点做任何事情 从节点开启主从复制,有3种方式:
- 配置文件:在从服务器的配置文件中加入:slaveof masterip masterport
- 启动命令:redis-server启动命令后加入: slaveof masterip masterport
- 客户端命令:Redis服务器启动后,直接通过客户端执行命令:slaveof masterip masterport 则该Redis实例成为从节点
断开主从复制
通过slaveof masterip masterport命令建立主从复制关系以后,可以通过slaveof no one断开
从节点断开复制后,不会删除已有的数据,只是不再接受主节点新的数据变化
主从复制原理
主从复制过程大体可以分为3个阶段:连接建立阶段、数据同步阶段、命令传播阶段
- 保存主节点信息
- 主从建立socket连接
从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接;如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行 slaveof no one 取消复制 - 发送ping命令
连接建立成功后从节点发送 ping 请求进行首次通信,ping 请求主要目的如下: 一是检测主从之间网络套接字是否可用 二是检测主节点当前是否可接受处理命令 如果发送 ping 命令后,从节点没有收到主节点的pong回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连 - 权限验证 如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。
- 同步数据集 从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤
- 命令持续复制
当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性
第一次连接是全量复制,随后的数据同步是增量复制
Redis内存淘汰策略
- volatile-lru:从设置过期时间的数据集(server.db[i].expires)中挑选出最近最少使用的数据淘汰。没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失
- volatile-ttl:除了淘汰机制采用LRU,策略基本上与volatile-lru相似,从设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰,ttl值越大越优先被淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。当内存达到限制无法写入非过期时间的数据集时,可以通过该淘汰策略在主键空间中随机移除某个key
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰,该策略要淘汰的key面向的是全体key集合,而非过期的key集合
- allkeys-random:从数据集(server.db[i].dict)中选择任意数据淘汰
- no-enviction:禁止驱逐数据,也就是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失,这也是系统默认的一种淘汰策略。
Redis哨兵模式
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例
- 新建sentinel.conf
- 配置哨兵 sentinel monitor 主机名 ip port 2 最后一个数字2表示主机挂掉,slave需要多少票成为新主机
- 启动哨兵 Redis-sentinel /sentinel.conf