NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

Redis 集群中 Key 用法的最佳实践

  • 2019-10-24
  • 本文字数:2935 字

    阅读完需:约 10 分钟

Redis 集群中 Key 用法的最佳实践

在 Redis 中,究竟什么是 Key?Redis(或任意其它的 key-value 数据库)的初衷是为每块单独的数据提供一个 Key(或称之为标识符)。Redis 通过数据类型迅速扩充了这一概念,一个 key 可以引用到多块数据,甚至上百万块的数据。随着模块的引入,key 的概念进一步被拉伸,因为单独的一块数据已经可以横跨多个 key(例如 RediSearch)。因此,当有人问 Redis 是否是一个 Key-value 数据库时,我一般都回答“它是一个发源于 key-value 的数据库”,但需要注意的是,现在已经很难把 Redis 仅仅当做一个 key-value 数据库来看待了。


然而,在集群中,Redis 的 Key 依然是非常重要的。在 Redis 集群中,每个节点或分片都只有部分 key 和数据。当然了,如果 Redis 以高可用性方式运行,数据还可能会分布在从节点中。但不管怎么说,一个单独的 key 是不会分布到多个节点中的。


一个集群被分割成 16384 个 slot,这是集群能支持的最大节点数或分片数(如果你需要有更多的节点数或分片数,请与我们联系)。由于大多数集群都是由少量节点组成的,这些 slot 用来对 key 做逻辑划分。在以下 4 节点的简化集群实例中,我们有如下的 slot 分布:



举个例子,如果你知道一个 key 它在 slot 2000 上,那你就知道它在 Node #0 上。同样的,如果你知道一个 key 它在 slot 9000 上,那它就在 Node#02 上。实际上,现实中的集群比这要复杂得多,因为 slot 可能一直在节点间移动和重新平衡,但为了理解事务和 key,这种集群的简化概念理解就够了。


那么 key 和 slot 到底是怎么关联起来的呢?这些 slot 实际上都是散列 slot,每个 key 都先用一个散列函数进行计算(这个散列函数可接受任意长度的字符串),计算完成后得到一个散列数字。散列经常用在密码这个类似但又比这复杂得多的场合,一般来说密码都不是直接存储的,而是存储为密码的某个数学表示。在 Redis 中,你访问请求的 Key,本质上也是访问这个 key 的数学表示,只是在这个场景中,我们使用的是 crc16 散列函数。Crc16 散列函数返回的是一个 14bit 的整数,我们用这个返回的整数除于 16384 并取其余数。这刚好是 Redis 集群能支持的最大分片数,很有趣,不是吗?(译者注:作者可能想表达的意思是,crc16 返回的是一个 14bit 的整数,2 的 14 次方刚好是 16384,集群最大能支持的 slot 个数也是 16384,这两个数字刚好相等,所以有趣。)

这一切如何和 Redis 事务关联起来?

Redis 的事务仅支持单个 Slot,这可确保 Redis 的最大吞吐量。因为不需要节点/分片间的通信,这也避免了很多的故障情况。鉴于此,当你在 Redis 中执行事务的时候,你要确保所有 Key 都在同一个 slot 中。那么,怎么知道事务中一个 key 跟其它 key 是在同一个 slot 中呢(或者在同一个节点/分片中)?


虽然许多 key 可能是属于同一个 slot 的,但从 key 的命名角度来说这也是难于预测的,给 key 命名时每次都去检查下 key 是否同一个 slot 也不现实(Redis 开源版本或企业版本中,都支持 CLUSTER KEYSLOT 命令)。解决这个问题的最佳方式是对 key 进行规划和使用 hash tag 特性。在开源 Redis 中,花括号{}表示 hash tag,这个两个花括号中间的字符才会进行 CRC16 散列计算。让我们看几个例子:



在这些示例中,你可以看到,user-session:1234 和 user-profile:1234 这两个 key 是不允许在同一个事务中的,但 user-profile:{1234} 和 user-session:{1234} 是可以在同一个事务中的。


注意:你可能会想:“太好了,那我将所有 key 都放在同一个 slot 中,那就不用去担心集群的事务了”。很好,你并不孤单,因为我已经不是第一次听到这种不良导向了。Redis 不会阻止你干这种事情或者其它类似的事情,但你将会得到一个不均衡的集群,或者可能更糟糕的是,一个节点爆满了,但其他很多节点都是空的。只在有必要的时候使用 hash tag ,也同时要谨慎的使用它。


Redis 企业版也可以支持这种策略,但还增加了一个特性,使得数据分片更加透明。这个策略不是使用花括号,而是使用正则表达式来定义 key 中的哪部分进行 hash 运算。让我们用正则表达式“/user-.*:(?.+)/ ”来重新审视下上面的那个例子:



这个正则表达式足够灵活,以致于其它“user-”开头的 key 也可以被处理,所以我们也可以使用类似“user-image”或“user-pagecount”的 key。采用这个方案,每个用户的信息都保存在一个单独的 slot 中,可保证一个用户范围内的各种事务都可以被处理。


让我们来进一步扩展下这个样例。假设用户更改了他个人资料的一些信息,我们想要同时更新他的个人资料以及会话信息,并延长会话使会话不被过期。以下是一个典型(简化的)的事务实现版本:


>MULTIOK> HSET user-profile:1234username "king foo"QUEUED> HSET user-session:1234 username"king foo"QUEUED> EXPIRE user-session:12347200QUEUED> EXEC1) (integer) 12) (integer) 13) (integer) 1
复制代码


如果你要在 Redis 开源版本中运行这个样例,你必须保证这些 key 都有花括号,否则你会得到一个 CROSSSLOT 错误。这个错误的好处就是 Redis 会立刻通知你无效事务和跨 slot 违规。


>MULTIOK> HSET user-profile:1234username "king foo"QUEUED> HSET user-session:1234username "king foo"(error) ERR CROSSSLOT Keys inrequest don't hash to the same slot (command='HSET', key='user-session:1234')within 'MULTI'> EXPIRE user-session:12347200(error) ERR CROSSSLOT Keys inrequest don't hash to the same slot (command='EXPIRE', key='user-session:1234')within 'MULTI'> EXEC(error) EXECABORT Transactiondiscarded because of previous errors.
复制代码


请记住,slot 和 key 的问题并不局限于事务,其他场景下它们也是类似的,特别是一个命令需要操作多个 Key 的时候。举个例子:


>LPUSH my-list 1 2 3(integer) 3> RPOPLPUSH my-listmy-new-list(error) ERR CROSSSLOT Keys inrequest don't hash to the same slot (command='RPOPLPUSH', key='my-new-list')
复制代码


RPOPLPUSH 是个原子操作,它从一个 list 中取出元素,并将元素放入到另外一个 list 中,这个操作是原子性的。如果这两个 list 是两个不同的 slot 中(就像事务场景那样),你会得到一个 CROSSSLOT 错误。Redis 开源版本是严格限制这点的,任何命令同时操作两个 slot 都是禁止的。

充分利用集群

如果你已经是 Redis 单实例的高级用户,切换到集群后你会感觉到有些奇怪。你已经依赖的部分命令或事务,在集群中可能无法正常工作。如果你真的不走运,你之前设计的 keyspace 可能都会有问题。以下是让你的应用程序与 Redis 集群最佳工作的一些设计提示:


1、考虑 keyspace 设计。key 是否存在一些公共特征可将工作负载智能分散(按用户、按操作,按时间,或者其他)。使用 hashtag 或正则表达式将 key 分散到不同的 slot。


2、避免使用单个 key 来保存全局状态,而且这个操作还是事务性的,否则你很大可能会碰到 CROSSSLOTS 错误。


3、评估你的 MULTI/EXEC 事务。考虑下你是否真的需要事务,或者使用 pipeline 是否就能满足你要求。也不要忘记考虑多 key 的命令,这些多 key 操作的命令是否可以替换为多个命令来操作。


本文转载自公众号中间件小哥(ID:huawei_kevin)。


原文链接:


https://mp.weixin.qq.com/s/KKcd1LmVAvOF51jEx1Yvvw


2019-10-24 11:074344

评论

发布
暂无评论
发现更多内容

元器选型攻略之 电阻

元器件秋姐

元器件知识 元器件科普 电阻 电阻定义 常见电阻

透明LED屏幕如何设计显示效果更好?

Dylan

LED LED显示屏

通过支付网关提高第三方支付接入效率

产品海豚湾

产品经理 产品设计 支付系统 产品架构 11月月更

跨越速运如何构建实时统一的运单分析

StarRocks

#数据库

web前端和java培训学编程哪个更好

小谷哥

Service Mesh 的下一站是 Sidecarless 吗?

SOFAStack

MOSN

常规加密算法是什么?原理是怎么样?有哪些?

行云管家

算法 加密算法 国密

磐久网络|揭秘阿里云HAIL数据中心网络

云布道师

阿里云 数据中心 基础设施建设

基于云边协同架构的五大应用场景革新

阿里云视频云

阿里云 边缘计算 边缘云

教你用JavaScript完成进度条

小院里的霍大侠

JavaScript 编程开发 初学者 入门实战

云计算和虚拟化的三个小区别简单说明

行云管家

云计算 虚拟化

阿里P8面试官总结的《2022最新年底java面试题》,搞定90%以上的技术面

钟奕礼

Java 程序员 java面试 java编程

Intel Arch SIG:介绍下一代数据中心互联协议CXL及在龙蜥的规划 | 第 54 期

OpenAnolis小助手

开源 直播 intel 龙蜥大讲堂 CXL

Kotlin伴生对象(Companion Object)

子不语Any

android kotlin 11月月更

瓴羊Quick BI自助式报表分析工具,令企业的运营服务更高效

夏日星河

火山引擎DataLeap的Data Catalog系统公有云实践

字节跳动数据平台

大数据 火山引擎 大数据研发

java程序员,是不是很想进字节跳动?开发三年的我拿到了入职通知

钟奕礼

Java 程序员 java面试 java编程

LeetCode题解:938. 二叉搜索树的范围和,栈,JavaScript,详细注释

Lee Chen

JavaScript LeetCode

New Features | NFTScan 推出 BlueChip、Watch List、Activity Overview

NFT Research

区块链 NFT 数据基础设施

降价背后,函数计算规格自主选配功能揭秘

阿里巴巴云原生

阿里云 云原生 函数计算

QuTrunk与MindSpore量子神经网络初探

启科量子开发者官方号

人工智能 ai框架 量子编程

亚信科技亮相南京软博会,数智赋能百行千业

亚信AntDB数据库

AntDB aisware antdb AntDB数据库

老工程师荐读!PCB设计避坑指南

华秋PCB

PCB PCB设计

深入了解瓴羊Quick BI,对于商业智能BI发展情况更好分析

巷子

java 环境变量配置详细教程(2023 年全网最详细,没有之一)

千锋IT教育

火山引擎 DataTester 智能运营,帮企业实现“千人千面”精准营销

字节跳动数据平台

A/B 测试

开源共建 | TIS整合数据同步工具ChunJun,携手完善开源生态

袋鼠云数栈

大数据 开源 数据同步工具

华夏银行:详解iDo平台一体化运维的落地过程

嘉为蓝鲸

运维 金融 银行 数字化

制造业数字化发展,瓴羊Quick BI引起了需求者的关注

夏日星河

嘉为科技张敏:一文讲清场景工程方法论及运维组织能力内化

嘉为蓝鲸

运维 数字化

Kotlin作用域函数[Scope Function](上)

子不语Any

android kotlin 11月月更

Redis 集群中 Key 用法的最佳实践_文化 & 方法_中间件小哥_InfoQ精选文章