当前位置:首页 > 问答 > 正文

Redis槽位切换那点事儿,数据分布怎么能更顺畅点呢?

Redis槽位切换那点事儿,数据分布怎么能更顺畅点呢?这事儿得从Redis集群怎么存数据说起,根据Antirez(Redis创始人)的设计,Redis集群没用那种死板的“主从服务器固定对应”或者“按顺序分片”的法子,而是玩了个聪明的:把整个数据空间分成16384个小格子,每个格子叫一个“槽位”(slot),你的每一条数据,比如一个键值对,会根据键的名字通过一个算法(CRC16校验后取模16384)算出来它应该待在哪个槽位里,集群里的每个主节点都负责管理其中的一部分槽位,这样,数据就均匀地分散到不同的机器上了。

听起来挺美,对吧?但问题就出在“槽位管理”上,比如你一开始就三台机器,那好办,大概每台管五千多个槽位,可业务是活的呀,数据量涨了,访问压力大了,你就得加新机器进来分担压力;或者有机器顶不住了要下线,这时候,你就得进行“槽位迁移”:把原来由A机器管的某些槽位,连同槽位里的数据,一起搬家到B机器上去,这个搬家的过程,槽位切换”的核心部分,也是最容易出幺蛾子的地方。

那怎么让这个过程更顺畅,尽量不影响线上服务呢?首先啊,Redis集群本身的设计其实已经考虑到了,它不是那种“一刀切”的迁移,根据Redis官方文档里的描述,它的迁移是“在线”和“渐进式”的,啥意思呢?就是说,它不是一下子把整个槽位的数据全锁住,然后吭哧吭哧地搬,那样服务就得中断好久,它是这么干的:比如现在要把槽位100从节点A迁到节点B。

第一步,先在节点B上发个命令,告诉它:“嘿,你准备一下,马上有批槽位(包括100)要归你管了,但你先别急着对外宣布。”这时候,节点B会把这些槽位标记为“导入中”。

第二步,在节点A上发个命令,说:“喂,你管的槽位100要搬家了,新家是节点B,从现在开始,你每收到一个关于槽位100的新写命令,除了你自己处理,还得给节点B也转发一份过去(这步是为了保证迁移过程中新数据不丢),你可以开始把存量数据一点点地发给B了。”

这个“一点点”发存量数据就是关键,节点A会一批一批地扫瞄槽位100里的键,每扫到一批,就发给节点B,同时不阻塞对这个槽位的其他读写请求,除非某个键正好在被迁移的过程中被访问,可能会有一点点延迟,其他大部分请求都感觉不到变化。

第三步,等节点A把槽位100里所有的键都搬完了,它会再发个命令给节点B,确认迁移完成,集群配置会更新,正式对外宣布:“以后槽位100归节点B管啦!”这时候,如果还有请求误打到节点A问槽位100的事,节点A会直接告诉客户端:“你找错门了,新地址是节点B,你去找它。”客户端收到这个“挪窝”的通知后,就会更新自己的路由表,下次就直接找B了。

你看,这个过程设计得挺巧妙吧?但为啥实际操作中还是可能卡顿或不顺呢?问题往往出在细节和周边环境上。

第一,数据量太大,如果一个槽位里恰好有特别大的键,比如一个存了几百万个元素的超大集合(Hash或Set),迁移这个键本身就会耗时很久,期间对这个键的操作可能会被阻塞,或者整个槽位的数据量特别庞大,比如几十个GB,那迁移过程就像用吸管给游泳池换水,时间拉得很长,增加了很多不确定性。

第二,网络是个大坑,迁移说白了就是大量数据在网络里传输,如果节点A和B之间的网络带宽不够,或者延迟很高、不稳定,那迁移速度就快不起来,还可能因为网络抖动导致迁移失败,得重来,要是机房跨了地域,那问题就更明显了。

第三,客户端得“懂事”,Redis集群协议要求客户端能正确理解并处理那种“挪窝”的重定向响应,但有些老的或者实现得不好的客户端库,可能没完全照规矩来,比如收到重定向后没更新本地路由,或者更新不及时,导致请求还在往老节点上打,造成一堆错误和延迟。

那想让数据分布更顺畅点,有啥招呢?

规划得早点,别临时抱佛脚。 在设计集群初期,就得预估数据的增长,尽量让键的分布均匀些,有个小技巧,可以用花括号{}给键加上“标签”,user:{123}:profileuser:{123}:orders,这样计算槽位时只会看花括号里的内容,就能保证同一个用户的相关数据落在同一个槽位,但不同用户的数据还是分散的,这既方便了某些需要同时获取多个键的场景,也避免了某个热点用户的全部数据都挤在一个槽位,将来迁移时成烫手山芋。

迁移操作要温柔,别蛮干。 真要迁移时,最好在业务低峰期搞,别一口气迁移太多槽位,可以分批分次地进行,密切监控节点的网络流量、内存、CPU和延迟等指标,如果发现迁移导致目标节点B压力山大,或者源节点A响应变慢,就得适当放缓迁移速度,或者暂停一下,等系统平稳了再继续。

管好客户端。 确保你的应用程序用的Redis客户端库是较新的、对集群支持良好的版本,并且要测试它在面对重定向、节点失败等情况时的表现是否正常,一个“聪明”的客户端是保证集群高可用的重要一环。

工具用起来。 Redis官方和一些第三方工具提供了集群管理工具,redis-cli --cluster reshard 命令,它能帮你交互式地、相对安全地执行槽位迁移,用这些工具比自己手动发命令更靠谱,能减少操作失误。

说到底,Redis槽位切换这事儿,顺畅与否关键在于对细节的把控,理解了它底层“渐进式迁移”的原理,再结合自己业务的实际情况,做好预案、精细操作、严密监控,就能让数据在集群里的流动像溪水一样,虽然方向变了,但依然保持流畅。


Redis槽位切换那点事儿,数据分布怎么能更顺畅点呢?