# 使用 Redis 做为进程间通信解决方案
Redis 常用于缓存、消息队列、实时数据分析等高性能、低延迟的应用场景。
它有着使用简单、体积小、性能强、多平台兼容的特点。作为一个内存数据存储,Redis 支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,能够满足不同场景下的数据存储需求。Redis 提供了高效的读写性能,能够在高并发环境下处理大量的请求。
Redis 的设计强调简洁性和高效性,支持持久化机制,可以将数据持久化到磁盘,确保系统重启时数据不丢失。此外,Redis 支持高可用性部署和分布式集群,能够在大规模系统中提供一致性和可靠性。
Redis 是跨平台的,支持 Linux、macOS、Windows 等操作系统,并且有丰富的客户端库,几乎支持所有主流编程语言,方便开发者进行集成。由于其高性能和低资源消耗,Redis 已成为全球范围内最受欢迎的开源数据存储系统之一。
# 一、Windows 环境下 Redis 的安装
Windows 版本 Redis GitHub:https://github.com/tporadowski/redis (opens new window)
下载 msi
安装程序,或者 zip
压缩包,mis
安装程序会自动配置好环境变量,zip
需要手动配置,如果不配置环境变量那么后续的操作指令必须进入 zip
解压后的文件夹操作才行。
在命令行环境直接运行 Redis
redis-server redis.windows.conf
将 Redis 部署为 Windows 服务
redis-server --service-install redis.windows.conf
启动 Redis 服务
redis-server --service-start
停止 Redis 服务
redis-server --service-stop
卸载 Redis 服务
redis-server --service-uninstall
# 二、Redis 客户端基础操作
Redis 常用操作可以包含为以下几类:
- 键操作
- SET:设置键的值;
- GET:获取键的值;
- DEL:删除键;
- EXISTS:检查键是否存在;
- KEYS:查找符合模式的键;
- TTL:查看键的剩余过期时间;
- EXPIRE:设置键的过期时间;
- 字符串操作
- INCR / DECR:对键的数值进行递增或递减;
- APPEND:将值追加到字符串后面;
- 哈希操作
- HSET:设置哈希表中的字段值;
- HGET:获取哈希表中的字段值;
- HGETALL:获取哈希表中的所有字段和值;
- HDEL:删除哈希表中的字段;
- 列表操作
- LPUSH / RPUSH:将值插入到列表的头部或尾部;
- LPOP / RPOP:移除并返回列表的第一个或最后一个元素;
- LRANGE:获取列表中指定范围的元素;
- 集合操作
- SADD:将元素加入集合;
- SMEMBERS:获取集合中的所有元素;
- SREM:从集合中移除元素;
- SISMEMBER:判断元素是否在集合中;
- 有序集合操作
- ZADD:将元素及其分数加入有序集合;
- ZRANGE:获取有序集合中指定范围的元素;
- ZREM:移除有序集合中的元素;
- 事务操作
- MULTI:开启事务;
- EXEC:执行事务中的所有命令;
- DISCARD:放弃事务中的命令;
- 持久化操作
- SAVE:手动保存数据(阻塞操作);
- BGSAVE:后台保存数据;
- SLAVEOF:设置主从复制;
- 服务器管理
- INFO:查看 Redis 服务器的运行信息。
- SHUTDOWN:关闭 Redis 服务。
- CONFIG:查看或设置 Redis 配置项。
以上操作覆盖了 Redis 的核心功能,涉及数据存储、数据类型操作、事务管理、持久化以及服务器管理等多个方面。
在 Redis 安装目录下,有一个 redis-cli.exe 文件,即为 Redis 客户端。以下是客户端操作实例:
# 连接服务 redis-cli -h <hostname> -p <port>
redis-cli.exe -h 127.0.0.1 -p 6379
# 连通性测试
127.0.0.1:6379> ping
PONG
# 选择对应的数据库 默认配置16个 select db
127.0.0.1:6379> select 1
OK
# 设置一个键值对 set key value
127.0.0.1:6379> set name "Alice"
OK
# 获取指定键的值 get key
127.0.0.1:6379> get name
"Alice"
# 删除键 del key
127.0.0.1:6379> del name
(integer) 1
# 整数键值自增一 incr key
127.0.0.1:6379> incr count
(integer) 2
# 整数键值自减一 decr key
127.0.0.1:6379> decr count
(integer) 1
# 查看 Redis 中 key 的数量
127.0.0.1:6379> dbsize
(integer) 0
# 查看 Redis 服务器的运行信息
127.0.0.1:6379> info
# 删除当前数据库中的所有键
127.0.0.1:6379> flushdb
# 关闭 Redis 服务器 shutdown
127.0.0.1:6379> shutdown
# 退出 Redis 客户端
127.0.0.1:6379> exit
# 三、Redis 持久化功能关闭
要设置 Redis 的持久化策略或关闭持久化功能,可以通过修改 Redis 的配置文件(通常是 redis.conf
)或在运行时使用命令进行配置。在常规单机系统中,并不需要考虑数据持久化,关闭持久化可以减少系统资源的占用。
# 1. 关闭 RDB(快照持久化)
# 在 redis.windows.conf 中找到 save 相关内容,按以下方式修改
# 注释以下三行
# save 900 1
# save 300 10
# save 60 10000
# 打开该行注释,或手工添加
save ""
# 2. 关闭 AOF(追加日志持久化)
# 在 redis.windows.conf 中找到 appendonly 将值改为 no
appendonly yes
# 四、Redis 多实例启动
虽然 Redis 是单线程的,但这并不意味着它就是性能瓶颈。单线程的优势在于避免了多线程编程中常见的锁竞争问题(如线程同步和上下文切换的开销)。在多线程程序中,多个线程访问共享资源时需要使用锁,而锁会增加额外的开销。Redis 采用了事件循环模型,通过单线程顺序地处理每个请求,从而避免了这些问题,提升了性能。但是如果在多个不相关的业务系统中,用同一个 Redis 实例,并不是最好的选择,又或者如果单个 Redis 实例无法满足性能需求(例如,需要更多的 CPU 核心或内存),那么可以启动多个 Redis 实例,来达到业务隔离,或者性能扩展的目的。
在一个 PC 上启动多个 Redis 实例,可以通过以下几个步骤来实现。
# 1. 准备多个配置文件
复制 Redis 配置文件:
复制 Redis 的默认配置文件(redis.windows.conf
)作为每个实例的配置文件。
redis.windows.01.conf
redis.windows.02.conf
redis.windows.03.conf
……
修改每个配置文件
# 每个文件单独修改端口号
port 6379
port 6380
port 6381
……
# 每个文件单独修改存储路径
# 注意这地方必须是文件夹,如果文件夹不存在,需要手工创建,否则报错
dir ./instance/instance-01
dir ./instance/instance-02
dir ./instance/instance-03
……
# 每个文件单独修改日志路径
logfile ./logs/instance-01.log
logfile ./logs/instance-02.log
logfile ./logs/instance-03.log
……
# 2. 启动多个 Reids 实例
redis-server redis.windows.01.conf
redis-server redis.windows.02.conf
redis-server redis.windows.03.conf
# 3. Windows 系统部署多个 Redis 服务
# 多实例操作与单实例操作,只多了一个 --service-name 参数来指定 Reis 服务名称,其他没区别
# 安装服务
# 由于配置文件指定了日志输出文件,所以此处命令行并不会有 Reids 日志出现,需要在日志文件中查看执行状态
.\redis-server.exe --service-install redis.windows.01.conf --service-name redis-01
.\redis-server.exe --service-install redis.windows.02.conf --service-name redis-02
.\redis-server.exe --service-install redis.windows.03.conf --service-name redis-03
……
# 启动服务
.\redis-server.exe --service--start --service-name redis-01
.\redis-server.exe --service--start --service-name redis-02
.\redis-server.exe --service--start --service-name redis-03
……
# 停止服务
.\redis-server.exe --service--stop --service-name redis-01
.\redis-server.exe --service--stop --service-name redis-02
.\redis-server.exe --service--stop --service-name redis-03
……
# 卸载服务
.\redis-server.exe --service--uninstall --service-name redis-01
.\redis-server.exe --service--uninstall --service-name redis-02
.\redis-server.exe --service--uninstall --service-name redis-03
……
# 五、在 Python 中使用 Redis
python 自身并没有集成 redis 库,所以首先需要单独安装 redis 库。
pip install redis
# 1. 使用 Redis 进行视频流传输
Redis 本身是一个轻量级的内存数据库,适用于快速的键值存储和轻量级数据传输,因此对资源的占用相对较低,同时,Redis 是单线程模型,但由于其事件驱动的架构,可以高效处理大量请求,非常适合需要高并发和低延迟的进程间通信。对于视频帧的传输来说非常合适。
# Python 跨进程的视频传输例程
Server 端 (写视频)
import redis
import cv2
import time
# 初始化 Redis 连接
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 打开视频文件
cap = cv2.VideoCapture('./test.mp4')
# 检查视频是否成功打开
if not cap.isOpened():
print("Error: Could not open video.")
exit()
# 获取视频帧率(fps)
fps = cap.get(cv2.CAP_PROP_FPS)
while True:
ret, frame = cap.read()
if not ret:
break
# 将帧的原始像素数据直接转换为二进制
frame_bytes = frame.tobytes()
# 将二进制数据写入 Redis,覆盖原来的帧
r.set('video', frame_bytes)
# 控制帧的写入间隔,确保与视频帧率一致
time.sleep(1 / fps)
# 释放视频捕捉对象
cap.release()
Client 端(读队列)
import cv2
import redis
import time
import numpy as np
# 初始化 Redis 连接
r = redis.StrictRedis(host='localhost', port=6379, db=0)
while True:
# 从 Redis 中读取最新的帧数据
frame_bytes = r.get('video_1')
# 如果没有读取到数据,说明还没有视频帧写入Redis
if not frame_bytes:
print("Waiting for video frame to be available in Redis...", end='\r')
time.sleep(0.01) # 等待 0.01 秒后再次尝试
continue
# 将 Redis 二进制数据转换为视频帧
frame = np.frombuffer(frame_bytes, dtype=np.uint8).reshape((480, 852, 3)) # 注意分辨率
cv2.imshow('Video Playback', frame)
# 等待 0.01 秒,如果用户按下 'q' 键则退出
if cv2.waitKey(10) & 0xFF == ord('q'):
break
# 释放 OpenCV 窗口
cv2.destroyAllWindows()
# 2. 使用 Redis 队列进行控制流传输
Redis 提供了队列机制,可以用来实现先进先出(FIFO)的队列操作,队列机制保证了控制流数据的先后顺序,Redis 列表会持久化到磁盘(如果你启用了持久化配置),因此队列数据不会丢失,除非显式删除或 Redis 数据库被清空,非常适合控制流的通信。
# 基本操作
对于 先进先出(FIFO)队列,通常使用 RPUSH
来添加元素到队列尾部,然后使用 LPOP
从队列头部获取并删除元素。
LPUSH
:将元素添加到列表的左端(队列的头部);RPUSH
:将元素添加到列表的右端(队列的尾部);LPOP
:从列表的左端移除并返回元素(队列的头部);RPOP
:从列表的右端移除并返回元素(队列的尾部);
# 阻塞队列
可以使用 Redis 提供的阻塞队列命令 BLPOP
和 BRPOP
阻塞等待新数据到来。
BLPOP
:在队列为空时,阻塞直到有元素可以被LPOP
;BRPOP
:在队列为空时,阻塞直到有元素可以被RPOP
;
# Python 跨进程的队列读写例程
A 程序(写队列)
import redis
# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 向队列 'my_queue' 添加元素
r.rpush('my_queue', 'item1')
r.rpush('my_queue', 'item2')
r.rpush('my_queue', 'item3')
print("Items added to the queue.")
B 程序(读取队列)
import redis
# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 从队列 'my_queue' 读取并移除元素
while True:
item = r.lpop('my_queue')
if item:
print(f"Item retrieved from queue: {item.decode()}")
else:
print("Queue is empty.")
break
B 程序(阻塞模式读取队列)
import redis
# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 使用 BLPOP 来实现阻塞式队列读取
while True:
item = r.blpop('my_queue', timeout=0) # timeout=0 表示一直阻塞直到有元素
if item:
print(f"Item retrieved from queue: {item[1].decode()}")
else:
print("Queue is empty.")