Redis线程模型

官网地址: https://redis.io/

Redis目前支持一些复杂的数据结构,可以应用到多种业务场景;Redis数据保存在内存,但是持久化到磁盘,意味着Redis的可以保证数据安全,特定的场景也可以当做数据库来使用;

Redis单线程

Redis到底是单线程的还是多线程的?

Redis服务处理客户端连接的过程:

1、整体来说,Redis的整体线程模型可以简单解释为 客户端多线程,服务端单线程.

为了和多个客户端连接, Redis服务使用I/O多路复用的方式,使用一个线程维护与所有客户端的Socket链接。 将客户端并发的请求转换成串行的执行方式,这样Redis服务就可以用一个单独的线程完成客户端的请求。

redis.conf中可以配置客户端的最大连接数

maxClients 10000

2、严格来讲,Redis后端的线程模型和Redis的版本有关。

Redis4.X以前的版本,都是采用的1纯单线程。Redis5.x版本进行了一次大的核心代码重构。 到Redis6.x和7.x版本中,开始用一种全新的多线程机制来提升后台工作。尤其在现在的Redis7.x版本中,Redis后端的很多比较费时的操作,比如持久化RDB,AOF文件、unlink异步删除、集群数据同步等,都是由额外的线程执行的。例如,对于 FLUSHALL操作,就已经提供了异步的方式。

3、为什么Redis不使用多线程

Redis使用单线程就不能发挥多核CPU的优势,但是对现在的Redis而言,CPU不是Redis的性能瓶颈,反而是内存和网络。

Redis单线程的优势:减少了多线程环境下的上下文切换的开销,加锁的开销等。

Redis的原子性

Redis处理客户端的并发请求时,是将客户端的请求排队,然后串行执行。 如果A客户端的多个请求之间插入的其他客户端的请求,就无法保证A客户端请求响应的结果。

有什么方式可以控制Redis指令的原子性呢?

1. 复合指令

比如 MSET(HMSET)、GETSET、SETNX、SETEX。这些复合指令都能很好的保持子性。

2. Redis事务

127.0.0.1:6379> MULTI -- 表示开启事务

OK

127.0.0.1:6379(TX)> set k2 2

QUEUED

127.0.0.1:6379(TX)> incr k2

QUEUED

127.0.0.1:6379(TX)> get k2

QUEUED -- 这些指令只是进行了排队

127.0.0.1:6379(TX)> EXEC --执行事务

  1. OK

  2. (integer) 3

  3. "3"

127.0.0.1:6379> DISCARD -- 放弃事务

Redis事务可以通过Watch机制进一步保证在某个事务执行前,某一个key不被修改。

UNWATCH只有在开启WATCH的客户端才生效。

和Mysql的事务相比,Redis事务只能保证指令一起执行(中间不插入其他指令),不能保证全部成功全部失败的特性。

Redis事务执行过程:

当EXEC指令执行后,Redis会先将事务中的所有操作都先记录到AOF文件中,然后再执行具体的操作。这时有一种可能,Redis保存了AOF记录后,事务的操作在执行过程中,服务就出现了非正常宕机(服务崩溃了,或者执行进程被kill -9了)。这就会造成AOF中记录的操作,与数据不符合(数据不一致)。如果Redis发现这种情况,那么在下次服务启动时,就会出现错误,无法正常启动。这时,就要使用redis-check-aof工具修复AOF文件,将这些不完整的事务操作记录移除掉。这样下次服务就可以正常启动了。

3. Pipeline

使用实例:

-- 有个脚本command.txt

set count 1

incr count

incr count

incr count
-- 执行这个脚本中的指令

root@192-168-65-214 \~# cat command.txt | redis-cli -a 123qweasd --pipe

-- 查询执行的结果count的值

127.0.0.1:6379> get count

"4"

  1. 核心作用:优化RTT(round-trip time)
  2. 无法保证Redis原子性:与复合指令和Redis事物相比,Pipeline只是一次性的将所有的指令发送到服务端, 这些指令还是在服务端进行排队,无法保证这些指令之间不会插入其他客户端的请求(虽然概率比较低)。
  3. 不建议执行复杂操作:pipeline在执行过程中,会阻客户端。在pipeline中不建议拼装过多的指令。因为指令过多,会使客户端阻太长,同时服务端需要回复这个很繁忙的客户端,占用很多内存。
  4. pipeline机制适合做一些在非热点时段进行的数据调整任务。

4. Lua脚本

参考网站:https://wiki.luatos.com/ :直接在线调试lua语法

热点脚本可以缓存到服务器上。

Redis中有一个配置参数来控制Lua脚本的最长控制时间。默认5秒钟。

lua-time-limit 5000

示例1:

-- lua脚本中2个参数

127.0.0.1:6379> eval "return {KEYS1,KEYS2,ARGV1,ARGV2}" 2 key1 key2 first second

  1. "key1"

  2. "key2"

  3. "first"

  4. "second"

示例2:

-- 在lua脚本中,可以使用redis.call函数来调用Redis的命令

127.0.0.1:6379> set stock_1 1

OK

-- 调整 1号商品的库存。如果库存小于 10**,就设置为**10

127.0.0.1:6379> eval

"

local initcount = redis.call('get', KEYS1)

local a = tonumber(initcount)

local b = tonumber(ARGV1)

if a >= b

then redis.call('set', KEYS1, a)

return 1

end

redis.call('set',KEYS1, b)

return 0

"

1 "stock_1" 10 -- 1个变量

5. Redis Function

Redis Function允许将一些功能声明成一个统一的函数,提前加载到Redis服务端。客户端可以直接调用这些函数,而不需要再去开发函数的具体实现。

Function示例:

Lua 复制代码
#!lua name=mylib

local function my_hset(keys, args)

local hash = keys[1]
local time = redis.call('TIME')[1]
return redis.call('HSET',hash,'_last_modified_',time, unpack(args))
end
redis.register_function('my_hset', my_hset)

127.0.0.1:6379> FUNCTION LIST -- 查看有哪些函数

相关推荐
TDengine (老段)7 分钟前
TDengine 连接算子 — Inner/Outer/ASOF/Window Join 的实现与使用
大数据·数据库·物联网·哈希算法·时序数据库·tdengine·涛思数据
轻刀快马14 分钟前
跨越软硬件的共鸣(二):从 Cache 写策略看 Redis 与 DB 的一致性博弈
java·开发语言·redis·计算机组成原理
折哥的程序人生 · 物流技术专研14 分钟前
Java 23 种设计模式:从踩坑到精通 | 装饰器模式 —— 比继承更灵活的扩展方式,你用过吗?
java·装饰器模式·java面试·结构型模式·java设计模式·javaio·从踩坑到精通
lili001222 分钟前
2026 企业 AI 选型新范式:OpenRouter Fusion 证明多模型融合性价比远超单模型,企业该如何重构技术栈? - 微元算力(weytoken)
java·人工智能·python·重构·ai编程
Keano Reurink23 分钟前
搜索API与GSC数据对比:发现数据盲区
数据库·python·数据挖掘
shushangyun_24 分钟前
汽车服务行业B2B平台+AI解决方案哪家专业:2026年最新测评
java·运维·网络·数据库·人工智能·汽车
A.说学逗唱的Coke27 分钟前
【大模型专题】Spring AI Alibaba × Skill 整合实战:让 AI 真正“会干活
java·人工智能·spring
大黄说说39 分钟前
深入理解 Go 协程 Goroutine:并发编程的核心精髓
java·数据库·python
sulikey1 小时前
数据库系统概论4 - 更新与视图 期末速成课笔记
数据库·笔记·考试·期末速成·数据库系统概论
锋行天下1 小时前
数据库安全并发控制详解:乐观锁 vs 悲观锁 vs 原子操作
前端·数据库·后端