当前位置: 首页 > news >正文

Redis - 1 ( 11000 字 Redis 入门级教程 )

一:服务端高并发分布式结构演进之路

1.1 常见概念

概念定义生活例子类比
应用(Application)/ 系统(System)为完成一整套服务的程序或一组相互配合的程序群。为完成一项任务而组成的由一个人或一群相互配合的人构成的团队。
模块(Module)/ 组件(Component)在复杂应用中,为分离职责,将具有清晰职责和强内聚性的部分抽象为独立概念,以便理解和管理。军队中为攻克某据点,将人员分为突击小组、爆破小组、掩护小组、通信小组等。
分布式(Distributed)系统的多个模块部署在不同服务器上,模块间通过网络通信协作完成任务。为满足实际需求,一个原本在同一办公场地工作的团队被分散到多个城市,通过远程协作完成目标。
集群(Cluster)部署在多台服务器上、为实现特定目标而组成的一组组件。为攻克坚固的目标城市,指挥部集中大批炮兵部队形成一个炮兵打击集群。
分布式 vs 集群分布式注重物理形态,强调模块运行在不同服务器上;集群注重逻辑形态,强调多个组件协作完成特定服务目标。无需严格区分,分布式是物理分布,集群是逻辑协作。
主(Master)/ 从(Slave)集群中承担更多职责的称为主,其他承担附属职责的称为从。例如 MySQL 集群中,主库负责写入操作,从库负责从主库同步数据。主库类似于军队指挥官,掌握更多职责;从库类似于协助执行任务的团队成员。
中间件(Middleware)提供不同应用程序之间通信的软件,充当技术、工具和数据库之间的桥梁。一家饭店从市场采购食材业务量变大后,成立采购部作为厨房和市场之间的桥梁。

1.2 评价指标

概念定义说明
可用性(Availability)指在单位时间段内,系统能够正常提供服务的概率或期望。计算公式为系统正常提供服务时长 / 一年总时长。例如,4个9表示99.99%的可用性,5个9表示99.999%的可用性。常用“高可用”(High Availability,HA)作为非量化目标,表达对系统可用性的追求。
响应时长(Response Time,RT)指用户从完成输入到系统返回响应的时间间隔。例如外卖业务的响应时长 = 拿到外卖的时间 - 完成点单的时间。通常衡量最小响应时长、平均响应时长和中位数响应时长。响应时长越小越好,但实际中需结合实现限制和业务需求具体判断。
吞吐(Throughput)指单位时间内系统成功处理的请求数量。例如,高速公路每分钟通过20辆车,则吞吐量为20。衡量系统处理请求的能力,通常以极短时间内的吞吐量为近似值。
并发(Concurrent)指系统同一时刻支持的最大请求数量。例如,高速公路的并发为同时行驶的车辆数量,如同一时刻有2辆车,则并发量为2。并发量常用“高并发”(High Concurrent)作为非量化目标,表达对系统并发能力的追求。
吞吐 vs 并发吞吐量关注的是单位时间内处理的请求总量,而并发量关注的是同一时间点同时处理的请求数量。两者通常一起衡量系统性能,实际中可通过极短时间的吞吐量近似代替并发量。

1.3 架构演进

1.3.1 单机架构

在初期阶段,我们可以依靠精干的技术团队快速将业务系统投入市场以进行验证,并灵活应对变化需求。由于前期用户访问量较小,对系统性能和安全性要求不高,且架构简单,无需专业运维团队,因此选择单机架构是一个合理的决定。当用户在浏览器中输入 www.bit.com 时,首先通过 DNS 服务将域名解析为 IP 地址 10.102.41.1,随后浏览器会访问该 IP 对应的应用服务。

在这里插入图片描述

1.3.2 应用数据分离架构

随着系统上线,我们如预期般取得了成功,吸引了一批忠实用户,系统访问量逐步上升,逐渐逼近硬件资源的极限。同时,团队在此过程中积累了丰富的业务流程经验。面对当前的性能压力,我们需要未雨绸缪,通过系统重构和架构优化来提升系统的承载能力。由于预算有限,我们选择以最小代价提升性能的方法,将应用和数据分离。与之前的架构相比,主要区别在于将数据库服务独立部署在同一数据中心的其他服务器上,应用服务通过网络访问数据库,从而提高了系统的承载能力。

在这里插入图片描述

1.3.3 应用服务集群架构

随着系统的广受欢迎,爆款产品的出现使单台应用服务器无法满足需求。单机架构的应用服务器首先遇到了性能瓶颈,技术团队面临两种解决方案,并围绕各方案的优劣展开了热烈讨论。

扩展方式定义优势劣势
垂直扩展 / 纵向扩展通过购买性能更高、价格更昂贵的应用服务器来处理更多流量,提升单台服务器的性能。无需对系统软件进行任何调整,实施简单。硬件性能与价格增长关系非线性,性能翻倍可能需要超过4倍的成本;硬件性能提升有明显上限。
水平扩展 / 横向扩展调整软件架构,增加应用层服务器,将用户流量分担到多台服务器上,提升系统整体承载能力。成本相对较低,扩展空间大,性能提升上限高。增加系统复杂性,对技术团队的经验要求更高。

经过团队的学习、调研和讨论,我们最终选择了水平扩展方案来解决问题,但这需要引入一个新的组件——负载均衡。负载均衡的作用是解决用户流量如何分发到不同应用服务器的问题,它是一个专门用于流量分发的系统组件。在实际应用中,负载均衡不仅工作在应用层,也可能涉及网络层。同时,流量调度的算法种类繁多,这里简单介绍几种常见的算法。

算法定义特点
Round-Robin 轮询算法将请求依次公平地分发到不同的应用服务器上。简单公平,适用于服务器性能一致的场景。
Weight-Round-Robin 权重轮询算法为不同服务器设置权重,根据权重大小分配请求,使性能更强的服务器处理更多请求。能者多劳,适用于服务器性能不同的场景。
一致哈希散列算法根据用户的特征值(如 IP 地址)计算哈希值,并根据哈希结果分发请求,确保来自相同用户的请求总是分配到同一台服务器。保证请求一致性,适用于需要为特定用户提供固定服务的场景,例如专项客户经理服务。

在这里插入图片描述

1.3.4 读写分离 / 主从分离架构

在将用户请求通过负载均衡分发到不同的应用服务器后,系统可以并行处理请求,并通过动态扩展服务器数量来缓解压力。然而,在当前架构下,无论扩展多少台应用服务器,这些请求最终都需要从数据库进行读写操作。随着业务增长,数据库的压力逐渐成为系统承载能力的瓶颈。与应用服务器不同,数据库由于其特殊性无法简单地通过横向扩展来解决问题。原因在于,数据分散到多台服务器后,无法保证数据的一致性。数据的一致性是指在同一系统中,无论何时何地,用户看到的数据都应保持统一。例如,在银行的转账系统中,如果一笔转账的金额在一台数据库中被修改,而另一台数据库中未修改,用户看到的账户金额将是错误的。

为了解决这一问题,我们采用主从架构的方式。系统中保留一个主数据库处理写操作,而其他数据库作为从数据库,只负责从主库同步数据并处理读请求。从库通过数据同步机制维持与主库一致的状态。这样,所有写操作由主库承担,而读操作则分散到多个从库,从而分担数据库的压力。由于大多数系统中读写请求的比例极不均衡,例如100次读对应1次写,因此通过将读请求分散到各个从库,数据库的整体压力显著降低。当然,这种架构也有一定的代价,即主库到从库的数据同步存在时间成本,但这一问题暂时不在讨论范围内。

在这里插入图片描述

1.3.5 引入缓存 ⸺ 冷热分离架构

随着访问量的持续增加,我们发现业务中有一部分数据的读取频率远高于其他数据,这部分被称为热点数据,而其余为冷数据。针对热点数据,为了提升读取响应时间,我们引入本地缓存和分布式缓存机制。例如,缓存热门商品信息或热门商品的 HTML 页面。通过缓存,可以在数据库读写前拦截绝大多数请求,从而大幅降低数据库的压力。具体技术包括使用 Memcached 作为本地缓存,Redis 作为分布式缓存,同时需要解决缓存一致性、缓存穿透、缓存击穿、缓存雪崩以及热点数据集中失效等问题。在这里插入图片描述

1.3.6 垂直分库

随着业务数据量的不断增加,将大量数据存储在同一个数据库中已显得力不从心,因此可以根据业务需求对数据进行分库分表。例如,对于评论数据,可以按照商品ID进行哈希路由到对应的表中存储;对于支付记录,可以按小时创建表,并进一步拆分为小表,通过用户ID或记录编号来路由数据。这样,只要实时操作的表数据量足够小,并且请求能够均匀分发到多台服务器上的小表,就能通过水平扩展的方式提升数据库性能。其中,像 Mycat 这样的工具可以支持在大表拆分为小表情况下的访问控制。然而,这种方法显著增加了数据库运维的难度,对DBA提出了更高的要求。

当数据库设计发展到这种结构时,可以称为分布式数据库。尽管它在逻辑上是一个整体,但其不同部分由独立的组件实现。例如,分库分表的管理和请求分发由 Mycat 实现,SQL 的解析依赖单机数据库,读写分离可能由网关和消息队列完成,查询结果的汇总则由数据库接口层处理。这种架构是一类大规模并行处理(MPP)架构的实现方式,能够在高并发和大数据量的场景中发挥显著优势。

在这里插入图片描述

1.3.7 业务拆分 ⸺ 微服务

随着人员的增加和业务的发展,我们将业务拆分给不同的开发团队维护。每个团队独立实现自己的微服务,并通过隔离数据的直接访问来降低耦合性。团队之间的服务调用可以通过 Gateway、消息总线等技术实现关联。此外,一些通用功能,如用户管理、安全管理、数据采集等业务,也可以提取为公共服务,供各团队共享使用,从而提高整体开发效率和系统灵活性。

在这里插入图片描述

二: Redis

2.1 Redis 简介

Redis 是一种基于键值对(key-value)的 NoSQL 数据库,与其他键值对数据库不同,Redis 的值可以是多种数据结构和算法的组合,如字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(zset)、位图(Bitmaps)、HyperLogLog 和地理信息定位(GEO)等,因此能够满足多种应用场景。Redis 将所有数据存储在内存中,具备极高的读写性能,同时通过快照和日志的方式将数据持久化到硬盘上,保证在断电或机器故障等情况下数据不会丢失。此外,Redis 还提供了键过期、发布订阅、事务、流水线、Lua 脚本等功能,灵活强大。在合适的场景中,Redis 如同一把功能丰富的瑞士军刀,发挥出强大的作用。

2.2 Redis 的特性

2.2.1 速度快

正常情况下 Redis 执行命令的速度非常快,根据官方数据,读写性能可以达到每秒 10 万次。当然,这也与机器性能有关,但这里暂不讨论硬件差异,仅从以下四个方面分析 Redis 高速性能的原因:

原因描述
数据存储在内存中Redis 的所有数据都存储在内存中,根据 Google 2009 年给出的各层级硬件执行速度表明,内存的高访问速度是 Redis 快速性能的主要原因。
使用 C 语言实现Redis 使用 C 语言编写,C 语言与操作系统的交互更紧密,程序执行速度相对更快。
单线程架构Redis 使用单线程架构,避免了多线程可能导致的竞争问题,从而提高了执行效率。6.0 版本引入了多线程机制,但仅用于处理网络和 IO,不涉及命令执行,命令仍采用单线程模式。
精细优化的源代码Redis 源代码经过精心打磨,既追求性能又兼顾优雅,被评价为少有的性能与设计俱佳的开源代码。

2.2.2 基于键值对的数据结构服务器

几乎所有编程语言都提供类似字典的数据结构,例如 C++ 的 map、Java 的 map、Python 的 dict 等,这种以键值对方式组织数据的方式在开发中非常常见。而 Redis 不同于普通的键值对数据库,除了支持字符串作为值,还支持多种复杂的数据结构,这不仅方便应对多种应用场景,还能显著提高开发效率。Redis 的全称是 Remote Dictionary Server,主要提供五种数据结构:字符串(string)、哈希(hash)、列表(list)、集合(set)和有序集合(ordered set/zset)。此外,在字符串基础上,衍生出了位图(Bitmaps)和 HyperLogLog 等特殊数据结构,并在 Redis 3.2 版本中增加了 GEO(地理信息定位)功能,以支持 LBS(基于位置服务)的开发。在这些强大数据结构的帮助下,开发者可以构建出更多“有趣”和实用的应用。

2.2.3 丰富的功能

除了 5 种数据结构,Redis 还提供了许多额外的功能:

功能描述
键过期功能提供键的过期机制,可用于实现缓存功能。
发布订阅功能支持发布订阅机制,可以用来构建消息系统。
Lua 脚本支持支持 Lua 脚本功能,可以利用 Lua 创造出新的 Redis 命令,增强灵活性。
简单事务支持提供简单的事务功能,可以在一定程度上保证事务特性,支持多命令的原子性操作。
流水线(Pipeline)功能客户端可以将一批命令一次性发送到 Redis,减少网络开销,提高执行效率。

2.2.4 简单稳定

Redis 的简单性主要体现在以下三个方面:首先,Redis 的源码非常精简,早期版本只有约 2 万行代码,3.0 版本后由于增加了集群功能,代码量也仅增至约 5 万行,相较于许多 NoSQL 数据库,代码量要少得多,这使得开发和运维人员完全可以深入理解其源码。其次,Redis 使用单线程模型,这不仅让服务端的处理模型更简单,同时也简化了客户端开发。最后,Redis 不依赖操作系统的外部类库(例如 Memcached 依赖 libevent),而是自行实现了事件处理的相关功能。尽管 Redis 设计简单,但其稳定性极高,在大量使用场景中,因 Redis 自身 BUG 导致宕机的情况非常少见。

2.2.5 客户端语言多

Redis 提供了简单的 TCP 通信协议,使得许多编程语言可以轻松接入。同时,Redis 因受到社区和各大公司的广泛认可,支持它的客户端语言也非常丰富,几乎涵盖了所有主流编程语言,如 C、C++、Java、PHP、Python、NodeJS 等。后续将对 Redis 的客户端使用进行详细说明。

2.2.6 持久化

通常情况下,将数据存放在内存中存在一定风险,一旦断电或机器故障,重要数据可能会丢失。为了解决这一问题,Redis 提供了两种持久化方式:RDB 和 AOF。这两种策略可以将内存中的数据保存到硬盘,从而保障数据的持久性。后续将对 Redis 的持久化机制进行详细说明。

在这里插入图片描述

2.2.7 主从复制

Redis 提供了复制功能,可以创建多个数据完全相同的 Redis 副本(Replica),这也是实现分布式 Redis 的基础。后续将对 Redis 的复制功能进行详细演示。

在这里插入图片描述

2.2.8 高可用和分布式

Redis 提供了高可用的实现方式,如 Redis 哨兵(Redis Sentinel),用于故障检测和自动故障转移。同时,Redis 还支持 Redis 集群(Redis Cluster),实现真正的分布式架构,具备高可用性、读写扩展性和容量扩展能力。

2.3 Redis 的应用场景

应用场景描述
缓存(Cache)缓存机制广泛应用于大型网站,可加速数据访问速度并降低后端数据源压力。Redis 提供键值过期时间设置、灵活的内存控制和淘汰策略,为网站稳定性保驾护航。
排行榜系统Redis 提供列表和有序集合结构,支持按热度、发布时间或复杂维度构建排行榜系统,是开发各种排行榜功能的理想选择。
计数器应用计数器在网站中至关重要,如视频播放数或电商浏览数。Redis 天然支持计数功能,性能卓越,可轻松应对高并发场景,是计数器系统的重要选择。
社交网络Redis 支持社交网站的关键功能,如赞/踩、粉丝、共同好友/喜好、推送和下拉刷新。其灵活的数据结构可轻松实现这些功能,并高效处理大规模访问量。
消息队列系统消息队列是大型网站的基础组件,具有业务解耦和削峰特性。Redis 提供发布订阅和阻塞队列功能,虽然不如专业消息队列强大,但足以满足一般消息队列需求。

2.4 Redis 重要文件及作用

Redis 的安装过程就跳过了,我们直接讲 Redis 中重要文件的作用

程序/工具描述
redis-serverRedis 服务器程序,是 Redis 的核心运行程序。
redis-check-aof修复 AOF 文件的工具,是 redis-server 的软链接。
redis-check-rdb修复 RDB 文件的工具,是 redis-server 的软链接。
redis-sentinelRedis 哨兵程序,用于监控 Redis 集群的高可用性,是 redis-server 的软链接。
redis-cliRedis 命令行客户端程序,常用于学习和测试 Redis 操作。
redis-benchmark用于对 Redis 性能进行基准测试的工具。
redis-shutdown专用于停止 Redis 的脚本程序。
文件/目录描述
/etc/redis.confRedis 服务器的配置文件,用于定义 Redis 的运行参数和行为。
/etc/redis-sentinel.confRedis Sentinel 的配置文件,用于配置 Redis 哨兵程序的运行参数和行为。
/var/lib/redis/Redis 持久化文件(RDB 和 AOF)的默认存储目录,持久化时会在该目录下生成相关文件。
/var/log/redis/Redis 日志文件的默认存储目录。运行期间生成的日志按天分割,过期日志会以 gzip 格式压缩保存,方便查看运行情况。

2.5 Redis 命令行客户端

现在我们已经启动了 Redis 服务,接下来介绍如何使用 redis-cli 来连接和操作 Redis 服务。redis-cli 提供了两种方式连接 Redis 服务器,具体如下。

连接方式描述
交互式方式使用 redis-cli -h {host} -p {port} 连接到 Redis 服务,连接后可以在交互式环境中执行所有操作,无需重复输入 redis-cli。
命令方式使用 redis-cli -h {host} -p {port} {command} 直接执行命令并获取返回结果,无需进入交互式环境。
  1. 交互方式:
[root@host ~]# redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set key hello
OK
127.0.0.1:6379> get key
"hello"
  1. 命令方式:
[root@host ~]# redis-cli -h 127.0.0.1 -p 6379 ping
PONG
[root@host ~]# redis-cli -h 127.0.0.1 -p 6379 set key hello
OK
[root@host ~]# redis-cli -h 127.0.0.1 -p 6379 get key
"hello"

在这里插入图片描述

三: 预备知识

在这里插入图片描述

3.1 KEYS pattern

KEYS pattern 返回匹配指定 pattern 的所有键。

通配样式描述解释
h?llo匹配 hello、hallo 和 hxllo? 表示匹配任意一个字符
h*llo匹配 hllo 和 heeeello* 表示匹配零个或多个任意字符
h[ae]llo匹配 hello 和 hallo,但不匹配 hillo[ae] 表示匹配 a 或 e
h[^e]llo匹配 hallo、hbllo 等,但不匹配 hello[^e] 表示匹配除 e 之外的任意一个字符
h[a-b]llo匹配 hallo 和 hbllo[a-b] 表示匹配 a 到 b 范围内的任意一个字符
MSET firstname Jack lastname Stuntman age 35
"OK"KEYS *name*
1) "firstname"
2) "lastname"KEYS a??
1) "age"KEYS *
1) "age"
2) "firstname"
3) "lastname"

3.2 EXISTS

EXISTS 用于判断一个或多个 key 是否存在,返回存在的 key 的数量。

redis> SET key1 "Hello"
"OK"redis> EXISTS key1
(integer) 1redis> EXISTS nosuchkey
(integer) 0redis> SET key2 "World"
"OK"redis> EXISTS key1 key2 nosuchkey
(integer) 2

3.3 DEL

DEL 命令用于删除一个或多个 key,返回值为成功删除的 key 的数量。

redis> SET key1 "Hello"
"OK"redis> SET key2 "World"
"OK"redis> DEL key1 key2 key3
(integer) 2

3.4 EXPIRE

EXPIRE 命令为指定的 key 添加秒级的过期时间。如果设置成功返回值为 1;如果设置失败返回值为 0,因为 key 可能不存在,所以导致设置失败。

redis> SET mykey "Hello"
"OK"redis> EXPIRE mykey 10
(integer) 1redis> TTL mykey
(integer) 10

3.5 TTL

TTL 用于获取指定 key 以秒为单位的剩余过期时间。返回值为剩余的过期时间;如果返回 -1 表示该 key 没有设置过期时间,返回 -2 表示该 key 不存在。

redis> SET mykey "Hello"
"OK"redis> EXPIRE mykey 10
(integer) 1redis> TTL mykey
(integer) 10

3.6 TYPE

TYPE 命令用于返回指定 key 的数据类型,可能的返回值包括:none(key 不存在)、string(字符串)、list(列表)、set(集合)、zset(有序集合)、hash(哈希)和 stream(流)。

redis> SET key1 "value"
"OK"redis> LPUSH key2 "value"
(integer) 1redis> SADD key3 "value"
(integer) 1redis> TYPE key1
"string"redis> TYPE key2
"list"redis> TYPE key3
"set"

3.7 Redis 数据结构和内部编码

Redis 提供多种数据结构,包括 string(字符串)、list(列表)、hash(哈希)、set(集合)和 zset(有序集合),这些是对外暴露的基本数据类型。实际上,Redis 针对每种数据结构都有多种底层内部编码实现,并会根据具体场景自动选择最适合的内部编码,以优化性能和存储效率。

在这里插入图片描述

数据结构内部编码
stringraw, int, embstr
hashhashtable, ziplist
listlinkedlist, ziplist
sethashtable, intset
zsetskiplist, ziplist
内部编码作用
raw用于存储较大的字符串,直接以原始格式保存,适合处理大数据量的字符串值。
int用于存储整型数据,将字符串转换为整数存储,减少内存消耗,提高操作效率。
embstr用于存储小的、不可变的字符串,提供高效的内存分配和释放,适合短字符串的快速存取操作。
hashtable用于存储哈希表,适用于包含较多键值对或键值对较大的情况,支持快速查找、插入和删除操作。
ziplist用于存储紧凑型的数据,适用于元素数量较少且每个元素较小的情况,通过连续内存存储节省空间,但在元素较多时性能会下降。
linkedlist适用于列表元素数量较多或每个元素较大的情况,通过指针连接元素,支持快速插入和删除,适合处理大规模数据。
intset用于存储小范围的整数集合,元素较少时内存占用低,适合集合元素为整数且数量较少的场景。
skiplist用于存储有序集合的数据,支持快速范围查找和排序操作,适合处理大范围的有序数据,如排名或分值范围查询。
quicklist是 ziplist 和 linkedlist 的结合体,既保留了 ziplist 的内存紧凑性,又支持 linkedlist 的快速插入删除操作,适合复杂列表场景。

可以看到每种数据结构通常都有两种或以上的内部编码实现,例如 list 数据结构包含 linkedlist 和 ziplist 两种编码。同时,一些内部编码(如 ziplist)可以被多种数据结构共用作为其内部实现。具体的内部编码可以通过执行 object encoding 命令进行查询。

127.0.0.1:6379> set hello world
OK127.0.0.1:6379> lpush mylist a b c
(integer) 3127.0.0.1:6379> object encoding hello
"embstr"127.0.0.1:6379> object encoding mylist
"quicklist"

可以看出,键 hello 的值采用了 embstr 编码,而键 mylist 的值则使用了 ziplist 编码。Redis 的这种设计带来了两个重要优势:

优势描述
可改进内部编码内部编码的优化不会影响外部数据结构和命令。比如 Redis 3.2 引入了 quicklist,将 ziplist 和 linkedlist 的优点结合,为列表类型提供了更优的内部编码实现,用户几乎无感知。
场景化优化不同的内部编码在不同场景下发挥优势。比如 ziplist 节省内存,但在列表元素较多时性能下降,此时 Redis 会根据配置自动将编码切换为 linkedlist,用户无需干预,完全无感知。

3.8 单线程架构

Redis 采用单线程架构来实现高性能的内存数据库服务。下面通过多个客户端命令调用的示例,说明 Redis 单线程的命令处理机制;接着分析其单线程模型为何能够实现如此高的性能;最后解释为什么理解单线程模型是使用和运维 Redis 的关键。首先我们开启三个 redis-cli 客户端同时执行命令

  1. 客⼾端 1 设置⼀个字符串键值对:
127.0.0.1:6379> set hello world
  1. 客⼾端 2 对 counter 做⾃增操作:
127.0.0.1:6379> incr counter
  1. 客⼾端 2 对 counter 做⾃增操作:
127.0.0.1:6379> incr counter

Redis 客户端发送的命令经历了发送命令、执行命令、返回结果三个阶段,其中重点在于命令的执行过程。Redis 的单线程模型指的是:尽管从宏观上看,多个客户端似乎同时向 Redis 发送命令,但从微观角度来看,这些命令是以线性方式逐条执行的。虽然命令的执行顺序可能不确定,但一定不会有两条命令同时被执行。可以将 Redis 想象成只有一个服务窗口,多个客户端按照到达的先后顺序排队接受服务。例如,两条 incr 命令无论执行顺序如何,结果一定是正确的,不会发生并发问题,这就是 Redis 单线程执行模型的核心特点。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通常情况下,单线程的处理能力往往不如多线程。例如,运输 10,000 公斤货物,如果每辆车一次只能运载 200 公斤,需要 50 次才能完成;但如果有 50 辆车合理分工,只需一次即可完成任务。然而,Redis 使用单线程模型仍然能够实现每秒万级别的处理能力,这主要归结于以下三点原因。

原因描述
纯内存访问Redis 将所有数据存储在内存中,内存的响应时长大约为 100 纳秒,这是 Redis 达到每秒万级别访问的重要基础。
非阻塞 IORedis 使用 epoll 作为 I/O 多路复用技术,并通过自身的事件处理模型将连接、读写、关闭等操作转换为事件,避免在网络 I/O 上浪费时间。
单线程避免线程切换和竞态单线程简化了数据结构和算法的实现,使程序模型更加简单;同时避免了多线程环境下因线程竞争共享数据而导致的切换和等待消耗。

在这里插入图片描述

虽然单线程为 Redis 带来了许多优势,但也存在一个致命的问题:对单个命令的执行时间有严格要求。如果某个命令执行时间过长,其他命令将被阻塞在等待队列中,无法及时响应,从而导致客户端阻塞。这对 Redis 这样的高性能服务来说是非常严重的。因此,Redis 更适用于需要快速执行的场景。

相关文章:

Redis - 1 ( 11000 字 Redis 入门级教程 )

一:服务端高并发分布式结构演进之路 1.1 常见概念 概念定义生活例子类比应用(Application)/ 系统(System)为完成一整套服务的程序或一组相互配合的程序群。为完成一项任务而组成的由一个人或一群相互配合的人构成的团…...

python opencv的sift特征检测(Scale-Invariant Feature Transform)

sift 官方文档地址:https://docs.opencv.org/4.10.0/da/df5/tutorial_py_sift_intro.html 创建SIFT实例cv2.SIFT.create()特征检测sift.detect描述子sift.compute/sift.detectAndCompute画特征cv2.drawKeypoints 原图 特征点 代码 import cv2first ./12.pngsif…...

Xilinx FPGA的Bitstream比特流加密设置方法

关于Xilinx FPGA的Bitstream比特流加密设置方法更多信息可参阅应用笔记xapp1084。 使用加密bitstream分两个步骤: 将bitstream的AES密钥存储在FPGA芯片内将使用AES密钥加密的Bitstream通过SPI Flash或JTAG加载至FPGA芯片 AES密钥可以存储在两个存储区之一&#x…...

如何通过深度学习提升大分辨率图像预测准确率?

随着科技的不断进步,图像处理在各个领域的应用日益广泛,特别是在医疗影像、卫星遥感、自动驾驶、安防监控等领域中,大分辨率图像的使用已经成为了一项不可或缺的技术。然而,大分辨率图像带来了巨大的计算和存储压力,同…...

Oracle SqlPlus常用命令简介

参考资料 【SQL*Plus】SETシステム変数の設定前後の具体例 目录 一. 执行系命令1.1 执行系统命令1.2 执行sql脚本文件1.2.1 在数据库中执行sql脚本1.2.2 通过sqlplus执行sql脚本 二. show命令2.1 显示SqlPlus中的全部环境变量2.2 显示指定环境变量的设置 三. 时间显示3.1 set …...

【微服务】【Sentinel】认识Sentinel

文章目录 1. 雪崩问题2. 解决方案3. 服务保护技术对比4. 安装 Sentinel4.1 启动控制台4.2 客户端接入控制台 参考资料: 1. 雪崩问题 微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。动图演示: 在微服务系统…...

C++并行处理支持库 之六

C并行处理支持库 之六 std::promise构造器APIs应用实例 通过使用Futures标准库,我们可以获取异步任务返回值,捕获异步任务引发的异常。异步任务就是在独立线程中启动运行的函数。 这些值以共享状态进行通信,其中异步任务可以写入其返回值&…...

Linux-frp_0.61.1内网穿透的配置和使用

下载frp frp官网 https://gofrp.org/zh-cn/docs/setup/ frp安装包下载地址 https://github.com/fatedier/frp/releases?page1 下载之后在服务器上 解压 tar -zxvf frp_0.61.1_linux_amd64.tar.gztar:一个用于压缩和解压缩的工具。-z:表示使用 gzi…...

SpringBoot集成ECDH密钥交换

简介 对称加解密算法都需要一把秘钥,但是很多情况下,互联网环境不适合传输这把对称密码,有密钥泄露的风险,为了解决这个问题ECDH密钥交换应运而生 EC:Elliptic Curve——椭圆曲线,生成密钥的方法 DH&…...

深度学习的魔法世界

技术文章:深度学习的魔法世界 引言 嘿,今天我们要一起探索一个非常酷的魔法世界——深度学习!这是一门让计算机变得超级聪明的科学。我们会用最简单的语言来解释深度学习的基本概念,让你们也能轻松理解。 一、深度学习的六大魔…...

【超级详细】Vue3项目上传文件到七牛云的详细笔记

概述 继上一篇笔记介绍如何绑定七牛云的域名之后,这篇笔记主要介绍了如何在Vue3项目中实现文件上传至七牛云的功能。我们将使用Cropper.js来处理图像裁剪,并通过自定义组件和API调用来完成整个流程。 这里直接给出关键部分js代码,上传之前要先…...

设计模式-建造者模式

在面向对象的设计中,建造者模式(Builder Pattern) 是一种常用的设计模式,它属于创建型模式,旨在将复杂对象的创建与其表示分离。通过该模式,我们可以一步一步地构造一个复杂的对象,避免构造函数…...

springboot+vue实现SSE服务器发送事件

思路 一个基于订阅发布机制的SSE事件。客户端可以请求订阅api(携带客户端id),与服务器建立SSE链接;后续服务器需要推送消息到客户端时,再根据客户端id从已建立链接的会话中找到目标客户端,将消息推送出去。…...

IDEA | SpringBoot 项目中使用 Apifox 上传接口

目录 1 安装 Apifox Helper 插件2 获取 Apifox 的 API 访问令牌3 IDEA 中设置 API 访问令牌4 IDEA 中上传接口5 常见问题5.1 如何自动设置目录名5.2 如何自动设置接口名5.3 如何更改上传位置 Apifox 官方指南: https://apifox.com/help/applications-and-p…...

SpringBoot基础二

扩展SpringBoot 扩展SpringBoot中的SpringMVC的默认配置 SpringBoot默认已经给我们做了很多SpringMVC的配置,哪些配置? 1、视图解析器ViewResolver 2、静态资料的目录 3、默认首页index.html 4、图标名字和图标所在目录,favicon.ico 5、类型转…...

力扣第129题:求根到叶子节点数字之和 - C语言解法

力扣第129题:求根到叶子节点数字之和 - C语言解法 题目描述 给定一个二叉树,求根到叶子节点的数字之和。 每条从根到叶子的路径都代表一个数字。例如,根到叶子路径 1->2->3 代表数字 123。返回所有路径数字之和。 示例 1&#xff1…...

图像处理-Ch7-小波函数

个人博客!无广告观看,因为这节内容太多了,有点放不下,分了三节 文章目录 多分辨率展开(Multi-resolution Expansions)序列展开(Series Expansions)尺度函数(Scaling Function)例:哈尔尺度函数(Haar scaling func)多分…...

Unity中实现转盘抽奖效果(一)

实现思路: 旋转转盘的z轴,开始以角加速度加速到角速度最大值,结束的时候,以角加速度减速使角速度减少到0,然后转盘z轴旋转的角度就是加上每秒以角速度数值大小,为了使角度不能一直增大,对360度…...

小程序基础 —— 07 创建小程序项目

创建小程序项目 打开微信开发者工具,左侧选择小程序,点击 号即可新建项目: 在弹出的新页面,填写项目信息(后端服务选择不使用云服务,开发模式为小程序,模板选择为不使用模板)&…...

Apache Commons Pool :介绍与使用

Apache Commons Pool :介绍与使用 什么是 commons-pool2? commons-pool2 是 Apache Commons 提供的一个开源对象池实现框架。它旨在为应用程序提供通用的对象池支持,方便开发者管理资源(如数据库连接、网络连接等)复…...

(二)编译原生SDK以及配置交叉编译链

文章目录 编译原生SDKLinuxSDK的安装第一步解压LinuxSDK第二步安装依赖软件第三步解压Buildroot的dl文件 Linux系统镜像编译、生成第一步 配置编译环境第二步 编译 LinuxSDK编译上面配置好的 环境配置编译 LinuxSDK配置内核选项配置 Buildroot编译 Qt 库 编译生成 Linux 系统镜…...

YK人工智能(三)——万字长文学会torch深度学习

2.1 张量 本节主要内容: 张量的简介PyTorch如何创建张量PyTorch中张量的操作PyTorch中张量的广播机制 2.1.1 简介 几何代数中定义的张量是基于向量和矩阵的推广,比如我们可以将标量视为零阶张量,矢量可以视为一阶张量,矩阵就是…...

【游戏设计原理】41 - 游戏的核心

1. 如何理解? 这条原理主要在讲述“游戏核心”这一概念的重要性及其在游戏开发中的作用。游戏的核心是指决定游戏整体玩法和体验的核心元素,它通常是游戏的主要机制、目标或动作方式。理解这一原理时,我们可以从以下几个层面来考虑&#xff…...

GraalVM:云原生时代的Java虚拟机

1. 概述 GraalVM是由Oracle公司开发的一款高性能、多语言的虚拟机平台。它不仅兼容传统的JVM字节码执行,还引入了即时编译(JIT)技术的革新,以及对多种编程语言的支持。GraalVM旨在通过提供更高效的执行环境来满足云计算环境中日益…...

goView二开低代码平台1.0

官网文档地址:GoView 说明文档 | 低代码数据可视化开发平台 简介:GoView 是一个拖拽式低代码数据可视化开发平台,通过拖拽创建数据大屏,使用Vue3框架,Ts语言和NaiveUI组件库创建的开源项目。安装步骤和地址文档里都有…...

【golang】go errors 处理错误追踪打印堆栈信息

目录 背景使用参考 背景 使用原生go语言编程时,常常需要处理错误,然而golang中没有像java/python等其他语言的try-catch方式一样的方式来处理异常事件,只能通过函数返回值接收并处理错误。 在实践中,由于牛马的不熟练或随意处理错…...

【brew安装失败】DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0

从你提供的 nslookup 输出看,DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0,这通常意味着无法解析该域名或该域名被某些 DNS 屏蔽了。这种情况通常有几个可能的原因: 可能的原因和解决方法 本地 DNS 问题: 有可能是你的本…...

【Python系列】Python 连接 PostgreSQL 数据库并查询数据

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

深度学习利用Kaggle和Colab免费GPU资源训练

这两个平台,我先用的Colab,在修改完无数bug,成功训练完一个epoch后,超时了,查阅了官网资料,之后应该还可以用,但这个限制是动态的,你可能第二天就可以用,也没准下个月。遂…...

WebAssembly 学习笔记

WASM 概述 wasm最初是为了在浏览器获得接近原生的性能体验。 支持将其他语言实现的程序编译到wasm字节码,引入到浏览器由JS交互调用。 后又有了脱离JS的wasm运行时,可以直接运行wasm。 从而促成了wasm跨平台分发的能力。 但由于运行时的安全沙箱限制&a…...

二、github基础

Github基础 备用github.com网站一、用户界面-Overview(概览)1用户信息2 导航栏3 热门仓库4 贡献设置5贡献活动6搜索和筛选7自定义收藏8贡献统计9最近活动10其他链接 二、用户界面-Repositories(仓库)1 libusb_stm322 savedata3 Fi…...

「下载」智慧文旅运营综合平台解决方案:整体架构,核心功能设计

智慧文旅运营综合平台,旨在通过集成大数据、云计算、物联网、人工智能等先进技术,为景区、旅游企业及相关管理机构提供一站式的智慧化运营服务。 智慧文旅运营综合平台不仅能够提升游客的游览体验,还能帮助景区管理者实现资源的优化配置和业务…...

《探寻真正开源的大模型:开启AI创新新纪元》

《探寻真正开源的大模型:开启AI创新新纪元》 一、开源大模型崛起:AI 发展的新曙光二、开源大模型的 “庐山真面目”三、明星开源大模型闪耀登场(一)LLaMA 3:实力强劲的开源先锋(二)Phi-3&#x…...

麒麟信安云在长沙某银行的应用入选“云建设与应用领航计划(2024)”,打造湖湘金融云化升级优质范本

12月26日,2024云计算产业和标准应用大会在北京成功召开。大会汇集政产学研用各方专家学者,共同探讨云计算产业发展方向和未来机遇,展示云计算标准化工作重要成果。 会上,云建设与应用领航计划(2024)建云用…...

C#如何操作数据库

C#如何操作数据库 前言1、查询操作2、增删改操作3、需要返回id主键的sql语句执行 前言 本文主要交代如何通过引用 using MySql.Data.MySqlClient;来操作数据库 需要导入.dll文件 例如:在本地Mysql下载目录下->Connecter NET 8.0->Assemblies->net5.0->…...

c++领域展开第八幕——类和对象(下篇 初始化列表、类型转换、static成员)超详细!!!!

文章目录 前言一、初始化列表二、类型转换三、static成员总结 前言 上篇博客我们实现了一个简单的日期类,基本的类和对象是清楚了 今天我们再来学习后面的一些类和对象的语法,慢慢的完善所学的东西 fellow me 一、初始化列表 • 之前我们实现构造函数时…...

termux-boot安卓开机自动启动应用

termux安装 github 蓝奏云 v119.1 termux-boot安装 github 蓝奏云 v0.8.1 安装 给权限运行加锁后台 am启动应用命令 am start -n 包名/启动项获取包名和启动入口(图中app为爱玩机工具箱) 例 简黑时钟蓝奏云 包名com.hm.jhclock 桌面启动项com.hm.jh…...

Echart实现3D饼图示例

在可视化项目中,很多地方会遇见图表;echart是最常见的;这个示例就是用Echart, echart-gl实现3D饼图效果,复制即可用 //需要安装,再引用依赖import * as echarts from "echarts"; import echar…...

【JAVA】神经网络的基本结构和前向传播算法

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默, 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把…...

设计模式-抽象工厂模式

在设计模式中,抽象工厂模式(Abstract Factory Pattern)是一个非常常见且重要的模式,它属于创建型模式,用于提供创建一组相关或相互依赖对象的接口,而无需指定具体类。它的核心思想是将“创建对象”这一功能…...

webpack

前言 在现代前端开发的浪潮中,Webpack 已经成为一个不可或缺的构建工具。它不仅能够帮助我们打包 JavaScript 代码,还能够处理各种资源(如 CSS、图片、字体等),并提供一系列优化手段,极大地提升开发效率和…...

BLIP论文笔记

论文地址 BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation 论文思想 其实Clip就相当于只用了ITC...

Java List 集合详解:基础用法、常见实现类与高频面试题解析

正文 在 Java 集合框架中,List 是一个非常重要的接口,广泛用于存储有序的元素集合。本文将带你深入了解 List 接口的基本用法、常见实现类及其扩展,同时通过实际代码示例帮助你快速掌握这些知识。 👉点击获取2024Java学习资料 1…...

HTML5 SSE

HTML5 SSE(Server-Sent Events,服务器发送事件)是一种允许服务器实时向浏览器推送数据的技术。它是HTML5规范的一部分,主要通过HTTP协议实现。SSE的主要特点包括: 单向通信:与WebSocket不同,SSE…...

SpringBoot篇(监控)

目录 学习前言 一、什么是监控? 二、监控的意义 1. 简介 2. 总结 3. 思考 三、可视化监控平台 1. 简介 2. 实操 2.1. 服务端开发 2.2. 客户端开发 配置多个客户端 2.3. 总结 2.4. 思考 四、监控原理 1. 简介 2. 总结 五、自定义监控指标 1. 简介…...

python23-常用的第三方库01:request模块-爬虫

requests 模块是 Python 中的一个第三方库,用于发送 HTTP 请求。 它提供了一个简单且直观的 API,使得发送网络请求和解析响应变得非常容易。requests 模块支持各种 HTTP 方法,如 GET、POST、PUT、DELETE 等,并且具有处理 cookies…...

【pytorch】现代卷积神经网络

文章目录 1 AlexNet2 VGG3 NiN4 GoogLeNet5 批量规范化batch normalization6 ResNet6.1 残差块6.2 resnet 7 DenseNet7.1 稠密块体7.2 过渡层7.3 DenseNet模型 1 AlexNet AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。 AlexNet使用ReLU而不…...

Excel 身份证号计算年龄

1. 设置身份证号列格式 复制身份证列值到记事本或其他地方重新设置身份证号列单元格格式为“文本”将复制出去的身份证号重新复制粘贴回来 2. 年龄列单元格中添加公式 DATEDIF(DATE(LEFT(MID(A2, 7, 8), 4), MID(MID(A2, 7, 8), 5, 2), RIGHT(MID(A2, 7, 8), 2)), TODAY(), …...

【ArcGIS Pro】完整的nc文件整理表格模型构建流程及工具练习数据分享

学术科研啥的要用到很多数据,nc文件融合了时间空间数据是科研重要文件。之前分享过怎样将nc文件处理成栅格后整理成表格。小编的读者还是有跑不通整个流程的,再来做一篇总结篇,也分享下练习数据跟工具,如果还是弄不了的&#xff0…...

WebRTC的线程模型

WebRTC中的线程类: Thread类: (1)Thread类中的数据: class Thread {// 消息队列:MessageList messages_; // 消息队列,所有需要线程处理的消息,都要先入队PriorityQueue delayed_m…...