官网地址: 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 --执行事务
OK
(integer) 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"
- 核心作用:优化RTT(round-trip time)
- 无法保证Redis原子性:与复合指令和Redis事物相比,Pipeline只是一次性的将所有的指令发送到服务端, 这些指令还是在服务端进行排队,无法保证这些指令之间不会插入其他客户端的请求(虽然概率比较低)。
- 不建议执行复杂操作:pipeline在执行过程中,会阻客户端。在pipeline中不建议拼装过多的指令。因为指令过多,会使客户端阻太长,同时服务端需要回复这个很繁忙的客户端,占用很多内存。
- 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
"key1"
"key2"
"first"
"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 -- 查看有哪些函数