目录
缓存分为进程内缓存和进程外缓存。
所谓进程内缓存就是在本地程序创建一个存储数据空间,相比进程外缓存由于没有网络开销效率更高,常见框架如:EhCache。但进程内缓存有一个致命的缺点,在分布式环境下无法保证多台机器缓存的一致性,需要额外写通知代码,即便是有通知也无法保证百分百的一致性,所以进程内缓存一般用于保存系统启动时及加载完成的数据并且在系统运行过程中不需要改变,例如系统配置参数等。在使用进程内缓存时一般用一个map存储数据,实现比较简单。
进程外缓存就是借助第三方工具存储数据常用的如redis、memcached。
redis
redis是一款使用C语言开发的开源的缓存管理工具,是一个key-value形式的存储系统,它的功能强大,支持多种数据类型:String、Hash、Set、ZSet、List。
redis除了可以用作缓存外由于它的单线程特性,天生可以保证线程安全它也常用来实现作分布式锁,它的List链表常用作队列来使用。另外redis是支持持久化的内存数据库,它在启动时会开启一个守护线程每隔一段时间会将内存中的数据实例化到磁盘,当redis意外重启时会从磁盘将上一次备份的数据刷到内存中,减少数据丢失带来的灾害。它提供两种方式进行持久化:快照和AOF。它的快照持久化方式支持配置,可以配置当key键修改达到的次数以及过多久来持久化一次。AOF及日志记录,它会记录每一次的执行命令,当redis重启时会先重新执行所有记录的命令。
redis是单进程单线程的内存数据库,它采用IO复用技术让单个线程可以高效处理多个请求,由于它的单进程单线程特性天生不存在线程安全问题,当然对于分布式的redis集群复合操作还是需要加锁的。
多路复用IO
古典的IO都是阻塞执行任务:当有新任务到达时会开启一个新的线程并阻塞处理,直到这个任务执行完成。这种执行方式对机器要求极高,每一个线程只能处理一个任务,在超大访问量场景会带来不小的挑战,比如tomcat默认只有500线程开启,超过这个阀值所有的新请求都会阻塞直至有任务执行完成。
多路复用IO就是一个IO管理多个请求,在redis中每一个网络请求都是一个文件描述,它会监听所有网络连接的文件描述符,当有文件事件产生时,文件处理器就会回调绑定文件处理器的事件处理器。其实redis在内部分为文件处理器与事件处理器等多个线程,事件处理器又分为命令回复、命令请求、连接应答处理器,每个处理器的执行互不影响。在进行复合操作时要注意,redis无法保证命令的执行顺序,当有需要使用复合命令时可以使用lua脚本实现。
memcached
memcached也是一款使用C语言开发的开源的缓存管理工具,memcached完全基于内存,不可以持久化数据。同样使用非阻塞IO。它的效率要略高于redis。在宕机时memcache中的数据会被完全抹除。memcached仅支持key-value形式数据。当memcached中的数据达到内存阀值时会将内存中不常用的数据自动销毁,并替换成新数据。memcached本身并不支持分布式,只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。
总的来说redis和memcached各有优点,memcached是全内存的数据库,因此效率必然是比redis快一些(这种效率的提升在并发量千万级以下的场景是可以忽略的);但是redis支持多种数据机构,并且支持持久化,没有线程安全问题。还是要看业务场景。两个数据库的key和value都不宜过大,否则会造成极其严重后果。相对而言memcached由于采用了slab allocation机制分配管理内存(整理内存以重复使用,Slab Allocation的原理相当简单。将分配的内存分割成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunk的集合),并且分配的内存不会释放,会循环使用)在运行时几乎不产生内存碎片,它的稳定性要优于redis,除非机器宕机一般情况下memcached很难挂。
从源码分析来看,redis的源码要比memcached源码更加整洁清爽,这也可能是memcached效率仅仅比redis高一丢丢的原因之一吧。