Redis运维--性能优化、故障处理

517 阅读14分钟

Redis 运维的故障与处理方法

Redis是一个纯内存的Key-Value型数据库,可以提供高并发和低延时的服务,生产上Redis的性能将会直接影响到服务的质量

Redis常见运维故障

  1. 使用 keys* 把库堵死。——建议使用别名把这个命令改名。
  2. 超过内存使用后,部分数据被删除。——这个有删除策略的,选择适合自己的即可。
  3. 没开持久化,却重启了实例,数据全掉。——记得非缓存的信息需要打开持久化。
  4. RDB的持久化需要 Vm.overcommit_memory=1 ,否则会持久化失败。
  5. 没有持久化情况下,主从,主重启太快,从还没认为主挂的情况下,从会清空自己的数据,人为重启主节点前,先关闭从节点的同步。

redis故障常见问题详解

1、尽量使用短的key
当然在精简的同时,不要为了key的“见名知意”。对于value有些也可精简,比如性别使用0、1。

2、避免使用keys
keys *, 这个命令是阻塞的,即操作执行期间,其它任何命令在你的实例中都无法执行。当redis中key数据量小时到无所谓,数据量大就很糟糕了。所以我们应该避免去使用这个命令。可以去使用SCAN,来代替。

3、在存到Redis之前先把你的数据压缩下
redis为每种数据类型都提供了两种内部编码方式,在不同的情况下redis会自动调整合适的编码方式。

4、设置key有效期
我们应该尽可能的利用key有效期。比如一些临时数据(短信校验码),过了有效期Redis就会自动为你清除!

5、选择回收策略(maxmemory-policy)
当Redis的实例空间被填满了之后,将会尝试回收一部分key。根据你的使用方式,强烈建议使用 volatile-lru(默认) 策略——前提是你对key已经设置了超时。但如果你运行的是一些类似于 cache 的东西,并且没有对 key 设置超时机制,可以考虑使用 allkeys-lru 回收机制,具体讲解查看 。maxmemory-samples 3 是说每次进行淘汰的时候 会随机抽取3个key 从里面淘汰最不经常使用的(默认选项)。
maxmemory-policy 六种方式 :``volatile``-lru:只对设置了过期时间的key进行LRU(默认值)``allkeys-lru : 是从所有key里 删除 不经常使用的key``volatile``-random:随机删除即将过期key``allkeys-random:随机删除``volatile``-ttl : 删除即将过期的``noeviction : 永不过期,返回错误

6、使用bit位级别操作和byte字节级别操作来减少不必要的内存使用
bit位级别操作:GETRANGE, SETRANGE, GETBIT and SETBIT``byte``字节级别操作:GETRANGE and SETRANGE

7、尽可能地使用hashes哈希存储

8、当业务场景不需要数据持久化时,关闭所有的持久化方式可以获得最佳的性能
数据持久化时需要在持久化和延迟/性能之间做相应的权衡.

9、想要一次添加多条数据的时候可以使用管道

10、限制redis的内存大小(64位系统不限制内存,32位系统默认最多使用3GB内存) 
数据量不可预估,并且内存也有限的话,尽量限制下redis使用的内存大小,这样可以避免redis使用swap分区或者出现OOM错误。(使用swap分区,性能较低,如果限制了内存,当到达指定内存之后就不能添加数据了,否则会报OOM错误。可以设置maxmemory-policy,内存不足时删除数据) 过期键的删除策略

11、Redis的过期键的删除策略
我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。过期策略通常有以下三种:

  • 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)Redis中同时使用了惰性过期和定期过期两种过期策略。

12、Redis key的过期时间和永久有效分别怎么设置?
EXPIRE和PERSIST命令。

13、我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

  • 定时去清理过期的缓存;
  • 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

Redis监控参数

  Redis可以通过info all命令的的输出获取相关性能指标。可以分为以下十类:

  • server(基本信息)
  • clients(连接信息)
  • memory(内存信息)
  • persistence(持久化信息)
  • stats(命令等状态信息)
  • replication(复制信息)
  • cpu(CPU相关信息)
  • cluster(集群信息)
  • keyspace(键空间信息)
  • commandstats(调用命令相关信息)

1.1 Server

uptime_in_days:从Redis server 启动到现在的天数

1.2 Clients

connected_clients:已连接客户端的数量 (不包括从服务器)
blocked_clients:正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量

1.3 Memory

used_memory:已使用的内存(不包含内存碎片)
used_memory_rss:已使用的内存(包含内存碎片)
used_memory_peak:过去Redis内存使用的峰值
maxmemory:最大内存
通过used_memory/maxmemory可以计算出内存使用率,未设置淘汰策略(maxmemory_policy)时,达到maxmemory限制不能写入数据。

1.4 Persistence

rdb_last_bgsave_status/aof_last_write_statu/aof_last_bgrewrite_statuss:最后一次持久化/AOF重写状态
aof_pending_bio_fsync:后台IO队列中等待fsync的任务数
aof_current_size:AOF当前文件大小

1.5 Stats

total_commands_processed:服务器已执行的命令数量
instantaneous_ops_per_sec:每秒执行命令数
rejected_connections:因为最大客户端数量限制而被拒绝的连接请求数量
expired_keys:过期的数据库键数量
evicted_keys:因最大内存容量限制而被驱逐的键数量
keyspace_hits:键空间命中数
keyspace_misses:键空间未命中数
total_net_input_bytes:网络流入总流量
total_net_output_bytes:网络流出总流量

1.6 Replication

master_link_status:主从连接状态
master_repl_offset:主从间这个变量的差距代表延迟的偏移量

1.7 CPU

used_cpu_sys:Redis服务器耗费的系统CPU
used_cpu_user:Redis服务器耗费的用户CPU
used_cpu_sys_children:Redis后台进程耗费的系统CPU
used_cpu_user_children:Redis后台进程耗费的用户CPU

1.8 Cluster

cluster_enabled:是否使用集群

1.9 Keyspace

dbx:keys=xxx,expires=x,avg_ttl=x:数据库的键数量、带有过期时间的键的数量,存活的时间计数

1.10 Commandstats

# Commandstats (执行命令的次数,执行命令所耗费的毫秒数)
cmdstat_replconf:calls=175272,usec=335561,usec_per_call=1.91
cmdstat_command:calls=6,usec=5579,usec_per_call=929.83
cmdstat_set:calls=2117489,usec=8755492,usec_per_call=4.13
cmdstat_auth:calls=417,usec=809,usec_per_call=1.94
cmdstat_zrangebyscore:calls=174,usec=2521,usec_per_call=14.49
cmdstat_latency:calls=1210,usec=2403,usec_per_call=1.99
cmdstat_bgrewriteaof:calls=1,usec=1901,usec_per_call=1901.00
cmdstat_zadd:calls=26908,usec=573861,usec_per_call=21.33
cmdstat_host::calls=16,usec=398,usec_per_call=24.88
cmdstat_zremrangebyscore:calls=2454,usec=14800,usec_per_call=6.03
cmdstat_slowlog:calls=2420,usec=10422,usec_per_call=4.31
cmdstat_info:calls=90152,usec=7952483,usec_per_call=88.21
cmdstat_psync:calls=1,usec=2465,usec_per_call=2465.00
cmdstat_config:calls=1216,usec=101234,usec_per_call=83.25

Redis的参数调优问题,根据场景不同,有不同的调优方法,以下列举一些参数调优。场景的海量数据,高并发。

(1) timeout参数,非常需要关注,官方对timeout的解释如下:该参数表示当某一个客户端连接上来并闲置timeout (单位秒)的时间后,Redis服务端就主动关闭这个客户端连接。我们发现如果客户端对连接处理比较差的时候,存在连接不释放的问题,导致连接池耗尽,单个redis默认的连接数是1000.所以需要在timeout参数做文章,强制释放无效连接,我们的参数调整为timeout30000

(2)持久化参数, RDB和AOF究竟开哪个,很多人很纠结。其实选择很简单,两者的区别是持久化的颗粒度不一样,如果你关注数据的强一致性,选择AOF,如果你选择更好的性能,选型RDB。如果你选择极致的性能,又能容忍数据的丢失,那你可以完全不用开启持久化,当然了,集群是一定要开持久化的。

(3) tcp—backlog和maxclient,这两个参数可能大家也比较迷糊, maxclient模式10000,一般情况下是够了,但是在高并发海量访问的时候,还是会出现客户端缓慢的情况,那就是系统的限制,就是backlog参数,你可以理解为在三次握手时进入acceptqueue队列的最大值,也就是send_Q,所以这个值设大点是没有坏处的,我们的设置是1024

(4)重点提一下安全的参数, requirepass foobared个人认为,如果你们觉得你们hold住,那就不用开,如果在DMZ区,数据又比较重要,那就开。个人认为,密码的作用不是你想象中的带给你安全,第一redis一分钟可以访问你想象不到的次数,如果弱密码,分分钟破解,第二auth命令是明文的,破解也很容易。

(5) maxmemory,这个参数比较常见,但是也非常的坑。如果你选择用,有个原则,你是当数据库用还是当缓存用,如果当数据库用,就不要开,如果当缓存用,可以开。曾经有个BUG,多个salve的情况下,会导致擦除主节点的数据。如果你开启这个参数,记得预留一些空间给系统的buffer。

Redis故障排查

  1. 结合Redis 监控查看QPS、缓存命中率、内存使用率等信息。
  2. 确认机器层面的资源是否有异常。
  3. 故障时及时上机,使用 redis-cli monitor 打印出操作日志,然后分析(事后分析此条失效)。
  4. 和研发沟通,确认是否有大Key在堵塞(大Key也可以在日常的巡检中获得) 和组内同事沟通,确实是否有误操作。
  5. 和运维同事、研发一起排查流量是否正常,是否存在被刷的情况。

redis常见问题详解

慢查询

  通常来讲,时间复杂度是O(1)的命令是安全的,时间复杂度是O(N)命令在使用时需要注意,避免在使用这些O(N)命令时发生问题主要有几个办法:

  • 不要把List当做列表使用,仅当做队列来使用。
  • 通过机制严格控制Hash、Set、Sorted Set的大小。
  • 可能的话,将排序、并集、交集等操作放在客户端执行。
  • 绝对禁止使用KEYS命令。
  • 避免一次性遍历集合类型的所有成员,而应使用SCAN类的命令进行分批的,游标式的遍历。

网络延迟

  • 尽可能使用长连接或连接池,避免频繁创建销毁连接。
  • 客户端进行的批量数据操作,应使用管道完成。

数据持久化引发的延迟

  • AOF + fsync always的设置虽然能够绝对确保数据安全,但每个操作都会触发一次fsync,会对Redis的性能有比较明显的影响。
  • AOF + fsync every second是比较好的折中方案,每秒fsync一次。
  • AOF + fsync never会提供AOF持久化方案下的最优性能。
  • 使用RDB持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置。
  • 每一次RDB快照和AOF Rewrite都需要Redis主进程进行fork操作。fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟。

大对象和大键/键个数和长度的突增

  • 尽量只用小的KEY,并且KEY要有可读性,严格控制Value的大小。

数据淘汰引发的延迟

  • 当同一秒内有大量key过期时,也会引发Redis的延迟。在使用时应尽量将key的失效时间错开。

引入读写分离机制

  Redis的主从复制能力可以实现一主多从的多节点架构,在这一架构下,主节点接收所有写请求,并将数据同步给多个从节点。在这一基础上,可以让从节点提供对实时性要求不高的读请求服务,以减小主节点的压力。尤其是针对一些使用了长耗时命令的统计类任务,完全可以指定在一个或多个从节点上执行,避免这些长耗时命令影响其他请求的响应。

在遇到 Redis 性能变慢的时候能高效解决问题。

  1. 获取当前 Redis 的基线性能;
  2. 开启慢指令监控,定位慢指令导致的问题;
  3. 找到慢指令,使用 scan 的方式;
  4. 将实例的数据大小控制在 2-4GB,避免主从复制加载过大 RDB 文件而阻塞;
  5. 禁用内存大页,采用了内存大页,生成 RDB 期间,即使客户端修改的数据只有 50B 的数据,Redis 需要复制 2MB 的大页。当写的指令比较多的时候就会导致大量的拷贝,导致性能变慢。
  6. Redis 使用的内存是否过大导致 swap;
  7. AOF 配置是否合理,可以将配置项 no-appendfsync-on-rewrite 设置为 yes,避免 AOF 重写和 fsync 竞争磁盘 IO 资源,导致 Redis 延迟增加。
  8. bigkey 会带来一系列问题,我们需要进行拆分防止出现 bigkey,并通过 UNLINK 异步删除。

Redis的基础优化有哪些

  1. 设置Redis客户端连接的超时时间
  2. 设置 redis客户端最大连接数
  3. 设置redis自动碎片清理
  4. 设置redis最大内存阈值
  5. 设置key回收策略
  6. 开启 AOF 持久化
  7. 设置 config set activedefrag yes 开启内存碎片自动清理,或者 定时执行 memory purge 清理内存碎片
  8. 设置 内存淘汰策略 maxmemory-policy 实现保证内存使用率不超过系统最大内存
  9. 尽可能使用 Hash 数据类型存储数据,如果 Hash 中包含很少的字段,那么该类型的数据也将仅占用很少的空间
  10. 设置 key 的过期时间,精简键名 和 键值,控制键值的大小
  11. 设置 config set requirepass 开启密码验证
  12. 合理设置 maxclient 最大连接数参数(10000),tcp-backlog 连接排队数(1024), timeout 连接超时时间(30000)