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

相关推荐
Villiam_AY7 分钟前
Redis 缓存机制详解:原理、问题与最佳实践
开发语言·redis·后端
飛_40 分钟前
解决VSCode无法加载Json架构问题
java·服务器·前端
柊二三2 小时前
XML的简略知识点
xml·数据库·oracle
木棉软糖3 小时前
一个MySQL的数据表最多能够存多少的数据?
java
程序视点4 小时前
Java BigDecimal详解:小数精确计算、使用方法与常见问题解决方案
java·后端
GEM的左耳返4 小时前
Java面试全攻略:Spring生态与微服务架构实战
spring boot·redis·spring cloud·微服务·kafka·java面试
愿你天黑有灯下雨有伞4 小时前
Spring Boot SSE实战:SseEmitter实现多客户端事件广播与心跳保活
java·spring boot·spring
每天敲200行代码4 小时前
MySQL 事务管理
数据库·mysql·事务
巴里巴气4 小时前
MongoDB索引及其原理
数据库·mongodb
Java初学者小白4 小时前
秋招Day20 - 微服务
java