写点什么

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

评论

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

SAP OData V4 模型支持的一些数据绑定模式

汪子熙

JavaScript 前端开发 SAP ui5 5月月更

跨平台应用开发进阶(十二) :uni-app 实现应用桌面图标角标提示及应用跳转

No Silver Bullet

uni-app 5月月更 桌面角标

1.1 历史长河中的顶层设计

凌晞

数据结构-复杂度计算经典案例

芒果酱

数据结构 算法 5月月更

[ CloudWeGo 社区动态 ] Kitex 电商项目案例

baiyutang

Go 微服务 5月月更

SpringMVC源码分析:POST请求中的文件处理

程序员欣宸

Java spring 5月月更

Amazon CodePipeline 与 GitHub 集成

亚马逊云科技 (Amazon Web Services)

GitHub Code

WordPress 编辑用户

海拥(haiyong.site)

WordPress 5月月更

Kubernetes 节点弹性扩展实践组件 Amazon Karpenter:部署 GPU 推理应用

亚马逊云科技 (Amazon Web Services)

Kubernetes 部署

PostgreSQL出现死锁怎么办?

慕枫技术笔记

数据库 5月月更

数学建模学习资料

乌龟哥哥

5月月更

如何透过 Serverless 与 API 的方式异步搜寻数据湖中的数据

亚马逊云科技 (Amazon Web Services)

Serverless 数据 API

【C 语言】指针 Five 之 ["​⚔ 空指针 - NULL、💣 指针使用之前检查有效性、🗡 指针运算 💣 指针+- 整数、💣 指针 - 指针、指针关系运算、💣 标准规定、⚔ 指针和数组、⚔ 二级指针、⚔ 指针数组"]

謓泽

5月月更

在线HTML转JSON工具

入门小站

工具

【刷题第16天】数组中出现次数超过一半的数字

白日梦

5月月更

从“数据”到“大数据”,激发数据潜力,深耕智能应用!

亚马逊云科技 (Amazon Web Services)

大数据 数据 智能开发

M_5: 设计微博系统中”微博评论“的高性能高可用计算架构。

Jadedev

架构训练营

Linux环境编译静态库

Loken

音视频 5月月更

架构实战营 - 第 6 期 模块六课后作业

天琪实刚亮

架构实战营

提效客户体验管理:结合 K-Means 聚类和 RFM 模型的客户分群

龙国富

RFM 客户分群 K-Means

LinkedList 源码分析-迭代器

zarmnosaj

5月月更

C++最佳实践 | 1. 工具

俞凡

c++ 最佳实践

跨平台应用开发进阶(十一) :uni-app 实现IOS原生APP-云打包集成极光推送(JG-JPUSH)详细教程

No Silver Bullet

uni-app ios 5月月更 云打包 原生APP

规模化软件开发的必由之路—大规模自动化测试

刘冉

自动化测试

Artery —— 单页面应用接口描述语言简介

全象云低代码

前端 低代码 流程 页面 artery

密码学系列之:使用openssl检测网站是否支持ocsp

程序那些事

Java 密码学 程序那些事 5月月更

flask框架【入门学习笔记一】

恒山其若陋兮

5月月更

Linux 入门及常见Shell命令

宇宙之一粟

Linux Shell 5月月更

react-router原理分析

正经工程师

React React-Router

架构学习(一)

爱晒太阳的大白

5月月更

记一次 JMeter 压测 HTTPS 性能问题

阿里巴巴云原生

阿里云 云原生 https Jmeter 压测

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