本系列文章:
Redis(一)数据类型、常用命令
Redis(二)Redis客户端的使用
Redis(三)持久化、主从复制
Redis(四)Redis内存
Redis(五)哨兵、集群
Redis(六)缓存、分布式锁
Redis(七)Redis优化建议
可通过info memory
命令获取内存相关指标。示例:
各个属性的含义:
属性 | 说明 |
---|---|
used_memory | Redis分配器分配的内存总量,也就是内部存储的所有数据内存占用量 |
used_memory_human | 以可读的形式返回used_memory |
used_memory_rss | 从操作系统的角度显示Redis进程占用的物理内存总量 |
used_memory_peak | 内存使用的最大值,表示used_memory的峰值 |
used_memory_peak_human | 以可读的格式返回used_memory_peak |
used_memory_lua | Lua引擎所消耗的内存大小 |
mem_fragmentation_ratio | used_memory_rss / used_memory比值,表示内存碎片率 |
mem_allocator | Redis所使用的的内存分配器,默认为jemalloc |
当mem_fragmentation_ratio(used_memory_rss / used_memory)
>1时,说明used_memory_rss-used_memory多出的部分内存并没有用于数据存储,而是被内存碎片所消耗。如果两者相差很大,说明碎片率严重。
当mem_fragmentation_ratio
<1时,这种情况一般出现在操作系统把Redis内存交换到硬盘导致,出现这种情况时要格外关注,由于硬盘速度远远慢于内存,Redis性能会变得很差。
Redis进程内消耗主要包括:自身内存+对象内存+缓冲内存+内存碎片
,Redis空进程自身内存消耗非常少,通常used_memory_rss(Redis进程占用的物理内存总量)在3MB左右,一个空的Redis进程消耗内存可以忽略不计。
sizeof(keys)+sizeof(values)
。键对象都是字符串,在使用Redis时应当避免使用过长的键。client-output-buffer-limit
控制,不同类型客户端的默认值:普通客户端
:指除了复制和订阅的客户端之外的所有连接。一般情况下,普通客户端的内存消耗可以忽略不计,但是当有大量慢连接客户端接入时这部分内存消耗就不能忽略了,可以设置maxclients(最大连接数)做限制。从客户端
:当主从节点之间网络延迟较高或主节点挂载大量从节点时这部分内存消耗将占用很大一部分,建议主节点挂载的从节点不要多于2个,主从节点不要部署在较差的网络环境下,如异地跨机房环境。订阅客户端
:当订阅服务的消息生产快于消费速度时,输出缓冲区会产生积压造成输出缓冲区空间溢出。复制积压缓冲区
:Redis在2.8版本之后提供了一个可重用的固定大小缓冲区用于实现部分复制功能,由repl-backlog-size
参数表示,默认1M,示例:AOF缓冲区
:这部分空间用于在Redis重写期间保存最近的写入命令,这部分空间占用通常很小。
小
:[8byte],[16byte,32byte,48byte,…,128byte],[192byte,256byte,…512byte],[768byte,1024byte,…,3840byte]
大
:[4KB,8KB,12KB,…,4072KB]
巨大
:[4MB,8MB,12MB,…]
比如当保存10KB对象时jemalloc可能会采用12KB的块存储,而剩下的3KB空间变为了内存碎片不能再分配给其他对象存储。
jemalloc正常的碎片率(mem_fragmentation_ratio)在1.03左右。
以下场景容易出现高内存碎片问题:
- 频繁做更新操作。
- 大量过期键删除,键对象过期删除后,释放的空间无法得到充分利用,导致碎片率上升。
出现高内存碎片问题时常见的解决方式:
数据对齐
:在条件允许的情况下尽量做数据对齐,比如数据尽量采用数字类型或者固定长度字符串等。- 安全重启:重启节点可以做到内存碎片重新整理。
Redis使用maxmemory
参数限制最大可用内存。示例:
如果不设置maxmemory或者设置为0,64位系统不限制内存,32位系统最多使用3GB内存
。
maxmemory限制的是Redis实际使用的内存量,也就是used_memory统计项对应的内存。由于内存碎片的存在,实际消耗的内存可能会比maxmemory设置的更大,实际使用时要小心这部分内存溢出。
比如一台24GB内存的服务器,为系统预留4GB内存,预留4GB空闲内存给其他进程或Redis fork进程,留给Redis16GB内存,这样可以部署4个maxmemory=4GB的Redis进程。
通过config set maxmemory
可以设置一个Redis示例的内存上限,示例:
Redis的内存回收机制主要体现在以下两个方面:
- 删除到达过期时间的键对象;
- 内存使用达到maxmemory上限时触发内存溢出控制策略。
从库不会进行过期扫描,从库对过期的处理是被动的。主库在key到期时,会在AOF文件里增加一条del指令,同步到所有的从库,从库通过执行这条del指令来删除过期的key。
Redis中有个设置时间过期的功能,即对存储在Redis 数据库中的值可以设置一个过期时间。
我们执行set key
命令 的时候,都可以指定expire time
,即过期时间,以此来限定这个 key 可以存活的时间。
对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。
键过期策略通常有以下三种:定时过期、惰性过期和定期过期。
在设置键的过期时间的同时,创建一个定时器(timer)
,让定时器在键的过期时间来临时,立即执行对键的删除操作;定时删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放,但是
对CPU是最不友好的
。因为删除操作会占用CPU的时间,如果刚好碰上了CPU很忙的时候,比如正在做交集或排序等计算的时候,就会给CPU造成额外的压力。
从键空间中获取键时,都检查取得的键是否过期
,如果过期的话,就删除该键;如果没有过期,那就返回该键;每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键
。至于删除多少过期键,以及要检查多少个数据库,则由算法决定。 由于维护每个键精准的过期删除机制会导致消耗大量的CPU,成本过高(不采用第1种策略的愿意),因此Redis采用惰性删除和定期删除机制实现过期键的内存回收
。
惰性删除
:当客户端读取超时的键时,会执行删除键操作并返回空。单独用这种方式,浪费内存,存在内存泄露的问题(当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放)。正因为如此,Redis还提供另一种定时任务删除机制作为惰性删除的补充。
定期删除
:Redis内部维护一个定时任务,默认每秒运行10次,根据键的过期比例、使用快慢两种速率模式回收键。
定时任务删除过期键流程:
- 定时任务(每10秒执行一次,在Redis配置文件中有默认配置:
hz 10
)在每个数据库空间随机检查20个键,当发现过期时删除对应的键。- 如果超过检查数25%的键过期,循环执行回收逻辑直到不足25%或运行超时为止,慢模式下超时时间为25毫秒。
- 如果之前回收键逻辑超时,则在Redis触发内部事件之前再次以快模式运行回收过期键任务,快模式下超时时间为1毫秒且2秒内只能运行1次。
此处再简单回顾一下Key的过期时间和永久有效分别怎么设置。
setex
命令可以为key设置value值,并且同时设置过期时间。示例:
也可以用expire
命令,直接为key设置过期时间。示例:
setex
是一个原子操作,设置值,设置过期时间两个动作,会在同一时间完成。
persist
命令用于移除给定 key 的过期时间,使得 key 永不过期。示例:
当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略,由maxmemory-policy
参数控制,示例:
在Redis4.0以前,内存溢出策略有6种,默认值为noeviction
,含义为:
1、
noeviction
:默认策略,不会删除任何键,拒绝所有写入操作并返回客户端错误信息OOM command not allowed when used memory,只响应读操作。
2、volatile-lru
:根据LRU算法删除超时的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。
3、allkeys-lru
:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。
4、allkeys-random
:随机删除所有键,直到腾出足够空间为止。
5、volatile-random
:随机删除过期键,直到腾出足够空间为止。
6、volatile-ttl
:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。
Redis4.0以后,又增加了以下两种:
7、
volatile-lfu
:从所有配置了过期时间的键中驱逐使用频率最少的键
(频率最少)。
8、allkeys-lfu
:从所有键中驱逐使用频率最少的键
(频率最少)。
从这些策略的名称可以看出:
volatile:表示过期的键
allkeys:表示所有键
lru:表示使用LRU算法,简单来说是删除过期时间最久的键
lfu:表示使用LFU算法,简单来说是删除使用频率最少的键
ttl:表示马上要过期
内存淘汰策略可以通过配置文件来修改,redis.conf对应的配置项是maxmemory-policy 修改对应的值就行。要动态配置的话,可以用config set maxmemory-policy {policy}
命令进行修改。
频繁执行回收内存成本很高,主要包括查找可回收键和删除键的开销。
Redis用的是一种近似LRU算法,它跟LRU算法还不太一样。之所以不使用LRU算法,是因为需要消耗大量的额外的内存,需要对现有的数据结构进行较大的改造。近似LRU算法则很简单,在现有数据结构的基础上使用随机采样法来淘汰元素,能达到和 LRU算法非常近似的效果。Redis为实现近似LRU算法,它给每个 key 增加了一个额外的小字段,这个字段的长度是24个bit,也就是最后一次被访问的时间戳。
Redis存储的所有值对象在内部定义为redisObject结构体:
Redis存储的数据都使用redisObject来封装,包括string、hash、list、set、zset在内的所有数据类型。
type
:表示当前对象使用的数据类型。
encoding
:表示Redis内部编码类型。
lru
:记录对象最后一次被访问的时间。
refcount
:记录当前对象被引用的次数,用于通过引用次数回收内存,当refcount=0时,可以安全回收当前对象空间。使用object refcount {key}
命令可以获取当前对象引用。
*ptr
:如果是整数,直接存储数据;否则表示指向数据的指针。Redis在3.0之后对值对象是字符串且长度<=39字节的数据,内部编码为embstr类型,字符串和redisObject一起分配,从而只要一次内存操作即可。
高并发写入场景中,在条件允许的情况下,建议字符串长度控制在39字节以内,减少创建redisObject内存分配次数,从而提高性能。
降低Redis内存使用最直接的方式就是缩减键和值的长度
。
在设计键时,在完整描述业务情况下,键值越短越好。
值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数据放入Redis。值对象缩减常见的做法分为两大类:
- 首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。
- 其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。
Java常见序列化组件占用内存空间对比(单位字节):
java-built-in-serializer
表示Java内置序列化方式。
值对象除了存储二进制数据之外,通常还会使用通用格式存储数据比如:json、xml等作为字符串存储在Redis中。这种方式优点是方便调试和跨语言,但是同样的数据相比字节数组所需的空间更大,在内存紧张的情况下,可以使用通用压缩算法压缩json、xml后再存入Redis,从而降低内存占用,例如使用GZIP压缩后的json可降低约60%的空间。
共享对象池是指Redis内部维护[0-9999]的整数对象池,用于节约内存
。
整数对象池在Redis源码中定义,不能修改。
是否使用整数对象池内存对比:
可见使用共享对象池后,相同的数据内存使用降低30%以上。
当设置maxmemory并启用LRU相关淘汰策略如:volatile-lru,allkeys-lru时,Redis禁止使用共享对象池。
LRU算法需要获取对象最后被访问时间,以便淘汰最长未访问数据,每个对象最后访问时间存储在redisObject对象的lru字段。对象共享意味着多个引用共享同一个redisObject,这时lru字段也会被共享,导致无法获取每个对象的最后访问时间。因此,使用LUR相关淘汰策略时,无法使用共享对象池
。
如果没有设置maxmemory,直到内存被用尽Redis也不会触发内存回收,所以共享对象池可以正常工作。因此,设置了maxmemory后,无法使用共享对象池
。
Redis没有采用原生C语言的字符串类型而是自己实现了字符串结构,内部简单动态字符串(simple dynamic string,SDS),图示:
获取字符串长度、已用长度、未用长度的时间复杂度都是O(1)。
内部实现空间预分配机制,字符串缩减后的空间不释放,作为预分配空间保留。
字符串之所以采用预分配的方式是防止修改操作需要不断重分配内存和字节数据拷贝。但同样也会造成内存的浪费。字符串预分配每次并不都是翻倍扩容,空间预分配规则:
- 第一次创建len属性等于数据实际大小,free等于0,不做预分配。
- 修改后如果已有free空间不够且数据小于1M,每次预分配一倍容量。如原有len=60byte,free=0,再追加60byte,预分配120byte,总占用空间:60byte+60byte+120byte+1byte。
- 修改后如果已有free空间不够且数据大于1MB,每次预分配1MB数据。如原有len=30MB,free=0,当再追加100byte,预分配1MB,总占用空间:1MB+100byte+1MB+1byte。
尽量减少字符串频繁修改操作如append、setrange,改为直接使用set修改字符串,降低预分配带来的内存浪费和内存碎片化
。
Redis对外提供了string、list、hash、set、zet等类型,对内也有不同的底层数据结构的实现,可以用object encoding {key}
命令查看,示例:
Redis针对每种对外数据类型可以采用至少两种编码方式来实现,示例:
不同编码主要是为了实现效率和空间的平衡。
编码类型转换在Redis写入数据时自动完成,这个转换过程是不可逆的,转换规则只能从小内存编码向大内存编码转换。示例:
Redis之所以不支持编码回退,主要是数据增删频繁时,数据向压缩编码转换非常消耗CPU。
示例中用到了list-max-ziplist-entries参数,这个参数用来决定列表长度在多少范围内使用ziplist编码。
ziplist
编码主要目的是为了节约内存,因此所有数据都是采用线性连续的内存结构。ziplist编码是应用范围最广的一种,可以分别作为hash、list、zset类型的底层数据结构实现。一个ziplist可以包含多个entry(元素),每个entry保存具体的数据(整数或者字节数组),内部结构:
zlbytes
:记录整个压缩列表所占字节长度,方便重新调整ziplist空间。类型是int-32,长度为4字节。
zltail
:记录距离尾节点的偏移量,方便尾节点弹出操作。类型是int-32,长度为4字节。
zllen
:记录压缩链表节点数量,当长度超过216-2时需要遍历整个列表获取长度,一般很少见。类型是int-16,长度为2字节。
entry
:记录具体的节点,长度根据实际存储的数据而定。
zlend
:记录列表结尾,占用一个字节。
Entry的元素:
prev_entry_bytes_length
:记录前一个节点所占空间,用于快速定位上一个节点,可实现列表反向迭代。
encoding
:标示当前节点编码和长度,前两位表示编码类型:字符串/整数,其余位表示数据长度。
contents
:保存节点的值,针对实际数据长度做内存占用优化。
ziplist的特点:
- 底层实现为数据紧凑排列的一块连续内存数组。
- 可以模拟双向链表结构,以O(1)时间复杂度入队和出队。
- 新增删除操作涉及内存重新分配或释放,加大了操作的复杂性。
- 读写操作涉及复杂的指针移动,最坏时间复杂度为O(n2)。
- 适合存储小对象和长度有限的数据。
intset
是集合类型编码的一种,内部表现为存储有序、不重复的整数集。当集合只包含整数且长度不超过set-max-intset-entries
配置时被启用。示例:
intset结构:
字段含义:
encoding
:整数表示类型,根据集合内最长整数值确定类型,整数
类型划分为三种:int-16、int-32、int-64。length
:表示集合元素个数。contents
:整数数组,按从小到大顺序保存。
intset保存的整数类型根据长度划分,当保存的整数超出当前类型时,将会触发自动升级操作且升级后不再做回退。因此,使用intset编码的集合时,尽量保持整数范围一致,如都在int-16范围内。防止个别大整数触发集合升级操作,产生内存浪费。
intset数据结构插入命令复杂度为O(n),查询命令为O(log(n)),由于整数占用空间非常小,所以在集合长度可控的基础上,写入命令执行速度也会非常快,因此当使用整数集合时尽量使用intset编码。
对于存储相同的数据内容利用Redis的数据结构降低外层键的数量,也可以节省大量内存。比如:通过在客户端预估键规模,把大量键分组映射到多个hash结构中降低键的数量。
同样的数据使用ziplist编码的hash类型存储比string类型节约内存。当value越小(即小对象)越明显。
hash-ziplist类型比string类型写入耗时,但随着value空间的减少,耗时逐渐降低。
hash类型节省内存的原理是使用ziplist编码,如果使用hashtable编码方式反而会增加内存消耗。
ziplist长度需要控制在1000以内,否则由于存取操作时间复杂度在O(n)到O(n2)之间,否则会导致CPU消耗严重。
ziplist适合存储小对象,对于大对象不但内存优化效果不明显还会增加命令操作耗时。
关于hash键和field键的设计:
- 当键离散度较高时,可以按字符串位截取,把后三位作为哈希的field,之前部分作为哈希的键。如:key=1948480哈希key=group:hash:1948,哈希field=480。
- 当键离散度较低时,可以使用哈希算法打散键,如:使用crc32(key)&10000函数把所有的键映射到“0-9999”整数范围内,哈希field存储键的原始值。
- 尽量减少hash键和field的长度,如使用部分键内容。
要考虑两方面内容:1.Redis的内存淘汰策略
。2.Redis的最大内存设置
。思路:首先计算出20w数据所需的内存空间,设置最大内存(maxmemory
),然后选择合适的内存淘汰策略。
当Redis内存数据集大小上升到一定大小的时候,就会执行内存淘汰策略。示例:
至于最大内存设置,可以对最大内存进行设置。当使用的内存到达指定的限制时,Redis会根据内存淘汰策略删除键,以释放空间。示例:
获取最大内存命令:config get maxmemory
。
社会最大内存命令示例:config set maxmemory 1GB
。
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将Redis当缓存来使用配置淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
可以通过双向链表来实现:
- 通过双向链表来实现,新数据插入到链表头部。
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部。
- 当链表满的时候,将链表尾部的数据丢弃。
Redis是单线程架构,因此阻塞问题是需要非常重视的。导致阻塞问题的原因大致分为:
- 内在原因:不合理地使用API或数据结构、CPU饱和、持久化阻塞等。
- 外在原因:CPU竞争、内存交换、网络问题等。
当Redis阻塞时,应用会收到大量Redis超时异常,比如Jedis客户端会抛出JedisConnectionException异常,这时一般会触发告警,何时触发报警一般根据应用的并发量决定,如1分钟内超过10个异常触发报警。
应该围绕这几个方面去排查:API或数据结构使用不合理;CPU饱和的问题;持久化相关的阻塞。
典型的不合理使用API和数据结构,比如:对一个包含上万个元素的hash结构执行hgetall操作,由于数据量比较大且命令算法复杂度是O(n),这条命令执行速度必然很慢。
slowlog-log-slower-than
和slowlog-max-len
配置慢查询阀值和慢查询记录。slowlog-log-slower-than单位是微秒(1秒=1000毫秒=1000000微秒),默认值是10000。
如果slowlog-log-slower-than=0会记录所有的命令,slowlog-log-slower-than<0对于任何命令都不会进行记录。
Redis使用了一个列表来存储慢查询日志,slowlog-max-len
就是列表的最大长度。一个新的命令满足慢查询条件时被插入到这个列表中,当慢查询日志列表已处于其最大长度时,最早插入的一个命令将从列表中移出,例如slowlog-max-len设置为5,当有第6条慢查询插入的话,那么队头的第一条数据就出列,第6条慢查询就会入列。
总的来说,在Redis中有两种修改配置的方法,一种是修改配置文件,另一种是使用config set命令动态修改。slowlog-log-slower-than
和slowlog-max-len
自然也可以用这两种方式进行修改。
1、获取慢查询日志:slowlog get {n}
,示例:
慢查询日志有4个属性组成,分别是慢查询日志的标识id、发生时间戳、命令耗时、执行命令和参数。
2、获取慢查询日志列表当前的长度:slowlog len
,示例:
3、慢查询日志重置(即清空满查询日志列表):slowlog reset
。
使用慢查询的一些建议:
- 对于
·slowlog-max-len
,并不会占用太大内存,因为Redis会对慢查询中的长命令做截断操作,线上可设置为1000以上。- 对于
slowlog-log-slower-than
,建议设置为1毫秒。- 由于慢查询日志是一个先进先出的队列,也就是说如果慢查询比较多的情况下,可能会丢失部分慢查询命令,为了防止这种情况发生,可以定期执行
slow get
命令将慢查询日志持久化到其他存储中(例如MySQL)。
上面介绍了一些慢查询的简单使用和原理,接下来看下慢查询在项目中的运用。
执行slowlog get {n}
命令可以获取最近的n条慢查询命令,默认对于执行超过10毫秒的命令都会记录到一个定长队列中,线上实例建议设置为1毫秒便于及时发现毫秒级以上的命令。
如果命令执行时间在毫秒级,则实例实际OPS只有1000左右。慢查询队列长度默认128,可适当调大。
发现慢查询后,需要作出及时调整。参考:
修改为低复杂度的命令
,如hgetall改为hmget等,禁用keys、sort等命令。调整大对象
,缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。大对象拆分过程需要视具体的业务决定。
单线程的Redis处理命令时只能使用一个CPU,CPU饱和是指Redis把单核CPU使用率跑到接近100%。使用top
命令可以看出对应Redis进程的CPU使用率。
当出现CPU饱和情况时,可以查看当前Redis的并发量是否达到极限,建议使用命令redis-cli -h {ip} -p {port} --stat
获取当前Redis实时的使用情况,示例:
如果只有几百或几千OPS的Redis实例就接近CPU饱和是很不正常的,可能使用了高算法复杂度的命令
。
对于开启了持久化功能的Redis节点,需要排查是否是持久化导致的阻塞。持久化引起主线程阻塞的操作主要有:fork阻塞、AOF刷盘阻塞、HugePage写操作阻塞。
info stats
命令获取到latest_fork_usec指标,表示Redis最近一次fork操作耗时,如果耗时很大,比如超过1秒,则需要做出优化调整,如避免使用过大的内存实例和规避fork缓慢的操作系统等。示例:info persistence
命令中的aof_delayed_fsync指标,每次发生fdatasync阻塞主线程时会累加。示例:外在原因主要围绕三个方面进行排查:CPU竞争、内存交换、网络问题。
进程竞争
:Redis是典型的CPU密集型应用。当其他进程过度消耗CPU时,将严重影响Redis吞吐量。可以通过top等命令定位到CPU消耗的时间点和具体进程,这个问题需要调整服务之间部署结构。
绑定CPU
:部署Redis时为了充分利用多核CPU,通常一台机器部署多个实例。常见的一种优化是把Redis进程绑定到CPU上,用于降低CPU频繁上下文切换的开销。这种优化存在意外情况:
当Redis父进程创建子进程进行RDB/AOF重写时,如果做了CPU绑定,会与父进程共享使用一个CPU。子进程重写时对单核CPU使用率通常在90%以上,父进程与子进程将产生激烈CPU竞争,极大影响Redis稳定性。因此对于开启了持久化或参与复制的主节点不建议绑定CPU。
如果操作系统把Redis使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的Redis性能急剧下降。
识别Redis内存交换的检查方法:
1、保证机器充足的可用内存。
2、确保所有Redis实例设置最大可用内存(maxmemory),防止极端情况
下Redis内存不可控的增长。
3、降低系统使用swap优先级,如echo10>/proc/sys/vm/swappiness.
网络问题经常是引起Redis阻塞的问题点。常见的网络问题主要有:连接拒绝、网络延迟、网卡软中断等。
info stats
命令查看,示例:echo511>/proc/sys/net/core/somaxconn
命令进行修改。可以通过netstat-s
命令获取因backlog队列溢出造成的连接拒绝统计,示例:redis-cli -h {host} -p {port}
命令后面加入如下参数进行延迟测试:1、
--latency
:持续进行延迟测试,分别统计:最小值、最大值、平均值、采样次数。
2、--latency-history
:统计结果同–latency,默认每15秒完成一行统计。
3、--latency-dist
:使用统计图的形式展示延迟统计,每1秒采样一次。
网络延迟问题经常出现在跨机房的部署结构上。
带宽瓶颈通常出现在以下几个方面:机器网卡带宽、机架交换机带宽、机房之间专线带宽。
带宽占用主要根据当时使用率是否达到瓶颈有关,如频繁操作Redis的大对象对于千兆网卡的机器很容易达到网卡瓶颈。