一、线程模型(单线程)
redis是基于reactor模式开发的网络事件处理器,这个处理器叫做文件事件处理器(File event Handle),这个文件事件处理器是单线程的,采用IO多路复用机制来同时监听所有的socket,根据所有的socket上对应的事件选择对应的事件处理器处理。
redis对客户端请求流程看图。单个客户端的连接及请求过程:
首先,客户端与redis进行交互是基于网络编程的,在redis进程里面有一个server socket服务端,当客户端发起连接请求时会去找redis服务端进行连接,如果客户端redis设置了密码校验会需要参数校验。当客户端连接到redis的server socket请求连接时,服务端会触发一个叫AE_READABLE事件,然后这个事件会交给IO多路复用程序将其压入队列并按其客户端号进行排队,当文件事件分派器拿到这个事件会去绑定一个连接应答处理器,同时开启一个响应客户端socket 01的一个socket并且将这个事件AE_READABLE绑定命令请求处理器。
当客户端socket01向redis发送set key value 请求时,redis绑定对应的socket监听到写事件触发AE_READABLE事件,通过IO多路复用程序压入队列,排队等文件事件分派器分配事件,然后发现该事件绑定了命令请求处理器就去找该处理器从socket里面读出keyvalue 在内存里面完成对 键值对的设置,并且对对事件AE_WRITABLE进行绑定。如果是为读操作,和前面的步骤一样只是一个是从内存里面将键值对设置进去,一个是从内存将数据读取出来。当客户端进行读的操作的时候,当客户端准备读取数据时,客户端socket对于的socket会产生一个AE_WRITABLE事件通过IO多路复用程序进行压入队列,交给事件分配器分配给命令回复处理器(之前绑定了),redis准备好客户端要响应的数据,通过命令回复处理器抛出本次操作的结果:ok,并且解除事件AE_WRITABLE关联,处理器将数据写入socket供客户端读取。
对于比较前几年流行的memcached区别及优缺点在于:
1)Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
2)集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是redis目前是原生支持cluster模式的,redis官方就是支持redis cluster集群模式的,比memcached来说要更好。
对基于单线程模式下,效率高的缘由:
1.走纯内存机制
2.基于NIO的IO多路复用机制
3.单线程反而避免了多线程下频繁的上下文切换
二、过期机制
前要:redis是基于内存的所以其高性能肯定没话说,但是内存是有限的,如果把大量的数据都保存在内存里面,一个是保存不了这么大量的数据,还会将数据丢失,本来只能装10G,强行输入20G,那不就没了一半。所以在redis里面是有一个过期机制的,我们也可以设置这些数据的过期时间。
1.定期删除和惰性删除
事件:假设我们有一批数据其中有一大批数据设置了过期时间,少量设置为常用数据,当一些数据过了过期时间,理应该消失在内存但是还是显示内存不足。
原因:基于redis的过期机制的定期删除,redis在每隔100ms就会去随机抽一些设置了过期时间的数据进行检查,如果抽取到的数据过期了那么这条数据就没了。注意,这个定期删除不是全面扫描整个redis数据,而是随机抽取设置了过期时间的数据,所以在每次定期删除的时候就可能会堆积大量未被扫描到的过期数据,这些过期数据仍然存在内存所以占内存。所以需要惰性删除,惰性删除就是指redis不直接去找这些过期的数据,而是当你去读取某个数据的时候如果发现这个数据过期了那么直接清除,然后不返回任何东西回去。
后果:这两个删除做下来还是会发现当大量的数据存储进来之后,如果出现大量的过期或无用数据没有被定期删除和惰性删除还是会占用大量的内存,这个时候就要走内存淘汰机制。
2.内存淘汰机制
1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。(这个一般不用吧!)
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的!)
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。(这个也一般没人用吧!)
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
ps:来一个简单的LRU代码(基于jdk数据结构实现的java版LRU淘汰机制)
public class LRUCache extends LinkedHashMap {
private final int CACHE_SIZE;
// 这里就是传递进来最多能缓存多少数据
public LRUCache(int cacheSize) {
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); // 这块就是设置一个hashmap的初始大小,同时最后一个true指的是让linkedhashmap按照访问顺序来进行排序,最近访问的放在头,最老访问的就在尾
CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > CACHE_SIZE; // 这个意思就是说当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据
}
}