Redis

简介

Redis的场景:数据库DB、缓存Cache、消息队列MQ

产生原因:

  • Mysql的技术瓶颈:磁盘IO,速度慢
  • Redis:支持从内存读取数据

Redis支持的数据结构

  • 字符串String
  • 列表Lsit
  • 集合Set
  • 有序集合SortedSet
  • 哈希Hash
  • 消息队列Stream
  • 地理空间Geospatial
  • HyperLogLog
  • 位图Bitmap
  • 位域Bitfield

使用方式(命令行、API、图形化界面)

  • CLI(Command Line Interface)
  • API(Application Programming Interface)
  • GUI(Graphical User Interface)

Redis的优势

  • 性能高
  • 数据类型丰富,单键值对最大支持512MB大小的数据
  • 简单易用,支持所有主流编程语言
  • 支持数据持久化、主从复制、哨兵模式等高可用特性

Redis 为什么这么快?

Redis 内部做了非常多的性能优化,比较重要的有下面 3 点:

  1. Redis 基于内存,内存的访问速度比磁盘快很多;
  2. Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
  3. Redis 内置了多种优化过后的数据类型/结构实现,性能非常高。
  4. Redis 通信协议实现简单且解析高效。

1. Redis 基于内存

内存存储的优势

  • 内存的访问速度比硬盘快几个数量级。通过将数据存储在内存中,Redis 能够提供极低的延迟和高吞吐量,适合对性能有严格要求的应用,如实时分析和推荐系统。
  • 内存的特性使得Redis 能够处理高负载场景,但请注意,内存有限,数据持久化机制也是Redis设计的重要考虑。

2. 基于Reactor模式的事件处理模型

Reactor模式的设计

  • Redis采用了单线程的事件循环模型,这意味着所有的请求都在一个线程中处理,从而避免了多线程带来的复杂性和上下文切换的开销。
  • IO多路复用(如 epollselect)使得Redis可以高效地处理多个客户端的并发连接。通过一个事件循环,Redis能够在单个线程内高效管理输入和输出操作,减少了在多数情况下的资源消耗。

单线程优越性

  • 单线程可以简化代码逻辑,避免由于锁争用导致的性能瓶颈。
  • Redis通过高效的事件驱动机制,能支持大量并发连接而不会出现典型多线程程序的性能问题。

3. 多种优化过后的数据类型/结构

内置数据类型

  • Redis支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。每种数据结构都经过优化,能够高效地执行常见操作。
  • 例如,哈希结构非常适合于存储对象,集合和有序集合提供了快速的去重和排序功能。

性能优化的目的

  • 通过专门优化的数据结构,Redis能够针对特定场景,得到更快的响应时间和更高的数据处理能力,极大提高了整体的系统性能。

4. 简单且高效的通信协议

通信协议

  • Redis使用了一种简单的文本协议,称为RE-dis Serialization Protocol(RESP)。该协议使得客户端可以轻松地与服务器交互,并且解析效率很高。

高效性的效果

  • 简单的协议格式降低了理解和实现的复杂度,同时也使得网络数据序列化与反序列化的速度提高。这意味着客户端和服务器之间的通信延迟很低,有助于提高整体响应速度。

小结

Redis的设计充分考虑了高性能和高并发需求,结合内存存储、事件驱动模型、优化的数据结构及高效的通信协议,使其成为现代高性能应用程序的理想选择。同时,开发者在使用时也需注意数据持久化和内存管理,以确保数据的安全性和持久性。

Redis 除了做缓存,还能做什么?

  • 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:分布式锁详解
  • 限流:一般是通过 Redis + Lua 脚本的方式来实现限流。如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的 RRateLimiter 来实现分布式限流,其底层实现就是基于 Lua 代码+令牌桶算法。
  • 消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
  • 延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
  • 分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
  • 复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。

事务

Redis支持事务,即在一次请求中执行多个命令。步骤如下。

  • MULTI开启一个事务,将多个命令放入一个队列,入队到事务中,最后由 EXEC 命令触发事务, 一并执行事务中的所有命令。(DISCARD取消事务)

关系型数据库的事务一般是原子性操作(要么全部成功,要么全部失败)

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的

Redis不能保证事务中 的命令全部执行成功,但是可以提供一下三个重要的保证

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段

  • 开始事务。
  • 命令入队。
  • 执行事务。

Redis 线程模型(重要)

对于读写命令来说,Redis 一直是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作, Redis 6.0 版本之后引入了多线程来处理网络请求(提高网络 IO 读写性能)。

Redis 单线程模型了解吗?


持久化

redis的数据全部在内存中,如果突然宕机,数据就会全部丢失,未保证redis的数据在发生突发状况时不会丢失、或者只丢失少量,必须根据一些策略来把redis内存中的数据写到磁盘中,这样当redis服务重启时,就会将硬盘中的数据恢复到内存中。Redis持久化的意义就是为了保证突然宕机,内存数据不会全部丢失。

img

详细:Redis专题:万字长文详解持久化原理 - 码路印记 - SegmentFault 思否

主从复制

概念

主从复制,是指将一台Redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower) 。数据的复制是单向的,只能从主节点复制到父节点Master以写为主,Slave以读为主。

默认情况下,每台redis服务器都是主节点,且一个主节点能有多个从节点,但一个从节点只能有一个主节点。

作用

  1. 读写分离:主节点写,从节点读,提高服务器的读写负载能力
  2. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  3. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复 ; 实际上是一种服务的冗余。
  4. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载 ; 尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  5. 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

详情: Redis——Redis主从复制(工作流程详解)_redis主从复制过程-CSDN博客

哨兵模式

哨兵是一个分布式系统,你可以在一个架构中运行多个哨兵进程,这些进程使用流言协议来接收关于Master主服务器是否下线的信息,并使用投票协议来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。

概述

1.1、为什么要启动哨兵模式

当我们的redis主服务器宕机后,手动切换主从模式的这种人工干预,费事费力,并且会造成我们的服务一段时间不可用。所以我们通过哨兵模式来解决这个问题。

1.2、什么是哨兵模式

哨兵模式是对redis系统运行情况的监控,它是一个独立的进程,功能有二个:

  • 监控主机redis和从机redis是否正常运行
  • 主机故障后主动将从机转换为主机

1.3、哨兵工作原理

单个哨兵的工作原理:

单个哨兵,只需要监控主Redis,就可能得到从Redis。

img

多个哨兵的工作原理:

多个哨兵,不仅同时监控主从Redis,而且哨兵之间互为监控。

多个哨兵,防止哨兵单点故障。

img

1.4、哨兵功能

集群监控:负责监控主从集群中的Master和Slave进程是否正常工作。

故障转移(failover):如果Master宕机,会自动从Slave中选举出新的Master,进行主从自动切换。

配置中心:如果发生了故障转移,Sentinel负责通知客户端新的Master的地址。

消息通知:如果某个redis节点有故障,那么Sentsinel会发送报警消息给系统管理员。

常用命令

服务器相关命令

  • ping : 检测连接是否存活
  • echo: 在命令行打印一些内容
  • quit、exit: 退出客户端
  • shutdown: 退出服务器端
  • info: 返回redis相关信息
  • config get dir/* 实时传递接收的请求
  • showlog: 显示慢查询
  • select n: 切换到数据库n,redis默认有16个数据库(DB 0~DB 15),默认使用的第0个
  • dbsize: 查看当前数据库大小
  • move key n: 不同数据库之间数据是不能互通的,move移动键到指定数据库
  • flushdb: 清空当前数据库中的键值对。
  • flushall: 清空所有数据库的键值对。

key相关命令

在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。

常用命令:

  • keys * :查看当前数据库中所有的key
  • dbsize: 键总数
  • exists key: 检查键是否存在
  • del key [key …]: 删除键
  • expire key seconds: 键过期
  • ttl key: 获取键的有效时长
  • persist key: 移除键的过期时间
  • type key: 键的数据结构类型
  • randomkey: 随机返回数据库中一个键
  • rename key1 key2 : 重命名
  • renamex key1 key2 : 当key2不存在时,key1重命名

代码示例:

img

五大数据类型

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。其通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:

  • 字符串类型: string
  • 哈希类型: hash
  • 列表类型: list
  • 集合类型: set
  • 有序集合类型: sortedset(zset)

String(字符串)

字符串类型是Redis最基础的数据结构,其它的几种数据结构都是在字符串类型基础上构建的,字符串的值可以是:字符串、数字、二进制,但其值最大不能超过512M。

使用场景: 缓存、计数器、对象存储缓存(共享session)、限速

常用命令:

  • set key value: 设置一个key的value值
  • setnx key value: 仅当key不存在时进行set
  • setex key seconds value: set 键值对并设置过期时间
  • mset key value [key value …]: 设置多个key value
  • msetnx key1 value1 [key2 value2…]: 批量设置键值对,仅当参数中所有的key都不存在时执行,原子性操作,一起成功,一起失败
  • get key: 返回key的value
  • mget key [key …] : 批量获取多个key保存的值
  • exists key [key …]: 查询一个key是否存在
  • decr/incr key: 将指定key的value数值进行+1/-1(仅对于数字)
  • incrby/decrbyB key n: 按指定的步长对数值进行加减
  • incrbyfloat key n: 为数值加上浮点型数值
  • append key value: 向指定的key的value后追加字符串
  • strlen key: 返回key的string类型value的长度。
  • getset key value: 设置一个key的value,并获取设置前的值,如果不存在则返回null
  • setrange key offset value: 设置指定位置的字符
  • getrange key start end: 获取存储在key上的值的一个子字符串

代码示例:

img

List(列表)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),也可以获取指定范围指定下标的元素等。一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

两个特点:

1.列表中的元素是有序的,可以通过索引下标获取某个元素霍某个某个范围内的元素列表

2.列表中的元素可以是重复的

使用场景: 消息队列、栈、文章列表等。

常用指令:

  • 添加操作
  • lpush/rpush key value1[value2…]: 从左边/右边向列表中PUSH值(一个或者多个)
  • lpushx/rpushx key value: 向已存在的列名中push值(一个或者多个),list不存在 lpushx失败
  • linsert key before|after pivot value: 在指定列表元素的前/后 插入value
  • 查找操作
  • lindex key index: 通过索引获取列表元素
  • lrange key start end: 获取list 起止元素 (索引从左往右 递增)
  • llen key: 查看列表长度
  • 删除操作
  • lpop/rpop key: 从最左边/最右边移除值 并返回
  • lrem key count value: count >0:从头部开始搜索 然后删除指定的value 至多删除count个 count < 0:从尾部开始搜索… count = 0:删除列表中所有的指定value。
  • ltrim key start end: 通过下标截取指定范围内的列表
  • rpoplpush source destination: 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部
  • 修改操作
  • lset key index value: 通过索引为元素设值
  • 阻塞操作
  • blpop/brpop key1[key2] timout: 移出并获取列表的第一个/最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
  • brpoplpush source destination timeout: 和rpoplpush功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

代码示例:

img

Set(集合)

Redis的Set是string类型的无序集合,我们不能通过索引获取元素。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

应用场景: 标签(tag)

常用命令:

  • 集合内操作
  • sadd key member1[member2…]: 向集合中无序增加一个/多个成员
  • srem key member1[member2…]: 移除集合中一个/多个成员
  • scard key: 获取集合的成员数
  • smembers key: 返回集合中所有的成员
  • sismember key member: 查询member元素是否是集合的成员,若存在返回1,不存在返回0
  • srandmember key [count]: 随机返回集合中count个成员,count缺省值为1
  • spop key [count]: 随机移除并返回集合中count个成员,count缺省值为1
  • 集合间操作
  • sinter key1 [key2…]: 返回所有集合的交集
  • sinterstore destination key1[key2…]: 在SINTER的基础上,存储结果到集合中。覆盖
  • sunion key1 [key2…]: 返回所有集合的并集
  • sunionstore destination key1 [key2…]: 在SUNION的基础上,存储结果到及和张。覆盖
  • sdiff key1[key2…]: 返回所有集合的差集 key1- key2 - …
  • sdiffstore destination key1[key2…]: 在SDIFF的基础上,将结果保存到集合中。覆盖
  • smove source destination member: 将source集合的成员member移动到destination集合
  • sscan key [MATCH pattern] [COUNT count]: 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分

代码示例:

img

Hash(哈希)

几乎所有的编程语言都提供了哈希(hash)结构,Redis中 hash 是一个string类型的field和value的映射表value=