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

相关推荐
山岚的运维笔记8 小时前
SQL Server笔记 -- 第20章:TRY/CATCH
java·数据库·笔记·sql·microsoft·sqlserver
Gain_chance8 小时前
33-学习笔记尚硅谷数仓搭建-DWS层交易域用户粒度订单表分析及设计代码
数据库·数据仓库·hive·笔记·学习·datagrip
南极企鹅8 小时前
springBoot项目有几个端口
java·spring boot·后端
清风拂山岗 明月照大江9 小时前
Redis笔记汇总
java·redis·缓存
未来之窗软件服务9 小时前
计算机等级考试—高频英语词汇—东方仙盟练气期
数据库·计算机软考·东方仙盟
lekami_兰9 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
xiaoxue..9 小时前
合并两个升序链表 与 合并k个升序链表
java·javascript·数据结构·链表·面试
JQLvopkk9 小时前
C# 轻量级工业温湿度监控系统(含数据库与源码)
开发语言·数据库·c#
忧郁的Mr.Li9 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
yq1982043011569 小时前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端