Redis详解:原理和机制( 二 )


注册事件和删除事件就是对 epoll_ctl 的封装,根据操作不同选择不同的参数
阻塞监听是对 epoll_wait 的封装,在返回后将激活的事件保存在事件驱动中
Reactor 设计模式:事件驱动循环流程
Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)
当 main 函数初始化工作完成后,就需要进行事件驱动循环,而在循环中,会调用 IO 复用函数进行监听
在初始化完成后,main 函数调用了 aeMain 函数,传入的参数就是服务器的事件驱动
Redis 对于时间事件是采用链表的形式记录的,这导致每次寻找最早超时的那个事件都需要遍历整个链表,容易造成性能瓶颈 。而 libevent 是采用最小堆记录时间事件,寻找最早超时事件只需要 O(1) 的复杂度

Redis详解:原理和机制

文章插图
如上图,IO多路复用模型使用了Reactor设计模式实现了这一机制 。
通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理 。
用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select/epoll函数检查socket状态 。当有socket被激活时,则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作 。由于select/epoll函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型 。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket 。一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞 。
redis线程模型:
如图所示:
Redis详解:原理和机制

文章插图
简单来说,就是 。我们的redis-client在操作的时候,会产生具有不同事件类型的socket 。在服务端,有一段I/0多路复用程序,将其置入队列之中 。然后,IO事件分派器,依次去队列中取,转发到不同的事件处理器中 。
3、数据结构说明
http://redis.io/topics/data-types
2.1 Key
1、key越短越好:Redis是个内存数据库,Key键越短你需要的空间就越少,因此key不能太长,比如1024字节 。
举个例子:在一个32位的Redis服务器上,如果储存一百万个键,每个值的长度是32-character,那么在使用6-character长度键名时,将会消耗大约96MB的空间,但是如果使用12-character长度的键名时,空间消耗则会提升至111MB左右 。随着键的增多,15%的额外开销将产生重大的影响 。
2、key命名要表达清楚意思 。建议用”:”分隔域划分键名,用”.”作为单词间的连接,如”comment:1234:reply.to” 。
2.2 String
String是最简单的类型,一个key对应一个value,string类型是二进制安全的 。Redis的string可以包含任何数据,比如jpg图片或者序列化的对象(php中对象序列化函数serialize)
内部实现,其本质是一个byte数组,字符串的大小被限制在512M以内
structsdshdr{
longlen; //buf数组的长度
longfree; //buf数组中剩余可用字节数
charbuf[]; //存储实际字符串内容
}
所有常用命令的复杂度都是O(1),普通的Get/Set方法,可以用来做Cache,存Session,为了简化架构甚至可以替换掉Memcached 。
Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用来做计数器,做自增序列 。key不存在时会创建并贴心的设原值为0 。IncrByFloat专门针对float,没有对应的decrByFloat版本?用负数啊 。
SetNx, 仅当key不存在时才Set 。可以用来选举Master或做分布式锁:所有Client不断尝试使用SetNx master myName抢注Master,成功的那位不断使用Expire刷新它的过期时间 。如果Master倒掉了key就会失效,剩下的节点又会发生新一轮抢夺 。
2.3 Hash
Key-HashMap结构,相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性 。这样有些属性超长就让它一边呆着不动,另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突 。
2.4 List
List是一个双向链表,支持双向的Pop/Push,江湖规矩一般从左端Push,右端Pop——LPush/RPop,而且还有Blocking的版本BLPop/BRPop,客户端可以阻塞在那直到有消息到来,所有操作都是O(1)的好孩子,可以当Message Queue来用 。当多个Client并发阻塞等待,有消息入列时谁先被阻塞谁先被服务 。任务队列系统Resque是其典型应用 。


推荐阅读