1:Redis单线程详解
Redis 基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型 ;(Netty 的线程模型也基于 Reactor 模式)
这套事件处理模型对应的是 Redis 中的文件事件处理器(file event handler)。
由于文件事件处理器(file event handler)是单线程方式运行的,所以我们一般都说 Redis 是单线程模型。
既然是单线程,那怎么监听大量的客户端连接呢?
Redis 通过IO 多路复用程序(multiplexing) 来监听来自客户端的大量连接(或者说是监听多个 socket);
并根据 套接字目前执行的任务来为套接字关联不同的事件处理器。
当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,
与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,
降低了资源的消耗(和 NIO 中的 Selector 组件很像)。
文件事件处理器(file event handler)主要是包含 4 个部分:
1:多个 socket(客户端连接)
2:IO 多路复用程序(支持多个客户端连接的关键)
3:文件事件分派器(将 socket 关联到相应的事件处理器)
4:事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
文件事件处理器.png
2:Redis的多线程使用历程?
Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。
Redis4.0版本更新.png
Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,
使用这些命令就会使用主处理之外的其他线程来“异步处理”。
为什么在6.0之前不用多线程
1:单线程编程容易并且更容易维护;
2:Redis 的性能瓶颈不再 CPU ,主要在内存和网络;
3:多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
3:Redis6.0 之后为何引入了多线程?
Redis6.0 引入多线程:
提高网络 IO 读写性能(Redis 中的一个性能瓶颈在于内存和网络)
Redis6.0 引入了多线程,但只是在网络数据的读写这类耗时操作上使用了, 执行命令仍然是单线程顺序执行。
因此,不需要担心线程安全问题。
Redis6.0 的多线程默认是禁用的,只使用主线程。
如需开启需要修改 redis 配置文件
redis.conf :
io-threads-do-reads yes
开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis 配置文件
#官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程
redis.conf :
io-threads 4
4:Redis 给缓存数据设置过期时间有啥用?
1:有助于缓解内存的消耗
因为内存是有限的,如果缓存中的所有数据都是一直保存的话,很容易Out of memory;
2:我们的业务场景需要某个数据只在某一时间段内存在:
比如我们的短信验证码可能只在 1 分钟内有效;
比如用户登录的 token 可能只在 1 天内有效;
5:Redis是如何判断数据是否过期的呢?
Redis 通过一个叫做过期字典(可以看作是hash表)来保存数据过期的时间。
过期字典的键指向Redis数据库中的某个key(键),过期字典的值是一个long long类型的整数;
这个整数保存了key所指向的数据库键的过期时间(毫秒精度的UNIX时间戳)。
过期字典.png
//redisDb结构:
typedef struct redisDb {
...
dict *dict; //数据库键空间,保存着数据库中所有键值对
dict *expires // 过期字典,保存着键的过期时间
...
} redisDb;
6:过期的数据的删除策略
假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?
常用的过期数据的删除策略就两个:
惰性删除 :
只会在取出key的时候才对数据进行过期检查。
这样对CPU最友好,但是可能会造成太多过期 key 没有被删除。
定期删除 :
每隔一段时间抽取一批 key 执行删除过期key操作。
Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响。
定期删除对内存更加友好,惰性删除对CPU更加友好。
两者各有千秋,所以Redis 采用的是 定期删除+惰性/懒汉式删除 。
但是,仅仅通过给 key 设置过期时间还是有问题的。
因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。
这样就导致大量过期 key 堆积在内存里,然后就Out of memory了。(需要使用内存淘汰机制)
7:什么是Redis 内存淘汰机制
MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,
如何保证 Redis 中的数据都是热点数据?
Redis 提供 6 种数据淘汰策略:
allkeys-lru(least recently used):
当内存不足以容纳新写入数据时,在键空间中,
移除最近最少使用的 key(这个是最常用的)
volatile-lru(least recently used):
从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:
从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:
从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-random:
从数据集(server.db[i].dict)中任意选择数据淘汰
no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。(最少使用)
4.0后版本后新增策略
allkeys-lfu(least frequently used):
当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
volatile-lfu(least frequently used):
从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰