Redis 的开发规范和常见问题 | 学习笔记

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 快速学习 Redis 的开发规范和常见问题

开发者学堂课程【Redis 入门及实战:Redis 的开发规范和常见问题】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/777/detail/13651


Redis 的开发规范和常见问题

内容介绍:

一、 为什么要制定开发规则

二、 阿里内部系统的开发规约

三、 Redis 的常见问题处理

 

一、为什么要制定开发规则

这个课程的所有内容都是在解决 Redis 在设计时带来的问题,所以从 Redis 带来的问题进行讲解。

1、Redis- 从问题说起

Run-to-Completion in a solo thread – Redis 最大的问题

最大的问题:Redis 运行在一个单线程中。

image.png

如图:用户所有的来自不同 client 的请求,实际上在每个 event 到来后,交由后端单线程执行,等每个 event 处理完成后,才处理下一个。

单线程 run-to-completion in a solo thread-Redis 就是没有 dispatcher, 没有后端的 multl-worker。所以说,如果一旦有慢查询,比如:单机版的 keys、lrange、hgetall 等拖慢了一次查询,那么后面的请求可能会被拖慢。

使用 Sentine 判活的 trick:

ping 命令会受到慢查询影响,就会卡住,则 ping 失败。如果判活失败会导致主备频繁切换,一旦一个 db 的主备都当掉了,那么 Redis 就会 OOS。

如果一个进程不行,那就来一个风幕式及扩展成集群版会怎么样?

扩展成集群版确实能解决一部分问题,但常见的请求是可以分散到不同地域上去的。要注意的是,集群版还是解决不了单个 db 被卡主的问题。因为 Redis 的 key是规则,是按照外面的 Pkey 来做的。如果没有按照里面的子 key 或者是 field 来做,如果遇到了很大的 KKV 结构,同样会卡住。但一旦有其他请求,比如:mget、mset 之类的,或者卡住 db 上本来就有很多请求,仍然会 block 住及问题还是存在。

2、Protocol 问题-大量客户端与引擎 Full-Meshed 问题

image.png

扩展性比较差,基于 Question-Answer 模式,由于在 Question/Answer 里面没有对应的 Sequence 的存在,存储引擎端设法 match 请求和响应,只能采取 Run-To-Completion 来挂住链接。

当引擎挂住太多 active 链接的时候,性能下降太多,如果性能测试,当 db 等于10k active 链接时,性能下降约 30-35%,由于引擎挂住的链接不能被返回客户端,会大量报错。

看下面这张图:

“Could not get a resource from the pool”

image.png

由于 Redis 执行的采用 long to complition 特性,客户端只能采用连接池的方案,因为 Redis 协议不支持连接池收敛。

因为 Message 没有 Sequence ID,Request 和 Response 对不起来。

连接池具体运作方式是每次查询,都从连接池里面取出一个连接,当服务端返回结果之后,再放回连接池。

如果用户返回的及时,那么连接池一直保有的连续数并不高。

但是一旦返回不及时,又有新的请求,就只能再 checkout 一根连接。

当 Engine 层出现慢查询,就会让请求返回得慢。造成的后果是,很容易让用户把连接池用光。

当应用机器特别多时,按每个 client 连接池 50 个 max link 来算,很容易打到 10K连接的限制,导致 engine 回调速度慢。

当连接池被用完时,就会报没有连接的异常,“Could not get a resource from the pool”,这是非常常见的错误。

特别的有恶性循环的在里面,比如:服务端返回得慢、连接池的连接会创建得很快,用户很容易达到一万条,这时候,创建连接越多,性能越差,返回越慢,服务容易雪崩掉。

3、Redis-不要触碰边界

出了这么多问题,是为了在开发业务时,不要触碰 Redis 的边界。

(1)Redis 的边界

这里从计算、存储、网络三个维度出发,总结出下图。

image.png

计算方面:Lua、Wildcard、PUBSUB、热点等会大量消耗计算资源。

存储方面:Streaming、Bigkey 等会造成高存储的消耗。

高网络消耗方面:Keys 全表扫描、大量的 Batch,mget,mset、大Value、Bigkey Range 查询,比如:hgetall、smembers 会造成比较大的网络消耗。

对于 Redis 边界来说:

高并发不等于高吞吐

因为存在大 Value 的问题,高速储存并不会有特别大的高吞吐收益,相反会很危险。QPS 如果高起来,容易把网卡打爆,导致限流。

数据倾斜和算力倾斜

bigKey 对的问题:break 掉存储的分配律,bigkey 大概率是热点问题,所以会导致 cpu 分配的不均衡。

大 Range 的问题:对 NoSQL 的慢查询和导致的危害没有足够的重视

储存边界

Lua 使用不当造成的存储成本直线上升

数据倾斜带来的成本飙升,无法有效使用

对于 Latency 的解决问题(RT高)

存储引擎的 Latency 都是 P99Latency,如:99.99% 在 1ms 以内,99.5% 在 3ms以内,等等。

偶发性时延高是必然的,这个根因在于储存引擎内部的复杂性和熵

所以客户端一定要足够认识到 Latency。

 

二、 阿里内部系统的开发规约

如何避免边界,需要制定规约。

1、Redis 的使用建议

推荐

确定场景,是缓存cache )还是存储型

Cache 的使用原则是:“无它也可 ,有它更强”

永远不要强依赖 Cache,它会丢,也会被海汰

优先设计合理的数据结构和逻辑

设计避免 bigKey,就避免了 80% 的问题

Keyspace 能分开,就多申请几个 Redis 实例

pubsub 不适合做消息分发

尽量避免用 lua 做事务

不建议

我的服务对 RT 很敏感→低 RT 能让我的服务运行的更好,一旦有个 RT 比较高,服务就会崩溃。存储引擎 Latency 都是 P99 计算,客户端一定要避免这种情况,增加一些容错处理。

我把存储都公用在一个 redis 里→区分 cache 和内存数据库用法,区分应用。会导致业务混用的问题,比如:cache 的用法、持久型的用法,这两个业务一旦在一起时,容易造成一些数据淘汰或者是Redis升级之后,会导致其他业务受到不可控的影响。

我有一个大排行榜/大集合/大链表/消息队列;我觉得服务能力足够了→尽量拆散,服务能力不够可通过分布式集群版可以打散,但是如果出现大 Key,分布式集群解决不了。

我有一个特别大的 Value,存在 redis 里,访问能好些 →redis 吞吐量有瓶颈。大Value 会拖慢引擎,网卡、或者限制,所以如果有大 Value,redis 吞吐量是有瓶颈的。

2、Bigkey- 洪水猛兽

据初步统计,80% 问题是由 bigkey 导致。

image.png

上图:集群中有 4 个分片,每个分片大约有 100 个 key,从 key 个数来看,是均匀分布;但是图中有 key301,hash301,下面有两百万个 field,说明是大 key,导致严重的数据倾斜。可能其他的用了 10% 或 20% 的内存,大 key 就用了百分之八九十,而且大 key 大概率是热点,所以上图里面的用法可能会导致一个分片内存满,返回有问题,其他分片就很少用。另一问题,这个分片的访问比较热,造成网卡或者打满,或者 cpu 的打满,导致一些限流发生,服务可能就夯住了。

3、Redis LUA JIT( LUA 脚本文启)

image.png

上示意图表示一次 LUA 脚本的执行过程,客户端调用 EVAL script 后,会产生script load 行为,Lua JIT 开始编译字节生成字节码,会产生 SHA 字符串,表示bytecode 缓存;loading bytecode 后,开始执行脚本,还需保证在副本上执行成功,最后是 unload Cleaning 整个过程结束。

示意图中小火图标表示耗费 cpu 的程度,脚本的 compile-load-run-unload 非常耗费 cpu。

整个 lua 相当于把复杂事务推送到 Redis 中执行,如果稍有不慎内存会爆掉,引擎算力耗光后挂住 redis。

优化办法:比如:script+EVAL、可以将脚本在 redis 中预编译或加载,使用EVALSHA 来执行,会比纯 EVAL 省 cpu,但是 Redis 重启、切换等时,脚本就会消失。需要重新 reload,业务上面需要做这项处理。但是仍然是缺陷方案,建议使用复杂数据结构,或者 module 来取代 lua。比如,tell 推出的 tell steam 或者 CNS 低命令,来实现风幕式锁。

对于 JIT 技术在存储引擎中而言,“EVAL is evil”(可以把 lua 看做魔鬼)尽量避免使用 lua 耗费内存和计算资源(省事省心),稍有不慎,就会出现大问题。

某些 SDK(如 Redisson)很多高级实现都内置使用 lua,开发者可能莫名走入 CPU 运算风暴中,须谨慎。

4、Pubsub/Transaction/Pipeline

image.png

Pubsub 的典型场景

悲观锁(通知更新)

简单信号(消息传递)

不适合:稳定的更新(会丢消息) ; 1 对 N 的消息转发通道(服务瓶颈) ;模糊通知(算力瓶颈)

Transaction、Pipeline 比较多时,会造成 cpu 打满,造成慢查、服务夯阻。

Transaction

伪事务,没有回滚条件

集群版需要所有 key 使用 hashtag 保证,打 hashtag 会导致算力和存储倾斜。

Lua 中封装了 multi-exec,但更耗费 CPU (编译,加载)。比如编译加载时,线上经常会出现这种问题。

Pipeline

看上图,实际是将多个请求封装在一个请求中,合并在一个请求里发送,服务端一次性返回,这样有效地提高 IO 提高执行效率,但是需要用户去聚合小的命令,避免在 pipeline 里做大 range。注意 Pipeline 中的批量任务不是原子执行的(从来不是)。所以要处理 Pipeline

其中几个命令失败的场景。

5、KESY 命令

结论:一定会出问题( sooner or later )

现在没有,到后面客户量上涨后,必然会引发问题。

出现后无能为力处理。

这种情况在一开始时可以预防,可以在控制台通过危险命令禁用,禁止剔除掉命令。

出现时,可以用一些手段优化。

KEYS 命令的模糊命令:

因为 redis 存储 key 是用无序的,必须全标扫描。

Key 数目一多必然卡住,所以一定要优化。

image.png

举个例子:可以修改为 hash 结构,可以从全表扫描变为点查或部分 range。虽然 hash 结构中 field 太多也会变慢,但比 key 性能提升一个到两个数量级。但是这个例子里,product1 前缀可以提取成 hash 的一个 key,然后,要取 product1 前缀的所有东西,可以下发一个 HGETALL,这样就表示优化。

6、除去 KEYS,下面命令依然危险。

hgetall , smembers , lrange,zrange,exhgetall

lrange,zrange,exhgetall 这些复杂难度较高,O(n),携带 value 比较大时,打爆网卡。

建议使用各自命令的 scan 来替代

bitop, bitset

设置过远的 bit 会直接导致 OOM

flushall,flushdb(表示清空数据库)

在阿里云上面有二次校验,在控制台里点击清除数据,会要求是使用者去二次校验,这是比较好的校验功能。在阿里云控制台上面可以单独清理过期数据,对其他正常访问的数据没有影响。

数据丢失

配置中和 ziplist 相关的参数

Redis 在存储相关数据时,数据量较小时,使用 ziplist 结构,达到一定的量级,比如 key 变多,field 变多,就会转换数据结构,比如,hash 就会从 ziplist 转换成ziplist table。当一个结构在 ziplist 结构体质下时,开销变大,比如查询时是 On 级别的,但是占用的内存变少,所以不建议用户随便更改 ziplist 参数。因为修改ziplist,让 hash 在非常大时继续使用 ziplist 结构,因为时间复杂度变成了 On 或者是 O(m*n),这时候是非常大的数量级的提升,hash 的普通操作也会比较慢。

7、规范总结:

1.选型:用户需要确定场景是 cache 还是内存数据库使用

Cache 选择单副本,关闭 AOF ;内存数据库选择双副本

如果 keyspace 能够分开,就申请不同的实例来隔离,减少混用的影响。

2.使用:避免触发高速存储的边界

set/hash/zsetlis/tairhash/bloom/gis 等大 key (内部叫做 godkey )不要超过 3000 ,会记 sillylog

避免使用 keys,hgetall,lrange0-1 等大 range (使用 scan 替代)

避免使用大 value ( 10k 以上就算大 value,50k 会记录)

3.SDK: 使用规范

严禁设置低读超时和紧密重试( 建议 200ms 以下 read timeout,或者是有比较大的概率引起雪崩的 )

需要接受 P99 时延,在客户端做好重试逻辑去增加程序的健壮性,做一些容错处理。

尽量使用扩展数据结构,避免使用 lua

尽量避免 pubsub 和 blocking 的 API

4.接受主动运维

主动运维在阿里云上,比如机器卡机,或者机器有风险,会做主动运维,做 EHA 切换。

 

三、 Redis 的常见问题处理

1、Redis 常见问题的处理

Redis 内存控制是精华部分,大部分问题都是和内存有关。所以先了解 Redis 内存模型。

image.png

总体内存=链路内存(动态)+数据内存+管理内存(静态)

链路内存包括:Input buff、Output buff、等,这两个 buff 跟每个客户端的连接是有关系的,正常情况下是比较小的,但是当 range 操作时,或者有大 key 手法比较慢时,这两个区内存就会增大,影响数据,甚至会造成 OM。还包括 lua JIT 相关的,比如,JIT over hat、lua link,包函了 code catch,lua 执行缓存等等。

第二部分是用户数据区,就是用户实际存储的 value。最后一部分是静态 buff,启动时较小,比较恒定,这个区域主要是管理 key 和 vakue 的一些原数据,overhead。当 key 非常多时,比如,几千万几个亿,就会占用非常大的内存。最后一部分是 Repl+buff,aof+buff 都是比较小的。

OOM 场景

大都是动态内存管理失控

in/out buff 堆积

限流的影响( plus timer mem )

无所畏惧的 Lua 脚本

Tair/Redis

Tair/Redis

Redis 被定义为“缓存"

粗糖的动态内存控制

"Footprint" 是内存数据库的核心技术

Tair 致力于 footprint control

售卖内存接近 User Dataset

2、缓存分析-内存分布统计、bigKey,key pattern

对于内存,阿里云有现成的功能能一键分析,入口在实际管理,CloudDBA 下面的缓存分析;如果是热 Key 分析无需主动触发。缓存分析可以支持历史的备份集和现有的备份集进行分析,所以准实时,或者对历史的备分析。支持所有线上的社区版,企业版,包括所有架构,包括标准版,读写分离版,集群版。

image.png

上图是阿里云控制台的试用截图,这个功能是比较常用的。

3、缓存分析-内存分析统计、bigKey、key pattern

image.png

上图是缓存分析的报告介绍,可以看到每个 db 每个分布内存统计,包括不同类型的数据结构内存统计,key 对应的元素数分级统计,可以统计总体有多少大 key。统计 key 过期时间分布,可以发现过时间设计是否合理,took 100 big key 具体有哪些大 key,业务可以参照这做优化。Took 100 key 前缀,是做了 key padent 统计,如果 key,是按照业务模块来制定的 key 前缀,这就可以统计到个个业务用了多少内存,大体上可以指导业务去优化的。

4、热 key 分析

阿里云提供了在线和离线两种在线方式。

在线实时分析热 key

使用入口:“实例管理”--> "CloudDBA" --> "缓存分析” -- >"HotKey"

使用须知: Tair 版,或 Redis 版本> =redis 4.0

精确统计(非采样),能抓出当前所有 Per Key QPS > 3000 的记录

参考文档: htp://help.aliyun.com/document detail/160585.html

离线分析热 key

方法1 :缓存分析也可以分析出相对较热的 key,通过工具实现

方法2 :最佳实践,imonitor 命令+ redis faina 分析出热点 Key

方法3 :使用审计日志查询历史热 Key, 参考文档

https://help.aliyun.com/document detail/181195.html

5、Tair/Redis 全链路诊断

image.png

链路很长,从 APP 测的 SDK 到网络,到 VIP 还有 Proxy 和 DB。每个部分都有可能出问题。问题排查包括前端排查和后端排查,前端排查先要确定是一台出问题还是全部出问题,如果是一台出问题,那大概率是客户自己的问题。排查方向包括ECS、load、内存等等,PPS 的限制。

客户端包括链接池满、RT(跨地域、gc 等)、建链路慢(K8s、DNS 等)、大查询,发快收慢。网络有可能会丢包、收敛,运营商网格抖动;后面后面主要是后端排查,后端主要是慢查和 CPU 排查,主要的主键是 VIP、Proxy、DB。VIP 上问题比较少,一般没有问题。主要是从 Proxy 和 DB 上去排查。Proxy 的问题有:分发慢查、流量高(扩容 proxy)、消息堆积、Backend 网格抖动。DB 方面:容量,CPU,流量(见前文),主机故障,HA 速度,慢查询。如果是慢查询,需要客户端去排查,是否是业务层面使用方式有问题,进而改造慢查询。

6、Tair/Redis 诊断报告

对于全链路诊断,推出了诊断报告功能。

image.png

可以对某个时间端发起一键诊断,这里主要是后端排查。目前都是 DB 相关的,可以看到有哪些异常情况发生,核心指标的曲线,可以看哪些时间点,哪些节点有峰值;慢请求部分展示了 TOP tend 节点的慢命令的统计,性能水位可以看到哪些指标,哪些节点超过了预设的水位或者是节点发生了倾斜,这对发现问题有很大的帮助。

诊断可以是准实时的,比如过去的半小时,最近半小时或者是对过去某一天或者是某几天的诊断。目前还没有完全对外开放,如果使用有兴趣的话,可以在阿里云上提供端单独开放访问。

7、Tair/Redis 慢日志

设置合理的 Proxy 和 DB 慢日志采集参数

slowlog-log- slower-than: DB 分片上慢日志阈值,不可设置过低。因为

slowlog max-len : DB 分片 slowlog 链表最大保持长度

rt. _threshold ms : Proxy 上慢日志阈值,不可设置过低!

阈值设置过小,那么 DB 在采集慢日志会频繁地记录慢日志,可能会造成引擎的性能变慢,所以说大家尽量要使用默认的参数。

image.png

历史慢日志

使用入口:“实例管理”-->“日志管理” --> "慢日志”

使用须知:  Tair 版,或 Redis 版本> =redis4.0,具体查看帮助文档

可获取近 72 小时内的慢日志

实时慢日志

使用入口:“实例管理" --> "CloudDBA" --“慢请求”

实时获取,能抓出当前所有分片 slowlog

8、资源的规划-自建 VS 云 Redis

从优缺看:有很多同学都会自建 Redis。下面说明自建 Redis 和云 Redis 优缺点和成本。

自建 Redis 的优点:便宜、拥有最高权限,完全自主可控。

可操控性强,自主可控就意味着需要做的东西太多。

image.png

比如:不能做到快速弹性的资源创建,业务突发高峰时,没办法快速处理、需要有增值的 DB 去盯着或者是处理,甚至是基础架构人员长期去维护,管控节点和其他配套的第三方工具或者生态功能需要额外开发,或者是购买资源,安装部署。Redis 的内核是非常难以优化的,没有专家服务兜底,出现紧急问题或者是重大故障时,没有办法快速止血。如果使用云 Redis 服务,开箱即用,可以随时随地弹性升降配,和产品类型的转换。内核方面也做了很多优化,简化集群使用,支持跨SLOT 多 key 操作的 proxy,安全加固,账号权限控制的功能。还有企业级管理增值功能的特性,比如:高可用无脑裂,账号鉴权体系,操作审计,RDB+AOF 备份,5 秒级监控粒度,全量大 key 分析,命令读写统计等。性能强需求可选 Tair 性能增强型:具备多线程和丰富实用的数据结构,同时拥有秒级数据恢复,比如:PITR ,全球多活等强大功能。

成本与大规模需求可选 Tair 持久内存型与容量存储型,多种形态针对不同性能容量要求可以进一步降低成本。最后有 7*24 小时专家服务支持,即使出现重大的问题,也有阿里云的技术专家在线支持,快速止血。我们使用过表格里的建议的规格配置,发现云 Redis 服务的成本是低于自建的,这里对比的是内存版。如果负载压力满足条件,客户可以进一步降低成本,比如:tall 持久内存型,可以降低内存30% 的成本;tall 容量存储型,可以降低 20% 左右。所以选择性比较大。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3月前
|
NoSQL 数据可视化 Linux
redis学习四、可视化操作工具链接 centos redis,付费Redis Desktop Manager和免费Another Redis DeskTop Manager下载、安装
本文介绍了Redis的两个可视化管理工具:付费的Redis Desktop Manager和免费的Another Redis DeskTop Manager,包括它们的下载、安装和使用方法,以及在使用Another Redis DeskTop Manager连接Redis时可能遇到的问题和解决方案。
168 1
redis学习四、可视化操作工具链接 centos redis,付费Redis Desktop Manager和免费Another Redis DeskTop Manager下载、安装
|
3月前
|
NoSQL Linux Redis
Docker学习二(Centos):Docker安装并运行redis(成功运行)
这篇文章介绍了在CentOS系统上使用Docker安装并运行Redis数据库的详细步骤,包括拉取Redis镜像、创建挂载目录、下载配置文件、修改配置以及使用Docker命令运行Redis容器,并检查运行状态和使用Navicat连接Redis。
399 3
|
3月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
50 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
3月前
|
存储 Prometheus NoSQL
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
38 3
|
3月前
|
NoSQL 关系型数据库 MySQL
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
本文全面阐述了Redis事务的特性、原理、具体命令操作,指出Redis事务具有原子性但不保证一致性、持久性和隔离性,并解释了Redis事务的适用场景和WATCH命令的乐观锁机制。
448 0
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
|
3月前
|
NoSQL API Redis
如何使用 C++ 开发 Redis 模块
如何使用 C++ 开发 Redis 模块
|
3月前
|
NoSQL Redis
redis学习五、错误总结,redis正常运行时后会出现一些bug 总结。
本文介绍了Redis在正常运行时可能遇到的一个错误,即无法进行磁盘持久化的问题,并提供了通过设置`stop-writes-on-bgsave-error`为`no`来解决这一问题的方案。
146 0
|
5月前
|
缓存 NoSQL 关系型数据库
Redis学习总结
Redis学习总结
45 1
|
6月前
|
NoSQL API Redis
c++开发redis module问题之为什么在使用RedisModule_GetApi之前要通过((void**)ctx)[0]这种方式获取其地址
c++开发redis module问题之为什么在使用RedisModule_GetApi之前要通过((void**)ctx)[0]这种方式获取其地址
|
6月前
|
Rust NoSQL API
c++开发redis module问题之如果在加载module时,该module没有执行权限,Redis会如何解决
c++开发redis module问题之如果在加载module时,该module没有执行权限,Redis会如何解决