马 冰 姜学军 史海洋
(沈阳理工大学,辽宁 沈阳110159)
Redis是一个高性能的key-value数据库。redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便[1]。它有以下几个特点:
Redis使用标准C编写实现,而且数据加载到内存上,所以读写速度非常快。官方提供的数据表明,普通的Linux机器上,Redis的读写速度分别达到81000/s和110000/s。
Redis使用单线程的IO复用模型。自己封装了一个简单的AeEvent事件处理框架,主要实现了epoll,kqueue和select,对于单纯只有IO操作来说,单线程可以将速度优势发挥到最大。
由于所有数据保存于内存中,所以对数据的更新将异步地保存到磁盘上。
相比于其他内存型数据库,Redis不只支持字符串类型,而是支持多种数据结构。目前支持5种。分别为string(字符串)、hash(hash表)、list(双向链表)、set(无序集合)、zset(有序集合)。
Redis对不同数据类型操作时自动的,因此设置或增加key,从一个集合中增加或删除一个元素都能安全的操作。
Redis支持多种语言,诸如Ruby,Python,PHP,ErLang,Perl,Lua,Java,Scala等。
Redis支持简单而快速的主从复制。官方提供数据,Slave在21秒内即完成了Amazon网站10Gkey Set的复制。
目前只支持PHP,Ruby,Scala语言的Sharding功能。
1)小数据量,高速读写访问。
2)大数据量,有明显热点数据。
3)大数据量,无热点数据。
4)所有数据in-memory。
(代码都是用java语言完成,数据库操作采用MyBatis框架,Redis操作使用Spring提供的方法)
表1 执行五次来进行比较两者的时间(毫秒)
可见,redis的存取性能要极大程度优于数据库,所以在应用中合适的使用redis,能极大提升产品性能。
Redis存取速度快的原因分析如下:
4.3.1 纯内存操作
Redis所有数据存储于内存中,内存中的操作速度比磁盘快的多。
4.3.2 Redis的存取底层使用异步非阻塞IO
系统I/O可分为阻塞型,非阻塞同步型以及非阻塞异步型。
阻塞型I/O意味着控制权只到调用操作结束了才会回到调用者手里.结果调用者被阻塞了,这段时间了做不了任何其它事情。
非阻塞同步是会立即返回控制权给调用者的。调用者不需要等等,它从调用的函数获取两种结果:要么此次调用成功进行了;要么系统返回错误标识告诉调用者当前资源不可用,你再等等或者再试度看吧。
在非阻塞异步调用中,稍有不同。调用函数在立即返回时,还告诉调用者,这次请求已经开始了。系统会使用另外的资源或者线程来完成这次调用操作,并在完成的时候知会调用者(比如通过回调函数)[2]。
In a non-blocking asynchronous call,the calling function returns control to the caller immediately,reporting that the requested action was started.The calling system will execute the caller’s request using additional system resources/threads and will notify the caller(by callback for example),when the result is ready for processing。[3]
在以上三种IO形式中,非阻塞异步是性能最高、伸缩性最好的。
4.3.3 采用Epoll接口实现IO复用
与epoll模式相比较,有select模式,select模式是采用轮询的方式,来检测所有socket的状态,当有很大的socket集时,即使有小部分是活跃的,内核也需要把整个集合轮询才能知道所有socket的状态变化,而epoll模式是在创建时即对需要关注的socket注册事件,当某一个监听的socket有状态变化,就将其保存到一个内部数组中,当应用层需要检测时直接返回该数组,不需要对所有socket逐一检查,所以epoll模式的性能优于select模式。
需要设置数据的一定存活时间。例如找回密码功能,用户需要填写邮箱,然后发送一个连接到用户邮箱中,该连接是有时间限制,半小时后即失效。此时就可以利用redis存储数据可以设置有效时间的功能。并且不需要存储于数据库,读取redis的效率更高。
读取时直接在redis中查询key为"redis.global.forgetPass",如果有值则说明未超时,为空则说明连接失效。
一些全局的系统设置,基本上都是在第一次启动应用的时候设置,以后就不会再修改的数据,可以存于redis中,设置不自动失效。
所有业务数据,可以通过数据库主键ID当成key或者一些层级关系生成redis存储的key,将整个数据对象存储于redis,极大的减少数据库查询次数,提高应用性能。
例如:数据的层级为 代理商→租户→用户 则用户可以在redis中存储的数据为
Key:redis.user.partnerId.customerId.userId
Value:user的数据对象
e.g.redis.user.1.33.21 redis.user.3.42.13。
[1]百度百科.Redis[EB/OL].http://baike.baidu.com/view/4595959.htm,2014.
[2]veryDemo.两种高性能I/O设计模式(Reactor/Proactor)的比较[Z].2005.
[3]Artima developer.Comparing Two High-Performance I/ODesign Patterns[Z].2005.