为什么 Redis 要比 Memcached 更火?

JAVA葵花宝典

共 3485字,需浏览 7分钟

 ·

2020-09-12 04:31

前言


我们都知道,Redis和Memcached都是内存数据库,它们的访问速度非常之快。但我们在开发过程中,这两个内存数据库,我们到底要如何选择呢?它们的优劣都有哪些?


为什么现在看Redis要比Memcached更火一些?


这篇文章,我们就从各个方面来对比这两个内存数据库的差异,方便你在使用时,做出最符合业务需要的选择。


要分析它们的区别,主要从以下几个方面对比:


  • 线程模型

  • 数据结构

  • 淘汰策略

  • 管道与事务

  • 持久化

  • 高可用

  • 集群化


线程模型


要说性能,必须要分析它们的服务模型。


Memcached处理请求采用多线程模型,并且基于IO多路复用技术,主线程接收到请求后,分发给子线程处理。


这样做好的好处是,当某个请求处理比较耗时,不会影响到其他请求的处理。


当然,缺点是CPU的多线程切换必然存在性能损耗,同时,多线程在访问共享资源时必然要加锁,也会在一定程度上降低性能。


Redis同样采用IO多路复用技术,但它处理请求采用是单线程模型,从接收请求到处理数据都在一个线程中完成。推荐看一下这篇《Redis 到底是单线程还是多线程?》。


这意味着使用Redis,一旦某个请求处理耗时比较长,那么整个Redis就会阻塞住,直到这个请求处理完成后返回,才能处理下一个请求,使用Redis时一定要避免复杂的耗时操作。


单线程的好处是,少了CPU的上下文切换损耗,没有了多线程访问资源的锁竞争,但缺点是无法利用CPU多核的性能。


由于Redis是内存数据库,它的访问速度非常地快,所以它的性能瓶颈不在于CPU,而在于内存和网络带宽,这也是作者采用单线程模型的主要原因。同时,单线程对于程序开发非常友好,调试起来也很方便。开发多线程程序必然会增加一定的调试难度。


因此,当我们的业务使用key的数据比较大时,Memcached的访问性能要比Redis好一些。如果key的数据比较小,两者差别并不大。

严格来说,Redis的单线程指的是处理请求的线程,它本身还有其他线程在工作,例如有其他线程用来异步处理耗时的任务。


Redis6.0又进一步完善了多线程,在接收请求和发送请求时使用多线,进一步提高了处理性能。


数据结构


Memcached支持的数据结构很单一,仅支持string类型的操作。并且对于value的大小限制必须在1MB以下,过期时间不能超过30天。


而Redis支持的数据结构非常丰富,除了常用的数据类型string、list、hash、set、zset之外,还可以使用geo、hyperLogLog数据类型。


使用Memcached时,我们只能把数据序列化后写入到Memcached中。然后再从Memcached中读取数据,再反序列化为我们需要的格式,只能“整存整取”。


而Redis对于不同的数据结构可以采用不同的操作方法,非常灵活。


  • list:可以方便的构建一个链表,或者当作队列使用

  • hash:灵活地操作我们需要的字段,进行“整存零取”、“零存整取”以及“零存零取”

  • set:构建一个不重复的集合,并方便地进行差集、并集运算

  • zset:构建一个排行榜,或带有权重的列表

  • geo:用于地图相关的业务,标识两个地点的坐标,以及计算它们的距离

  • hyperLogLog:使用非常少的内存计算UV


总之,Redis正是因为提供了这么丰富的数据结构,近几年在内存数据库领域大放异彩,为我们的业务开发提供了极大的便利。


淘汰策略


Memcached必须设置整个实例的内存上限,数据达到上限后触发LRU淘汰机制,优先淘汰不常用使用的数据。


但它的数据淘汰机制存在一些问题:刚写入的数据可能会被优先淘汰掉,这个问题主要是它本身内存管理设计机制导致的。


Redis没有限制必须设置内存上限,如果内存足够使用,Redis可以使用足够大的内存。推荐看下《Redis 内存满了怎么办》


同时Redis提供了多种淘汰策略:


  • volatile-lru:从过期key中按LRU机制淘汰

  • allkeys-lru:在所有key中按LRU机制淘汰

  • volatile-random:在过期key中随机淘汰key

  • allkeys-random:在所有key中随机淘汰key

  • volatile-ttl:优先淘汰最近要过期的key

  • volatile-lfu:在所有key中按LFU机制淘汰

  • allkeys-lfu:在过期key中按LFU机制淘汰


我们可以针对业务场景,使用不同的数据淘汰策略。


管道与事务


Redis还支持管道功能,客户端一次性打包发送多条命令到服务端,服务端依次处理客户端发来的命令。这样可以减少来回往来的网络IO次数,提供高访问性能。


另外它还支持事务,这里所说的事务并不是MySQL那样严格的事务模型,这种事务模型是Redis特有的。


一般事务会配合管道一块使用,客户端一次性打包发送多条命令到服务端,并且标识这些命令必须严格按顺序执行,不能被其他客户端打断。同时执行事务之前,客户端可以告诉服务端某个key稍后会进行相关操作,如果这个客户端在操作这个key之前,有其他客户端对这个key进行更改,那么当前客户端在执行这些命令时会放弃整个事务操作,保证一致性。


持久化


Memcached不支持数据的持久化,如果Memcached服务宕机,那么这个节点的数据将全部丢失。


Redis支持将数据持久化磁盘上,提供RDB和AOF两种方式:


  • RDB:将整个实例中的数据快照到磁盘上,全量持久化

  • AOF:把每一个写命令持久到磁盘,增量持久化


Redis使用这两种方式相互配合,完成数据完整性保障,最大程度降低服务宕机导致的数据丢失问题。


高可用


Memcached没有主从复制架构,只能单节点部署,如果节点宕机,那么该节点数据全部丢失。业务需要对这种情况做兼容处理,当某个节点不可用时,把数据写入到其他节点以降低对业务的影响。


Redis拥有主从复制架构,两个节点组成主从架构,从可以实时同步主的数据,提高整个Redis服务的可用性。


同时Redis还提供了哨兵节点,在主节点宕机时,主动把从节点提升为主节点,继续提供服务。


主从两个节点还可以提供读写分离功能,进一步提高程序访问的性能。


集群化


Memcached和Redis都是由多个节点组成集群对外提供服务,但他们的机制也有所不同。


Memcached的集群化是在客户端采用一致性哈希算法向指定节点发送数据,当一个节点宕机时,其他节点会分担这个节点的请求。


而Redis集群化采用的是每个节点维护一部分虚拟槽位,通过key的哈希计算,将key映射到具体的虚拟槽位上,这个槽位再映射到具体的Redis节点。


同时每个Redis节点都包含至少一个从节点,组成主从架构,进一步提高每个节点的高可用能力。


当增加或下线节点时,需要手动触发数据迁移,重新进行哈希槽位映射。


Redis官方的集群化解决方案为Redis cluster,它采用无中心化的设计。另外也有第三方的采用中心化设计proxy方式的集群化解决方案,例如Codis、Twemproxy。


总结


从以上几个方面进行对比分析,总结如下表。


#MemcachedRedis
线程模型多线程单线程
数据结构仅支持string、value最大1M、过期时间不能超过30天string、list、hash、set、zset、geo、hyperLogLog
淘汰策略LRULRU、LFU、随机等多种策略
管道与事务不支持支持
持久化不支持支持
高可用不支持主从复制+哨兵
集群化客户端一致性哈希算法主从复制+哨兵+固定哈希槽位


整体来说,Redis提供了非常丰富的功能,而且性能基本上与Memcached相差无几,这也是它最近这几年占领内存数据库鳌头的原因。


如果你的业务需要各种数据结构给予支撑,同时要求数据的高可用保障,那么选择Redis是比较合适的。


如果你的业务非常简单,只是简单的set/get,并且对于内存使用并不高,那么使用简单的Memcached足够。


如果此文章能给您带来小小的工作效率提升,不妨在看、转发一下,以鼓励我写出更好的文章!


推荐阅读

阿里精选:Java 代码精简之道

Java8 中用法优雅的 Stream,性能也""优雅""吗?

ElasticSearch 索引 VS MySQL 索引

还在手动部署SpringBoot应用?试试这个自动化插件!

MySQL执行计划Explain详解

浏览 13
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报