写点什么

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:074879

评论

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

虚拟化解决方案 virtio 的技术趋势与 DPU 实践解读 | 龙蜥技术

OpenAnolis小助手

虚拟化 技术分享 DPU 龙蜥大讲堂 云豹智能

密码学系列之:ASN.1接口描述语言详解

程序那些事

密码学 程序那些事 5月月更 ASN.1

React Echarts 使用教程 - 如何在 React 中加入图表(内附数据看板实战搭建案例)

蒋川

react.js 可视化 eCharts 组件库 图表库

HashSet源码分析-基础结构

zarmnosaj

5月月更

MDM结合ESB数据集成说明

agileai

数据治理 数据集成 企业服务总线 主数据平台 基础数据方案

从Oracle日志解析学习数据库内核原理

沃趣科技

oracle 数据库内核

免费领 CRMEB 移动社交电商系统源码与授权

CRMEB

融云漫话:通信中台

融云 RongCloud

元宇宙用户已准备就绪,但技术瓶颈仍制约其真正“落地”

CECBC

元宇宙的十大经济规则

CECBC

大数据ETL自动化调度运维专家-TASKCTL

敏捷调度TASKCTL

Docker 大数据 程序员 DevOps 数据仓库

打造会“找”主人的麦克风,腾讯个性化语音增强技术荣获2022数博会领先科技成果奖

科技热闻

什么是区块哈希?哈希游戏开发技术原理方案(源码搭建)

开发微hkkf5566

【技术干货】代码示例:使用 Apache Flink 连接 TDengine

TDengine

数据库 tdengine

加码布局版式文档垂直赛道,福昕船舶图纸管理系统重磅发布

联营汇聚

如何实现24/7客户服务自动化?建设智能客服知识库

小炮

直播预告|来啦!「DevOps+MLOps Meetup」火热报名中!

第四范式开发者社区

人工智能 DevOps 极狐GitLab 特征平台 MLOps

揭秘亚马逊云科技软件开发工程师团队

亚马逊云科技 (Amazon Web Services)

软件开发 工程师

AI简报-增强版GAN图像超分:ESRGAN

AIWeker

人工智能 深度学习 5月月更 AI简报

周六晚8点,如何基于 eBPF 技术构建应用可观测平台?

OpenAnolis小助手

Linux 直播 内核 龙蜥社区 sig

【智人智语】史赛克全球数字化、机器人及赋能技术总裁柯若博:世界智能大会是一个非常重要的盛会

InfoQ 天津

ETH合并在即,这次升级将带来哪些机遇?

区块链前沿News

以太坊 eth Hoo

Hadoop3.0时代,怎么能不懂EC纠删码技术?

个推

大数据 hadoop hadoop3 大数据 开源

怎么用netty开发一个同时提供http和websocket的服务?

风斩断晚霞

Java spring Netty springboot websocket

浪潮云应用质效管理系统(AOM)荣获2022数博会领先科技成果“优秀项目”奖

云计算

【LeetCode】单词距离Java题解

Albert

LeetCode 5月月更

MASA Auth - SSO与Identity设计

MASA技术团队

C# .net 设计 构架 Auth

面向服务的架构

力软低代码开发平台

技术分享 | Javaer 如何做单元测试?

LigaAI

Java 单元测试 LigaAI

谁需要实验室内部管理系统?

低代码小观

实验室管理系统 数据管理系统 LIMS实验室信息管理系统 企业管理工具 检查系统服务

图解|高性能服务器设计之缓存系统一致性

C++后台开发

redis 后端开发 Linux服务器开发 高性能服务器 系统缓存

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