缓存架构设计

缓存的优势

1提升访问性能

2降低网络拥堵

3减轻服务负载

4增强可扩展性

缓存的代价

首先,服务系统中引入缓存,会增加系统的复杂度。

其次,由于缓存相比原始DB存储的成本更高,所以系统部署及运行的费用也会更高。

最后,由于一份数据同时存在缓存和DB中,甚至缓存内部也会有多个数据副本,多份数据就会存在一致性问题,同时缓存体系本身也会存在可用性问题和分区的问题。这就需要我们加强对缓存原理、缓存组件以及优秀缓存体系实践的理解,从系统架构之初就对缓存进行良好设计,降低缓存引入的副作用,让缓存体系成为服务系统高效稳定运行的强力基石。

缓存的根原理

一是时间局限性原理,即被获取过一次的数据在未来会被多次引用,比如一条微博被一个人感兴趣并阅读后,它大概率还会被更多人阅读,当然如果变成热门微博后,会被数以百万/千万计算的更多用户查看。

二是以空间换时间,因为原始数据获取太慢,所以我们开辟一块高速独立空间,提供高效访问,来达到数据获取加速的目的。

三是性能成本Tradeof,相同成本的容量,SSD硬盘容量会比内存大10~30倍以上,但读写延迟却高50~100倍。

缓存的三种模式

Cache Aside(旁路缓存)

适合场景:如果没有专门的存储服务,同时是对数据一致性要求比较高的业务,或者是缓存数据更新比较复杂的业务,这些情况都比较适合使用Cache Aside模式。使用一个Trigger组件,实时读取DB的变更日志,然后重新计算并更新缓存。如果读缓存的时候,Trigger还没写入cache,则由调用方自行到DB加载计算并写入cache。 

Read/Write Through(读写穿透)

存储服务收到业务应用的写请求时,会首先查cache,如果数据在cache中不存在,则只更新DB,如果数据在cache中存在,则先更新cache,然后更新DB。而存储服务收到读请求时,如果命中cache直接返回,否则先从DB加载,回种到cache后返回响应。

 

Write Behind Caching(异步缓存写入)

 数据更新时,Read/write Through是同步更新cache和DB,而Write BehindCaching则是只更新缓存,不直接更新DB,而是改为异步批量的方式来更新DB。但这种模型有个显著的缺点,即数据的一致性变差,甚至在一些极端场景下可能会丢失数据。

缓存的层次

本地Cache (瓶颈在宿主机本身)

进程间Cache(业务进程与缓存进程的资源竞争)

远程Cache。(瓶颈在带宽)

本地Cache的缓存组件有Ehcache、Guava Cache等,开发者自己也可以用Map、Set等轻松构建一个自己专用的本地Cache。进程间Cache和远程Cache的缓存组件相同,只是部署位置的差异罢了,这类缓存组件有Memcached、Redis、Pika等。

内存型缓存将数据存储在内存,读写性能很高,但缓存系统重启或Crash后,内存数据会丢失。持久化型缓存将数据存储到SSD/Fusion-IO硬盘中,相同成本下,这种缓存的容量会比内存型缓存大1个数量级以上,而且数据会持久化落地,重启不丢失,但读写性能相对低1~2个数量级。Memcached是典型的内存型缓存,而Pika以及其他基于RocksDB开发的缓存组件等则属于持久化型缓存。

缓存的设计步骤

1 缓存组件选型

Local-Cache,还是Redis、Memcached、Pika

2 缓存数据结构设计

你可以将这些业务数据封装为String、Json、Protocol Bufer等格式,序列化成字节序列,然后直接写入缓存中。读取时,先从缓存组件获取到数据的字节序列,再进行反序列化操作即可。对于只需要存取部分字段或需要在缓存端进行计算的业务,你可以把数据设计为Hash、Set、List、Geo等结构,存储到支持复杂集合数据类型的缓存中,如Redis、Pika等。

3 缓存分布设计

分布式算法(取模分布,一致性HASH分布)

分布读取策略(Client直接访问,Proxy代理访问)

缓存迁移:将数据从缓存节点进行动态拆分,把部分数据水平迁移到其他缓存节点。

缓存的运维:站在系统层面,要想更好得管理缓存,还要考虑缓存的服务化,考虑缓存体系如何更好得进行集群管理、监控运维等。

缓存的经典问题:

缓存失效

对于批量 key 缓存失效的问题,原因既然是预置的固定过期时间,那解决方案也从这里入手。设计缓存的过期时间时,使用公式:过期时间= baes 时间+随机时间。即相同业务数据写缓存时,在基础过期时间之上,再加一个随机的过期时间,让数据在未来一段时间内慢慢过期,避免瞬时全部过期,对 DB 造成过大压力,

缓存穿透

缓存穿透,则意味着有特殊访客在查询一个不存在的 key,导致每次查询都会穿透到 DB,如果这个特殊访客再控制一批肉鸡机器,持续访问你系统里不存在的 key,就会对 DB 产生很大的压力,从而影响正常服务。

第一种方案就是,查询这些不存在的数据时,第一次查 DB,虽然没查到结果返回 NULL,仍然记录这个 key 到缓存,只是这个 key 对应的 value 是一个特殊设置的值。

第二种方案是,构建一个 BloomFilter 缓存过滤器,记录全量数据,这样访问数据时,可以直接通过 BloomFilter 判断这个 key 是否存在,如果不存在直接返回即可,根本无需查缓存和 DB。

缓存雪崩

简单讲缓存节点因为自身问题被攻破了,导致其他缓存节点被攻破了。最后导致其他非缓存层也沦陷了。

方案一,对业务 DB 的访问增加读写开关,当发现 DB 请求变慢、阻塞,慢请求超过阀值时,就会关闭读开关,部分或所有读 DB 的请求进行 failfast 立即返回,待 DB 恢复后再打开读开关,如下图。

方案二,对缓存增加多个副本,缓存异常或请求 miss 后,再读取其他缓存副本,而且多个缓存副本尽量部署在不同机架,从而确保在任何情况下,缓存系统都会正常对外提供服务。

方案三,对缓存体系进行实时监控,当请求访问的慢速比超过阀值时,及时报警,通过机器替换、服务替换进行及时恢复;也可以通过各种自动故障转移策略,自动关闭异常接口、停止边缘服务、停止部分非核心功能措施,确保在极端场景下,核心功能的正常运行。

数据不一致

不一致的问题大多跟缓存更新异常有关。比如更新 DB 后,写缓存失败,从而导致缓存中存的是老数据。另外,如果系统采用一致性 Hash 分布,同时采用 rehash 自动漂移策略,在节点多次上下线之后,也会产生脏数据。缓存有多个副本时,更新某个副本失败,也会导致这个副本的数据是老数据。

第一个方案,cache 更新失败后,可以进行重试,如果重试失败,则将失败的 key 写入队列机服务,待缓存访问恢复后,将这些 key 从缓存删除。这些 key 在再次被查询时,重新从 DB 加载,从而保证数据的一致性。

第二个方案,缓存时间适当调短,让缓存数据及早过期后,然后从 DB 重新加载,确保数据的最终一致性。

第三个方案,不采用 rehash 漂移策略,而采用缓存分层策略,尽量避免脏数据产生。

数据并发竞争

业务场景:数据并发竞争在大流量系统也比较常见,比如车票系统,如果某个火车车次缓存信息过期,但仍然有大量用户在查询该车次信息。又比如微博系统中,如果某条微博正好被缓存淘汰,但这条微博仍然有大量的转发、评论、赞。上述情况都会造成该车次信息、该条微博存在并发竞争读取的问题。

方案一是使用全局锁。如下图所示,即当缓存请求 miss 后,先尝试加全局锁,只有加全局锁成功的线程,才可以到 DB 去加载数据。其他进程/线程在读取缓存数据 miss 时,如果发现这个 key 有全局锁,就进行等待,待之前的线程将数据从 DB 回种到缓存后,再从缓存获取。

方案二是,对缓存数据保持多个备份,即便其中一个备份中的数据过期或被剔除了,还可以访问其他备份,从而减少数据并发竞争的情况,如下图。

Hot key

对于大多数互联网系统,数据是分冷热的。比如最近的新闻、新发表的微博被访问的频率最高,而比较久远的之前的新闻、微博被访问的频率就会小很多。而在突发事件发生时,大量用户同时去访问这个突发热点信息,访问这个 Hot key,这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象,甚至会被 Crash。

要解决这种极热 key 的问题,首先要找出这些 Hot key 来。对于重要节假日、线上促销活动、集中推送这些提前已知的事情,可以提前评估出可能的热 key 来。而对于突发事件,无法提前评估,可以通过 Spark,对应流任务进行实时分析,及时发现新发布的热点 key。而对于之前已发出的事情,逐步发酵成为热 key 的,则可以通过 Hadoop 对批处理任务离线计算,找出最近历史数据中的高频热 key。找到热 key 后,就有很多解决办法了。首先可以将这些热 key 进行分散处理,比如一个热 key 名字叫 hotkey,可以被分散为 hotkey#1、hotkey#2、hotkey#3,……hotkey#n,这 n 个 key 分散存在多个缓存节点,然后 client 端请求时,随机访问其中某个后缀的 hotkey,这样就可以把热 key 的请求打散,避免一个缓存节点过载,

Big key

能压就压,大就拆成小

memcached的内存结构

Mc的系统架构主要包括网络处理模块、多线程处理模块、哈希表、LRU、slab内存分配模块5部分。Mc基于Libevent实现了网络处理模块,通过多线程并发处理用户请求;基于哈希表对key进行快速定位,基于LRU来管理冷数据的剔除淘汰,基于slab机制进行快速的内存分配及存储。

 

 

Mc访问经典问题

容量问题

性能瓶颈

连接瓶颈

局部故障

流量洪峰

解决方案:

分拆缓存池(取模还是一直hash)

首先对系统内的核心业务数据进行分拆,让访问量大的数据,使用独立的缓存池。同时每个缓存池4~8个节点,这样就可以支撑足够大的容量,还避免单个缓存节点压力过大。对于缓存池的分布策略,可以采用一致性哈希分布和哈希取模分布。一致性哈希分布算法中,首先计算Mc服务节点的哈希值,然后将其持续分散配置在圆中,这样每个缓存节点,实际包括大量大小各异的N个hash点。如下图所示,在数据存储或请求时,对key采用相同的hash算法,并映射到前面的那个圆中,从映射位置顺时针查找,找到的第一个Mc节点,就是目标存取节点。

系统运行过程中,Mc节点故障不可避免,有时候甚至短期内出现多次故障。在Mc节点故障下线后,如果采用一致性hash分布,可以方便得通过rehash策略,将该Mc节点的hash点、访问量,均匀分散到其他Mc节点。如果采用取模分布,则会直接导致1/N的访问miss,N是Mc资源池的节点数。

M-S双层架构

M-S-L1架构

经典工具: Twemproxy

Redis 分析

Version:0.9 StartHTML:0000000105 EndHTML:0000003228 StartFragment:0000000141 EndFragment:0000003188

Redis 是一个事件驱动程序,但和 Memcached 不同的是, Redis 并没有采用 libevent libev 这些开
源库,而是直接开发了一个新的事件循环组件。 Redis 作者给出的理由是,尽量减少外部依赖,而自己
开发的事件模型也足够简洁、轻便、高效,也更易控制。 Redis 的事件驱动模型机制封装在
aeEventLoop 等相关的结构体中,网络连接、命令读取执行回复,数据的持久化、淘汰回收 key 等,
几乎所有的核心操作都通过 ae 事件模型进行处理。

Redis 的事件驱动模型处理 2 类事件:

文件事件,如连接建立、接受请求命令、发送响应等;

时间事件,如 Redis 中定期要执行的统计、key 淘汰、缓冲数据写出、rehash 等。

 

 

Redis 的文件事件采用典型的 Reactor 模式进行处理。Redis 文件事件处理机制分为 4 部分: 

连接 socket IO

多路复用程序

文件事件分派器

事件处理器

IO 多路复用

Redis 封装了 4 种多路复用程序,每种封装实现都提供了相同的 API 实现。编译时,会按照性能和系统 平台,选择最佳的 IO 多路复用函数作为底层实现,选择顺序是,首先尝试选择 Solaries 中的 evport, 如果没有,就尝试选择 Linux 中的 epoll,否则就选择大多 UNIX 系统都支持的 kqueue,这 3 个多路复 用函数都直接使用系统内核内部的结构,可以服务数十万的文件描述符。 如果当前编译环境没有上述函数,就会选择 select 作为底层实现方案。select 方案的性能较差,事件发 生时,会扫描全部监听的描述符,事件复杂度是 O(n),并且只能同时服务有限个文件描述符,32 位机 默认是 1024 个,64 位机默认是 2048 个,所以一般情况下,并不会选择 select 作为线上运行方案。 Redis 的这 4 种实现,分别在 ae_evport、ae_epoll、ae_kqueue 和 ae_select 这 4 个代码文件中。

文件事件收集及派发器

Redis 中的文件事件分派器是 aeProcessEvents 函数。它会首先计算最大可以等待的时间,然后利用 aeApiPoll 等待文件事件的发生。如果在等待时间内,一旦 IO 多路复用程序产生了事件通知,则会立即 轮询所有已产生的文件事件,并将文件事件放入 aeEventLoop 中的 aeFiredEvents 结构数组中。每个 fired event 会记录 socket 及 Redis 读写事件类型。 这里会涉及将多路复用中的事件类型,转换为 Redis 的 ae 事件驱动模型中的事件类型。以采用 Linux 中的 epoll 为例,会将 epoll 中的 EPOLLIN 转为 AE_READABLE 类型,将 epoll 中的 EPOLLOUT、 EPOLLERR 和 EPOLLHUP 转为 AE_WRITABLE 事件。 aeProcessEvents 在获取到触发的事件后,会根据事件类型,将文件事件 dispatch 派发给对应事件处 理函数。如果同一个 socket,同时有读事件和写事件,Redis 派发器会首先派发处理读事件,然后再派 发处理写事件。

Redis 当前的主要时间事件处理函数有 2 个:

serverCron

moduleTimerHandler

Redis 中的时间事件分为 2 类:

单次时间,即执行完毕后,该时间事件就结束了。

周期性事件,在事件执行完毕后,会继续设置下一次执行的事件,从而在时间到达后继续执行,并 不断重复。

时间事件主要有 5 个属性组成。

事件 ID:Redis 为时间事件创建全局唯一 ID,该 ID 按从小到大的顺序进行递增。

执行时间 when_sec 和 when_ms:精确到毫秒,记录该事件的到达可执行时间。

时间事件处理器 timeProc:在时间事件到达时,Redis 会调用相应的 timeProc 处理事件。

关联数据 clientData:在调用 timeProc 时,需要使用该关联数据作为参数。

链表指针 prev 和 next:它用来将时间事件维护为双向链表,便于插入及查找所要执行的时间事 件。

时间事件的处理是在事件循环中的 aeProcessEvents 中进行。执行过程是:

1. 首先遍历所有的时间事件。

2. 比较事件的时间和当前时间,找出可执行的时间事件。

3. 然后执行时间事件的 timeProc 函数。

4. 执行完毕后,对于周期性时间,设置时间新的执行时间;对于单次性时间,设置事件的 ID 为 -1, 后续在事件循环中,下一次执行 aeProcessEvents 的时候从链表中删除。

Redis数据结构

Redis淘汰机制

淘汰方式:同步淘汰,异步淘汰

淘汰策略:

第一种淘汰策略是 noeviction,它是 Redis 的默认策略。在内存超过阀值后,Redis 不做任何清理工作,然后对所有写操作返回错误,但对读请求正常处理。noeviction 适合数据量不大的业务场景,将关键数据存入 Redis 中,将 Redis 当作 DB 来使用。
第二种淘汰策略是 volatile-lru,它对带过期时间的 key 采用最近最少访问算法来淘汰。使用这种策略,Redis 会从 redisDb 的 expire dict 过期字典中,首先随机选择 N 个 key,计算 key 的空闲时间,然后插入 evictionPool 中,最后选择空闲时间最久的 key 进行淘汰。这种策略适合的业务场景是,需要淘汰的 key 带有过期时间,且有冷热区分,从而可以淘汰最久没有访问的 key。

第三种策略是 volatile-lfu,它对带过期时间的 key 采用最近最不经常使用的算法来淘汰。使用这种策略时,Redis 会从 redisDb 中的 expire dict 过期字典中,首先随机选择 N 个 key,然后根据其 value 的lru 值,计算 key 在一段时间内的使用频率相对值。对于 lfu,要选择使用频率最小的 key,为了沿用evictionPool 的 idle 概念,Redis 在计算 lfu 的 Idle 时,采用 255 减去使用频率相对值,从而确保 Idle最大的 key 是使用次数最小的 key,计算 N 个 key 的 Idle 值后,插入 evictionPool,最后选择 Idle 最大,即使用频率最小的 key,进行淘汰。这种策略也适合大多数 key 带过期时间且有冷热区分的业务场景。

第四种策略是 volatile-ttl,它是对带过期时间的 key 中选择最早要过期的 key 进行淘汰。使用这种策略时,Redis 也会从 redisDb 的 expire dict 过期字典中,首先随机选择 N 个 key,然后用最大无符号long 值减去 key 的过期时间来作为 Idle 值,计算 N 个 key 的 Idle 值后,插入 evictionPool,最后选择Idle 最大,即最快就要过期的 key,进行淘汰。这种策略适合,需要淘汰的 key 带过期时间,且有按时间冷热区分的业务场景。


第五种策略是 volatile-random,它是对带过期时间的 key 中随机选择 key 进行淘汰。使用这种策略时,Redis 从 redisDb 的 expire dict 过期字典中,随机选择一个 key,然后进行淘汰。如果需要淘汰的key 有过期时间,没有明显热点,主要被随机访问,那就适合选择这种淘汰策略。


第六种策略是 allkey-lru,它是对所有 key,而非仅仅带过期时间的 key,采用最近最久没有使用的算法来淘汰。这种策略与 volatile-lru 类似,都是从随机选择的 key 中,选择最长时间没有被访问的 key 进行淘汰。区别在于,volatile-lru 是从 redisDb 中的 expire dict 过期字典中选择 key,而 allkey-lru 是从所有的 key 中选择 key。这种策略适合,需要对所有 key 进行淘汰,且数据有冷热读写区分的业务场景。

第七种策略是 allkeys-lfu,它也是针对所有 key 采用最近最不经常使用的算法来淘汰。这种策略与
volatile-lfu 类似,都是在随机选择的 key 中,选择访问频率最小的 key 进行淘汰。区别在于,volatile-flu 从 expire dict 过期字典中选择 key,而 allkeys-lfu 是从主 dict 中选择 key。这种策略适合的场景是,需要从所有的 key 中进行淘汰,但数据有冷热区分,且越热的数据访问频率越高。


最后一种策略是 allkeys-random,它是针对所有 key 进行随机算法进行淘汰。它也是从主 dict 中随机选择 key,然后进行删除回收。如果需要从所有的 key 中进行淘汰,并且 key 的访问没有明显热点,被随机访问,即可采用这种策略。 

 Redis数据恢复

RDB 快照文件主要由 3 部分组成。
1. 第一部分是 RDB 头部,主要包括 RDB 的版本,以及 Redis 版本、创建日期、占用内存等辅助信息。
2. 第二部分是各个 RedisDB 的数据。存储每个 RedisDB 时,会首先记录当前 RedisDB 的 DBID,然后记录主 dict 和 expire dict 的记录数量,最后再轮询存储每条数据记录。存储数据记录时,如果数据有过期时间,首先记录过期时间。如果 Redis 的 maxmemory_policy 过期策略采用 LRU 或者 LFU,还会将 key 对应的 LRU、LFU 值进行落地,最后记录数据的类型、key,以及 value。
3. 第三部部分是 RDB 的尾部。RDB 尾部,首先存储 Redis 中的 Lua 脚本等辅助信息。然后存储
EOF 标记,即值为 255 的字符。最后存 RDB 的 cksum。

触发构建 RDB 的场景主要有以下四种。
1. 第一种场景是通过 save 或 bgsave 命令进行主动 RDB 快照构建。它是由调用方调用 save 或
bgsave 指令进行触发的。
2. 第二种场景是利用配置 save m n 来进行自动快照生成。它是指在 m 秒中,如果插入或变更 n 个key,则自动触发 bgsave。这个配置可以设置多个配置行,以便组合使用。由于峰值期间,Redis的压力大,变更的 key 也比较多,如果再进行构建 RDB 的操作,会进一步增加机器负担,对调用方请求会有一定的影响,所以线上使用时需要谨慎。
3. 第三种场景是主从复制,如果从库需要进行全量复制,此时主库也会进行 bgsave 生成一个 RDB快照。
4. 第四种场景是在运维执行 flushall 清空所有数据,或执行 shutdown 关闭服务时,也会触发 Redis自动构建 RDB 快照。

AOF:

Redis 的 AOF 持久化是以命令追加的方式进行数据落地的。通过打开 appendonly 配置,Redis 将每一个写指令追加到磁盘 AOF 文件,从而及时记录内存数据的最新状态。这样即便 Redis 被 crash 或异常关闭后,再次启动,也可以通过加载 AOF,来恢复最新的全量数据,基本不会丢失数据.

Redis 通过 appendfsync 来设置三种不同的同步文件缓冲策略。
1. 第一种配置策略是 no,即 Redis 不主动使用 fsync 进行文件数据同步落地,而是由操作系统的
write 函数去确认同步时间,在 Linux 系统中大概每 30 秒会进行一次同步,如果 Redis 发生
crash,就会造成大量的数据丢失。
2. 第二种配置策略是 always,即每次将 AOF 缓冲写入文件,都会调用 fsync 强制将内核数据写入文件,安全性最高,但性能上会比较低效,而且由于频繁的 IO 读写,磁盘的寿命会大大降低。

3. 第三种配置策略是 everysec。即每秒通过 BIO 线程进行一次 fsync。这种策略在安全性、性能,以及磁盘寿命之间做较好的权衡,可以较好的满足线上业务需要。

混合持久化:

综合了 RDB 和 AOF 的优缺点,优势是包含全量数据,加载速度快。不足是头部的 RDB 格式兼容性和可读性较差。

Redis阻塞处理

Redis 在运行过程中,不可避免的会产生一些运行慢的、容易引发阻塞的任务,如将内核中的文件缓冲同步到磁盘中、关闭文件,都会引发短时阻塞,还有一些大 key,如一些元素数高达万级或更多的聚合类元素,在删除时,由于所有元素需要逐一释放回收,整个过程耗时也会比较长。而 Redis 的核心处理线程是单进程单线程模型,所有命令的接受与处理、数据淘汰等都在主线程中进行,这些任务处理速度非常快。如果核心单线程还要处理那些慢任务,在处理期间,势必会阻塞用户的正常请求,导致服务卡顿。为此,Redis 引入了 BIO 后台线程,专门处理那些慢任务,从而保证和提升主线程的处理能力。

Redis 的 BIO 线程采用生产者-消费者模型。主线程是生产者,生产各种慢任务,然后存放到任务队列中。BIO 线程是消费者,从队列获取任务并进行处理。如果生产者生产任务过快,队列可用于缓冲这些任务,避免负荷过载或数据丢失。如果消费者处理速度很快,处理完毕后就可以安静的等待,不增加额外的性能开销。再次,有新任务时,主线程通过条件变量来通知 BIO 线程,这样 BIO 线程就可以再次执行任务。

Redis 启动时,会创建三个任务队列,并对应构建 3 个 BIO 线程,三个 BIO 线程与 3 个任务队列之间一一对应。BIO 线程分别处理如下 3 种任务。

  1. close 关闭文件任务。rewriteaof 完成后,主线程需要关闭旧的 AOF 文件,就向 close 队列插入一个旧 AOF 文件的关闭任务。由 close 线程来处理。

  2. fysnc 任务。Redis 将 AOF 数据缓冲写入文件内核缓冲后,需要定期将系统内核缓冲数据写入磁盘,此时可以向 fsync 队列写入一个同步文件缓冲的任务,由 fsync 线程来处理。

  3. lazyfree 任务。Redis 在需要淘汰元素数大于 64 的聚合类数据类型时,如列表、集合、哈希等,就往延迟清理队列中写入待回收的对象,由 lazyfree 线程后续进行异步回收。

 Redis主从复制

 Redis集群方案

Client(自己去连需要的redis)

proxy

常见的 Proxy 端分区方案有 2 种,第一种是基于 Twemproxy 的简单分区方案,第二种是基于Codis 的可平滑数据迁移的分区方案。

Redis Cluster 分区   

Redis 社区版在 3.0 后开始引入 Cluster 策略,一般称之为 Redis-Cluster 方案。Redis-Cluster 按 slot 进行数据的读写和管理,一个 Redis-Cluster 集群包含 16384 个 slot。每个 Redis 分片负责其中一部分 slot。在集群启动时,按需将所有 slot 分配到不同节点,在集群系统运行后,按 slot 分配策略,将 key 进行 hash 计算,并路由到对应节点 访问。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值