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 -- 查看有哪些函数

相关推荐
Goldn.3 小时前
Java核心技术栈全景解析:从Web开发到AI融合
java· spring boot· 微服务· ai· jvm· maven· hibernate
极限实验室3 小时前
APM(一):Skywalking 与 Easyearch 集成
数据库·云原生
饕餮争锋3 小时前
SQL条件中WHERE 1=1 的功能
数据库·sql
玄斎4 小时前
MySQL 单表操作通关指南:建库 / 建表 / 插入 / 增删改查
运维·服务器·数据库·学习·程序人生·mysql·oracle
李慕婉学姐4 小时前
【开题答辩过程】以《基于Android的出租车运行监测系统设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·后端·vue
m0_740043734 小时前
SpringBoot05-配置文件-热加载/日志框架slf4j/接口文档工具Swagger/Knife4j
java·spring boot·后端·log4j
编织幻境的妖4 小时前
SQL查询连续登录用户方法详解
java·数据库·sql
编程小Y5 小时前
MySQL 与 MCP 集成全解析(核心原理 + 实战步骤 + 应用场景)
数据库·mysql·adb
未若君雅裁5 小时前
JVM面试篇总结
java·jvm·面试
kk哥88995 小时前
C++ 对象 核心介绍
java·jvm·c++