缓存系列文章–无底洞问题

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

一、背景 

 1. 什么是缓存无底洞问题:

Facebook的工作人员反应2010年已达到3000个memcached节点,储存数千G的缓存。他们发现一个问题–memcached的连接效率下降了,于是添加memcached节点,添加完之后,并没有好转。称为“无底洞”现象

2. 缓存无底洞产生的原因:

键值数据库或者缓存系统,由于通常采用hash函数将key映射到对应的实例,造成key的分布与业务无关,但是由于数据量、访问量的需求,需要使用分布式后(无论是客户端一致性哈性、redis-cluster、codis),批量操作比如批量获取多个key(例如redis的mget操作),通常需要从不同实例获取key值,相比于单机批量操作只涉及到一次网络操作,分布式批量操作会涉及到多次网络io。

 

 

3. 无底洞问题带来的危害:

(1) 客户端一次批量操作会涉及多次网络操作,也就意味着批量操作会随着实例的增多,耗时会不断增大。

(2) 服务端网络连接次数变多,对实例的性能也有一定影响。
4. 结论:

用一句通俗的话总结:更多的机器不代表更多的性能,所谓“无底洞”就是说投入越多不一定产出越多。

分布式又是不可以避免的,因为我们的网站访问量和数据量越来越大,一个实例根本坑不住,所以如何高效的在分布式缓存和存储批量获取数据是一个难点。

 

二、哈希存储与顺序存储

在分布式存储产品中,哈希存储与顺序存储是两种重要的数据存储和分布方式,这两种方式不同也直接决定了批量获取数据的不同,所以这里需要对这两种数据的分布式方式进行简要说明:

1. hash分布:

hash分布应用于大部分key-value系统中,例如memcache, redis-cluster, twemproxy,即使像mysql在分库分表时候,也经常会用user%100这样的方式。

hash分布的主要作用是将key均匀的分布到各个机器,所以它的一个特点就是数据分散度较高,实现方式通常是hash(key)得到的整数再和分布式节点的某台机器做映射,以redis-cluster为例子:

问题:和业务没什么关系,不支持范围查询。

2. 顺序分布

 

 3. 两种分布方式的比较:

分布方式 特点 典型产品
哈希分布 1. 数据分散度高2.键值分布与业务无关3.无法顺序访问

4.支持批量操作

一致性哈希memcacheredisCluster其他缓存产品
顺序分布 1.数据分散度易倾斜2.键值分布与业务相关3.可以顺序访问

4.支持批量操作

BigTableHbase

 

 

 

三、分布式缓存/存储四种Mget解决方案

 

1. IO的优化思路:

(1) 命令本身的效率:例如sql优化,命令优化

(2) 网络次数:减少通信次数

(3) 降低接入成本:长连/连接池,NIO等。

(4) IO访问合并:O(n)到O(1)过程:批量接口(mget),

 

2.  如果只考虑减少网络次数的话,mget会有如下模型

 

 

3. 四种解决方案:

(1).串行mget

将Mget操作(n个key)拆分为逐次执行N次get操作, 很明显这种操作时间复杂度较高,它的操作时间=n次网络时间+n次命令时间,网络次数是n,很显然这种方案不是最优的,但是足够简单。

 

(2). 串行IO

将Mget操作(n个key),利用已知的hash函数算出key对应的节点,这样就可以得到一个这样的关系:Map<node, somekeys>,也就是每个节点对应的一些keys

它的操作时间=node次网络时间+n次命令时间,网络次数是node的个数,很明显这种方案比第一种要好很多,但是如果节点数足够多,还是有一定的性能问题。

 

 

(3). 并行IO

此方案是将方案(2)中的最后一步,改为多线程执行,网络次数虽然还是nodes.size(),但网络时间变为o(1),但是这种方案会增加编程的复杂度。

它的操作时间=1次网络时间+n次命令时间

 

(4). hash-tag实现。

第二节提到过,由于hash函数会造成key随机分配到各个节点,那么有没有一种方法能够强制一些key到指定节点到指定的节点呢?

redis提供了这样的功能,叫做hash-tag。什么意思呢?假如我们现在使用的是redis-cluster(10个redis节点组成),我们现在有1000个k-v,那么按照hash函数(crc16)规则,这1000个key会被打散到10个节点上,那么时间复杂度还是上述(1)~(3)

那么我们能不能像使用单机redis一样,一次IO将所有的key取出来呢?hash-tag提供了这样的功能,如果将上述的key改为如下,也就是用大括号括起来相同的内容,那么这些key就会到指定的一个节点上。

例如:

 

user1,user2,user3......user1000
{user}1,{user}2,{user}3.......{user}1000

 

 

 

例如下图:它的操作时间=1次网络时间+n次命令时间

 

 

3. 四种批量操作解决方案对比:

方案 优点 缺点 网络IO
串行mget 1.编程简单2.少量keys,性能满足要求 大量keys请求延迟严重 o(keys)
串行IO 1.编程简单2.少量节点,性能满足要求 大量node延迟严重 o(nodes)
并行IO 1.利用并行特性2.延迟取决于最慢的节点 1.编程复杂2.超时定位较难 o(max_slow(node))
hash tags 性能最高 1.tag-key业务维护成本较高2.tag分布容易出现数据倾斜 o(1)

 

 

 

 

四、总结和建议

 

无底洞问题对资源和性能有一定影响,但是其实大部分系统不需要考虑这个问题,因为

1. 99%公司的数据和流量无法和facebook相比。

2. redis/memcache的分布式集群通常来讲是按照项目组做隔离的,以我们经验来看一般不会超过50对主从。

所以这里只是提供了一种优化的思路,开阔一下视野。

 

 

五、参考文献

  1. Facebook’s Memcached Multiget Hole: More machines != More Capacity  
  2. Multiget的无底洞问题
  3. 再说memcache的multiget hole(无底洞)

 一、背景 

 1. 什么是缓存无底洞问题:

Facebook的工作人员反应2010年已达到3000个memcached节点,储存数千G的缓存。他们发现一个问题–memcached的连接效率下降了,于是添加memcached节点,添加完之后,并没有好转。称为“无底洞”现象

2. 缓存无底洞产生的原因:

键值数据库或者缓存系统,由于通常采用hash函数将key映射到对应的实例,造成key的分布与业务无关,但是由于数据量、访问量的需求,需要使用分布式后(无论是客户端一致性哈性、redis-cluster、codis),批量操作比如批量获取多个key(例如redis的mget操作),通常需要从不同实例获取key值,相比于单机批量操作只涉及到一次网络操作,分布式批量操作会涉及到多次网络io。

 

 

3. 无底洞问题带来的危害:

(1) 客户端一次批量操作会涉及多次网络操作,也就意味着批量操作会随着实例的增多,耗时会不断增大。

(2) 服务端网络连接次数变多,对实例的性能也有一定影响。
4. 结论:

用一句通俗的话总结:更多的机器不代表更多的性能,所谓“无底洞”就是说投入越多不一定产出越多。

分布式又是不可以避免的,因为我们的网站访问量和数据量越来越大,一个实例根本坑不住,所以如何高效的在分布式缓存和存储批量获取数据是一个难点。

 

二、哈希存储与顺序存储

在分布式存储产品中,哈希存储与顺序存储是两种重要的数据存储和分布方式,这两种方式不同也直接决定了批量获取数据的不同,所以这里需要对这两种数据的分布式方式进行简要说明:

1. hash分布:

hash分布应用于大部分key-value系统中,例如memcache, redis-cluster, twemproxy,即使像mysql在分库分表时候,也经常会用user%100这样的方式。

hash分布的主要作用是将key均匀的分布到各个机器,所以它的一个特点就是数据分散度较高,实现方式通常是hash(key)得到的整数再和分布式节点的某台机器做映射,以redis-cluster为例子:

问题:和业务没什么关系,不支持范围查询。

2. 顺序分布

 

 3. 两种分布方式的比较:

分布方式 特点 典型产品
哈希分布 1. 数据分散度高2.键值分布与业务无关3.无法顺序访问

4.支持批量操作

一致性哈希memcacheredisCluster其他缓存产品
顺序分布 1.数据分散度易倾斜2.键值分布与业务相关3.可以顺序访问

4.支持批量操作

BigTableHbase

 

 

 

三、分布式缓存/存储四种Mget解决方案

 

1. IO的优化思路:

(1) 命令本身的效率:例如sql优化,命令优化

(2) 网络次数:减少通信次数

(3) 降低接入成本:长连/连接池,NIO等。

(4) IO访问合并:O(n)到O(1)过程:批量接口(mget),

 

2.  如果只考虑减少网络次数的话,mget会有如下模型

 

 

3. 四种解决方案:

(1).串行mget

将Mget操作(n个key)拆分为逐次执行N次get操作, 很明显这种操作时间复杂度较高,它的操作时间=n次网络时间+n次命令时间,网络次数是n,很显然这种方案不是最优的,但是足够简单。

 

(2). 串行IO

将Mget操作(n个key),利用已知的hash函数算出key对应的节点,这样就可以得到一个这样的关系:Map<node, somekeys>,也就是每个节点对应的一些keys

它的操作时间=node次网络时间+n次命令时间,网络次数是node的个数,很明显这种方案比第一种要好很多,但是如果节点数足够多,还是有一定的性能问题。

 

 

(3). 并行IO

此方案是将方案(2)中的最后一步,改为多线程执行,网络次数虽然还是nodes.size(),但网络时间变为o(1),但是这种方案会增加编程的复杂度。

它的操作时间=1次网络时间+n次命令时间

 

(4). hash-tag实现。

第二节提到过,由于hash函数会造成key随机分配到各个节点,那么有没有一种方法能够强制一些key到指定节点到指定的节点呢?

redis提供了这样的功能,叫做hash-tag。什么意思呢?假如我们现在使用的是redis-cluster(10个redis节点组成),我们现在有1000个k-v,那么按照hash函数(crc16)规则,这1000个key会被打散到10个节点上,那么时间复杂度还是上述(1)~(3)

那么我们能不能像使用单机redis一样,一次IO将所有的key取出来呢?hash-tag提供了这样的功能,如果将上述的key改为如下,也就是用大括号括起来相同的内容,那么这些key就会到指定的一个节点上。

例如:

 

user1,user2,user3......user1000
{user}1,{user}2,{user}3.......{user}1000

 

 

 

例如下图:它的操作时间=1次网络时间+n次命令时间

 

 

3. 四种批量操作解决方案对比:

方案 优点 缺点 网络IO
串行mget 1.编程简单2.少量keys,性能满足要求 大量keys请求延迟严重 o(keys)
串行IO 1.编程简单2.少量节点,性能满足要求 大量node延迟严重 o(nodes)
并行IO 1.利用并行特性2.延迟取决于最慢的节点 1.编程复杂2.超时定位较难 o(max_slow(node))
hash tags 性能最高 1.tag-key业务维护成本较高2.tag分布容易出现数据倾斜 o(1)

 

 

 

 

四、总结和建议

 

无底洞问题对资源和性能有一定影响,但是其实大部分系统不需要考虑这个问题,因为

1. 99%公司的数据和流量无法和facebook相比。

2. redis/memcache的分布式集群通常来讲是按照项目组做隔离的,以我们经验来看一般不会超过50对主从。

所以这里只是提供了一种优化的思路,开阔一下视野。

 

 

五、参考文献

  1. Facebook’s Memcached Multiget Hole: More machines != More Capacity  
  2. Multiget的无底洞问题
  3. 再说memcache的multiget hole(无底洞)

 一、背景 

 1. 什么是缓存无底洞问题:

Facebook的工作人员反应2010年已达到3000个memcached节点,储存数千G的缓存。他们发现一个问题–memcached的连接效率下降了,于是添加memcached节点,添加完之后,并没有好转。称为“无底洞”现象

2. 缓存无底洞产生的原因:

键值数据库或者缓存系统,由于通常采用hash函数将key映射到对应的实例,造成key的分布与业务无关,但是由于数据量、访问量的需求,需要使用分布式后(无论是客户端一致性哈性、redis-cluster、codis),批量操作比如批量获取多个key(例如redis的mget操作),通常需要从不同实例获取key值,相比于单机批量操作只涉及到一次网络操作,分布式批量操作会涉及到多次网络io。

 

 

3. 无底洞问题带来的危害:

(1) 客户端一次批量操作会涉及多次网络操作,也就意味着批量操作会随着实例的增多,耗时会不断增大。

(2) 服务端网络连接次数变多,对实例的性能也有一定影响。
4. 结论:

用一句通俗的话总结:更多的机器不代表更多的性能,所谓“无底洞”就是说投入越多不一定产出越多。

分布式又是不可以避免的,因为我们的网站访问量和数据量越来越大,一个实例根本坑不住,所以如何高效的在分布式缓存和存储批量获取数据是一个难点。

 

二、哈希存储与顺序存储

在分布式存储产品中,哈希存储与顺序存储是两种重要的数据存储和分布方式,这两种方式不同也直接决定了批量获取数据的不同,所以这里需要对这两种数据的分布式方式进行简要说明:

1. hash分布:

hash分布应用于大部分key-value系统中,例如memcache, redis-cluster, twemproxy,即使像mysql在分库分表时候,也经常会用user%100这样的方式。

hash分布的主要作用是将key均匀的分布到各个机器,所以它的一个特点就是数据分散度较高,实现方式通常是hash(key)得到的整数再和分布式节点的某台机器做映射,以redis-cluster为例子:

问题:和业务没什么关系,不支持范围查询。

2. 顺序分布

 

 3. 两种分布方式的比较:

分布方式 特点 典型产品
哈希分布 1. 数据分散度高2.键值分布与业务无关3.无法顺序访问

4.支持批量操作

一致性哈希memcacheredisCluster其他缓存产品
顺序分布 1.数据分散度易倾斜2.键值分布与业务相关3.可以顺序访问

4.支持批量操作

BigTableHbase

 

 

 

三、分布式缓存/存储四种Mget解决方案

 

1. IO的优化思路:

(1) 命令本身的效率:例如sql优化,命令优化

(2) 网络次数:减少通信次数

(3) 降低接入成本:长连/连接池,NIO等。

(4) IO访问合并:O(n)到O(1)过程:批量接口(mget),

 

2.  如果只考虑减少网络次数的话,mget会有如下模型

 

 

3. 四种解决方案:

(1).串行mget

将Mget操作(n个key)拆分为逐次执行N次get操作, 很明显这种操作时间复杂度较高,它的操作时间=n次网络时间+n次命令时间,网络次数是n,很显然这种方案不是最优的,但是足够简单。

 

(2). 串行IO

将Mget操作(n个key),利用已知的hash函数算出key对应的节点,这样就可以得到一个这样的关系:Map<node, somekeys>,也就是每个节点对应的一些keys

它的操作时间=node次网络时间+n次命令时间,网络次数是node的个数,很明显这种方案比第一种要好很多,但是如果节点数足够多,还是有一定的性能问题。

 

 

(3). 并行IO

此方案是将方案(2)中的最后一步,改为多线程执行,网络次数虽然还是nodes.size(),但网络时间变为o(1),但是这种方案会增加编程的复杂度。

它的操作时间=1次网络时间+n次命令时间

 

(4). hash-tag实现。

第二节提到过,由于hash函数会造成key随机分配到各个节点,那么有没有一种方法能够强制一些key到指定节点到指定的节点呢?

redis提供了这样的功能,叫做hash-tag。什么意思呢?假如我们现在使用的是redis-cluster(10个redis节点组成),我们现在有1000个k-v,那么按照hash函数(crc16)规则,这1000个key会被打散到10个节点上,那么时间复杂度还是上述(1)~(3)

那么我们能不能像使用单机redis一样,一次IO将所有的key取出来呢?hash-tag提供了这样的功能,如果将上述的key改为如下,也就是用大括号括起来相同的内容,那么这些key就会到指定的一个节点上。

例如:

 

user1,user2,user3......user1000
{user}1,{user}2,{user}3.......{user}1000

 

 

 

例如下图:它的操作时间=1次网络时间+n次命令时间

 

 

3. 四种批量操作解决方案对比:

方案 优点 缺点 网络IO
串行mget 1.编程简单2.少量keys,性能满足要求 大量keys请求延迟严重 o(keys)
串行IO 1.编程简单2.少量节点,性能满足要求 大量node延迟严重 o(nodes)
并行IO 1.利用并行特性2.延迟取决于最慢的节点 1.编程复杂2.超时定位较难 o(max_slow(node))
hash tags 性能最高 1.tag-key业务维护成本较高2.tag分布容易出现数据倾斜 o(1)

 

 

 

 

四、总结和建议

 

无底洞问题对资源和性能有一定影响,但是其实大部分系统不需要考虑这个问题,因为

1. 99%公司的数据和流量无法和facebook相比。

2. redis/memcache的分布式集群通常来讲是按照项目组做隔离的,以我们经验来看一般不会超过50对主从。

所以这里只是提供了一种优化的思路,开阔一下视野。

 

 

五、参考文献

  1. Facebook’s Memcached Multiget Hole: More machines != More Capacity  
  2. Multiget的无底洞问题
  3. 再说memcache的multiget hole(无底洞)

 

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
存储 缓存 NoSQL
缓存系列文章--7.无底洞问题(multiget hole)
    转载请注明出处哈:http://carlosfu.iteye.com/blog/2269678   最近有点忙,一直没更新博客,继续坚持下去。   一、背景    1. 什么是缓存无底洞问题: Facebook的工作人员反应2010年已达到3000个memcached节点,储存数千G的缓存。
1441 0
|
11天前
|
消息中间件 缓存 NoSQL
Redis经典问题:缓存雪崩
本文介绍了Redis缓存雪崩问题及其解决方案。缓存雪崩是指大量缓存同一时间失效,导致请求涌入数据库,可能造成系统崩溃。解决方法包括:1) 使用Redis主从复制和哨兵机制提高高可用性;2) 结合本地ehcache缓存和Hystrix限流降级策略;3) 设置随机过期时间避免同一时刻大量缓存失效;4) 使用缓存标记策略,在标记失效时更新数据缓存;5) 实施多级缓存策略,如一级缓存失效时由二级缓存更新;6) 通过第三方插件如RocketMQ自动更新缓存。这些策略有助于保障系统的稳定运行。
393 1
|
11天前
|
存储 消息中间件 缓存
Redis缓存技术详解
【5月更文挑战第6天】Redis是一款高性能内存数据结构存储系统,常用于缓存、消息队列、分布式锁等场景。其特点包括速度快(全内存存储)、丰富数据类型、持久化、发布/订阅、主从复制和分布式锁。优化策略包括选择合适数据类型、设置过期时间、使用Pipeline、开启持久化、监控调优及使用集群。通过这些手段,Redis能为系统提供高效稳定的服务。
|
4天前
|
缓存 NoSQL Redis
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?-- Redis多线程
【5月更文挑战第21天】Redis启用多线程后,主线程负责接收事件和命令执行,IO线程处理读写数据。请求处理流程中,主线程接收客户端请求,IO线程读取并解析命令,主线程执行后写回响应。业界普遍认为,除非必要,否则不建议启用多线程模式,因单线程性能已能满足多数需求。公司实际场景中,启用多线程使QPS提升约50%,或选择使用Redis Cluster以提升性能和可用性。
9 0
|
5天前
|
NoSQL Redis 数据库
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?-- Memcache + Redis 多线程
【5月更文挑战第20天】Redis采用单线程模式以避免上下文切换和资源竞争,简化调试,且其性能瓶颈在于网络IO和内存,而非多线程。相比之下,Memcache使用多线程能更好地利用多核CPU,但伴随上下文切换和锁管理的开销。尽管Redis单线程性能不俗,6.0版本引入多线程以提升高并发下的IO处理能力。启用多线程后,Redis结合Reactor和epoll实现并发处理,提高系统性能。
25 0
|
6天前
|
缓存 NoSQL 中间件
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?epoll、poll和select + Reactor模式
【5月更文挑战第19天】`epoll`、`poll`和`select`是Linux下多路复用IO的三种方式。`select`需要主动调用检查文件描述符,而`epoll`能实现回调,即使不调用`epoll_wait`也能处理就绪事件。`poll`与`select`类似,但支持更多文件描述符。面试时,重点讲解`epoll`的高效性和`Reactor`模式,该模式包括一个分发器和多个处理器,用于处理连接和读写事件。Redis采用单线程模型结合`epoll`的Reactor模式,确保高性能。在Redis 6.0后引入多线程,但基本原理保持不变。
24 2
|
7天前
|
缓存 NoSQL Redis
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?--epoll调用和中断
【5月更文挑战第18天】`epoll`包含红黑树和就绪列表,用于高效管理文件描述符。关键系统调用有3个:`epoll_create()`创建epoll结构,`epoll_ctl()`添加/删除/修改文件描述符,`epoll_wait()`获取就绪文件描述符。`epoll_wait()`可设置超时时间(-1阻塞,0立即返回,正数等待指定时间)。当文件描述符满足条件(如数据到达)时,通过中断机制(如网卡或时钟中断)更新就绪列表,唤醒等待的进程。
35 6
|
8天前
|
NoSQL Redis 缓存
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?
【5月更文挑战第17天】Redis常被称为单线程,但实际上其在处理命令时采用单线程,但在6.0后IO变为多线程。持久化和数据同步等任务由额外线程处理,因此严格来说Redis是多线程的。面试时需理解Redis的IO模型,如epoll和Reactor模式,以及其内存操作带来的高性能。Redis使用epoll进行高效文件描述符管理,实现高性能的网络IO。在讨论Redis与Memcached的线程模型差异时,应强调Redis的单线程模型如何通过内存操作和高效IO实现高性能。
37 7
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?
|
11天前
|
缓存 NoSQL 关系型数据库
【Redis】Redis 缓存重点解析
【Redis】Redis 缓存重点解析
28 0
|
11天前
|
缓存 NoSQL 关系型数据库
【Redis】Redis作为缓存
【Redis】Redis作为缓存
13 0