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 {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 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', KEYS[1])

local a = tonumber(initcount)

local b = tonumber(ARGV[1])

if a >= b

then redis.call('set', KEYS[1], a)

return 1

end

redis.call('set',KEYS[1], 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 -- 查看有哪些函数

相关推荐
掘金-我是哪吒14 分钟前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构
亲爱的非洲野猪40 分钟前
Kafka消息积压的多维度解决方案:超越简单扩容的完整策略
java·分布式·中间件·kafka
wfsm43 分钟前
spring事件使用
java·后端·spring
老纪的技术唠嗑局1 小时前
OceanBase PoC 经验总结(二)—— AP 业务
数据库
微风粼粼1 小时前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
缘来是庄1 小时前
设计模式之中介者模式
java·设计模式·中介者模式
阿里云大数据AI技术1 小时前
OpenSearch 视频 RAG 实践
数据库·人工智能·llm
rebel2 小时前
若依框架整合 CXF 实现 WebService 改造流程(后端)
java·后端
代码的余温3 小时前
5种高效解决Maven依赖冲突的方法
java·maven
慕y2743 小时前
Java学习第十六部分——JUnit框架
java·开发语言·学习