1. 如何查看Redis性能
info命令输出的数据可以分为10个分类,分别是:
server,clients,memory,persistence,stats,replication,cpu,commandstats,cluster,keyspace
为了快速定位并解决性能问题,我们选取其中几个作为参考:
redis:6379> info memory "# Memory used_memory:31590440 used_memory_human:30.13M used_memory_rss:38260736 used_memory_rss_human:36.49M used_memory_peak:31625024 used_memory_peak_human:30.16M used_memory_peak_perc:99.89% used_memory_overhead:9672728 used_memory_startup:862392 used_memory_dataset:21917712 used_memory_dataset_perc:71.33% allocator_allocated:31686224 allocator_active:32690176 allocator_resident:35946496 total_system_memory:16585785344 total_system_memory_human:15.45G used_memory_lua:37888 used_memory_vm_eval:37888 used_memory_lua_human:37.00K used_memory_scripts_eval:0 number_of_cached_scripts:0 number_of_functions:0 number_of_libraries:0 used_memory_vm_functions:37888 used_memory_vm_total:75776 used_memory_vm_total_human:74.00K used_memory_functions:184 used_memory_scripts:184 used_memory_scripts_human:184B maxmemory:4294967296 maxmemory_human:4.00G maxmemory_policy:allkeys-lru allocator_frag_ratio:1.03 allocator_frag_bytes:1003952 allocator_rss_ratio:1.10 allocator_rss_bytes:3256320 rss_overhead_ratio:1.06 rss_overhead_bytes:2314240 mem_fragmentation_ratio:1.21 mem_fragmentation_bytes:6650024 mem_not_counted_for_evict:0 mem_replication_backlog:0 mem_total_replication_buffers:0 mem_clients_slaves:0 mem_clients_normal:24072 mem_cluster_links:0 mem_aof_buffer:0 mem_allocator:jemalloc-5.2.1 active_defrag_running:0 lazyfree_pending_objects:0 lazyfreed_objects:0 "
used_memory是Redis使用的内存总量,包含了实际缓存占用的内存和Redis自身运行所占用的内存。used_memory_rss:从操作系统上显示已经分配的内存总量。maxmemory最大内存容量。
1.1 内存交换引起的性能问题
如果Redis使用的内存超过了最大可用内存,那么操作系统开始进行内存与swap空间交换,这时Redis和依赖Redis上数据的应用会受到严重的性能影响,所以通过查看used_memory指标可知道Redis的性能情况;
运行过程中触发RDB功能时,Redis会fork一个子进程把当前内存中的数据完全复制一份写入到硬盘上,若当前使用内存超过可用内存的45%,就会触发内存交换,大量频繁的更新操作,可能会加剧性能下降。
1.2 命令处理数
redis:6379> info stats "# Stats total_connections_received:395 total_commands_processed:530848 instantaneous_ops_per_sec:3 total_net_input_bytes:41630627 total_net_output_bytes:12904524 instantaneous_input_kbps:0.22 instantaneous_output_kbps:0.02 rejected_connections:0 total_reads_processed:531868 total_writes_processed:531477 io_threaded_reads_processed:0 io_threaded_writes_processed:0 reply_buffer_shrinks:1729 reply_buffer_expands:1356 "
Redis是单线程模型,客户端发送过来的命令会按照顺序执行,通过total_connections_received获取命令数量,如果命令处理总数处于上升或下降状态,那么可能是有2个原因引起的:
- 命令队列里的命令数量过多,后面命令一直在等待中
- 前面慢命令阻塞Redis
解决办法有:
- 通过单命令多参数的形式取代多命令单参数的形式,减少命令发送的数量,页减小网络交互;
- 管道命令,把几个命令合并一起执行,减少因网络开销引起的延迟问题;
- 避免操作大集合的慢命令;
1.3 延迟时间
Redis的延迟数据是无法从info信息中获取的,可以用 Redis-cli工具加 --latency参数运行。
redis-cli -h 127.0.0.1 -p 6379 --latency
该命令会统计三个延迟指标:最小值(min),最大值(max)和平均值(avg),单位是ms。如果发现延迟较大,有一些方法可以定位延迟出现的情况:
- 确保没有执行慢指令(慢指令会导致Redis服务阻塞),可以使用 slowlog 查看,具体:https://redis.io/commands/slowlog
- 关闭内核Transparent huge pages:echo never > /sys/kernel/mm/transparent_hugepage/enabled(需要重启Redis服务)
- 测试固有延迟(如果固有延迟已经较大,可能是操作系统负载较高或性能不佳)
- 使用Redis的Latency Monitoring功能监测延迟情况,具体:https://redis.io/topics/latency-monitor
1.4 客户端连接数
redis:6379> info clients "# Clients connected_clients:2 cluster_connections:0 maxclients:10000 client_recent_max_input_buffer:20480 client_recent_max_output_buffer:0 blocked_clients:0 tracking_clients:0 clients_in_timeout_table:0 "
Redis默认允许客户端连接的最大数量是10000,但是连接数过大会对性能影响较大;自Redis2.6以后,允许在配置文件(Redis.conf)maxclients属性上修改客户端连接的最大数,也可以通过在Redis-cli工具上输入config set maxclients 去设置最大连接数。
1.5 内存碎片率
在info中通过mem_fragmentation_ratio = used_memory_rss/used_memory,
如果内存碎片率超过1.5,那可能是操作系统或Redis实例中内存管理变差的表现。解决方法:
- 重启Redis服务器:额外碎片的产生是由于Redis释放了内存块,但内存分配器并没有返回内存给操作系统,这个内存分配器是在编译时指定的,可以是libc、jemalloc或者tcmalloc。重启Redis服务器可以让额外产生的内存碎片失效并重新作为新内存来使用。
- 限制内存交换: 如果内存碎片率低于1,Redis实例可能会把部分数据交换到硬盘上。内存交换会严重影响Redis的性能,所以应该增加可用物理内存或减少实Redis内存占用。
- 修改内存分配器:Redis支持glibc’s malloc、jemalloc11、tcmalloc几种不同的内存分配器,每个分配器在内存分配和碎片上都有不同的实现。不建议修改Redis默认内存分配器。
2. 缓存应用优化
2.1 缓存穿透
缓存穿透是指查询缓存中一个不存在的数据,请求直接访问到数据库服务。大量的恶意请求会导致存储层压力过大而崩溃。解决方案:
- 缓存空对象:第一次请求缓存和DB都没有,就存个空对象到缓存里面。但如果大批量的恶意请求过来,这样做就会导致缓存的key暴增,显然不是一个很好的方案。
- 布隆过滤器:请求通过布隆过滤器过滤,当布隆过滤器说某个值存在时,这个值可能不存在;但是它说不存在时,那就肯定不存在。
2.2 缓存失效
大批量的key同时失效,导致大量请求同时打向数据库,造成数据库压力过大,甚至直接挂掉。解决方案是失效时间随机化,可以是一个固定时间+随机时间方式来生成,更加保险的方案可以加分布式锁,拿到锁的才去访问数据库。
2.3 缓存雪崩
缓存雪崩是指缓存层挂掉之后,所有请求都打向数据库,数据库扛不住挂掉,导致对应的服务也挂掉,从而影响整个链路服务雪崩挂掉。解决方案:
- 保证缓存层的高可用性,可参考:Redis高可用方案_西木风落的博客-CSDN博客。
- 服务做限流、熔断,降级等,避免雪崩出现,比如Hystrix.
2.4 缓存一致性
引入缓存后,并发请求可能导致缓存中的数据就会与db数据不一致。如果先更新缓存,然后更新DB失败,那么下一个请求过来读取的缓存数据不是最新的。如果先更新db再更新缓存,更新DB时的请求读取数据是缓存的旧值。
保障缓存一致性可采用方案:加锁——淘汰缓存——更新DB——重新刷进缓存
3. Redis使用优化
3.1 消除bigKey
Redis中一个字符串最大512MB,一个二级数据结构(如hash、list、set、zset)可以存储大约40亿个(2^32-1)个元素,但实际中如果满足下面两种情况,就认为它是bigkey。
- 单个key对应的value超过10kb就是bigkey。
- 非字符串类型:哈希、列表、集合、有序集合,元素个数太多。
bigkey的坏处:
- 占用带宽大,容易造成网络堵塞。假设一个key的value大小为1M,有1000个连接并发,1s所占用的带宽就是1000M,而千兆网卡是 128M/s。
- redis命令堵塞,单线程处理bigkey有可能造成其他连接排队。
一般来说,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000(超过5000了可以采用分拆思想)
3.2 优化的一些建议
- 尽量使用短的key:短key减少网络消耗,对于value有些也可精简,比如性别使用0、1。
- 避免使用keys *: keys *命令是阻塞的,数据量大会严重影响性能,可以使用SCAN代替。
- Redis数据存储压缩:redis为每种数据类型都提供了两种内部编码方式,在不同的情况下redis会自动调整合适的编码方式。
- 设置key有效期:失效key及时清除,减小内存消耗占用
- 选择合适回收策略:Redis 基本知识_西木风落的博客-CSDN博客
- 使用管道替代多次命令交互;
- SLOWLOG优化
- 配置合适的持久化方案
4. 服务器配置优化
- 修改linux中TCP监听的最大容纳数量
在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。注意Linux内核默默地将这个值减小到/proc/sys/net/core/somaxconn
的值,所以需要确认增大somaxconn和tcp_max_syn_backlog两个值来达到想要的效果。
- 修改linux内核内存分配策略
redis在备份数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是一样的,比如parent占用的内存为8G,这个时候也要同样分配8G的内存给child,如果内存无法负担,往往会造成redis服务器的down机或者IO负载过高,效率下降。所以内存分配策略应该设置为 1(表示内核允许分配所有的物理内存,而不管当前的内存状态如何)。
内存分配策略有三种
可选值:0、1、2。
0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1, 不管需要多少内存,都允许申请。
2, 只允许分配物理内存和交换内存的大小(交换内存一般是物理内存的一半)。
- 修改Transparent Huge Pages(THP)的相关配置
Linux kernel 在2.6.38内核增加了 THP 特性,支持大内存页(2MB)分配,默认开启。当开启时可以降低 fork 子进程的速度,但 fork 操作之后,每个内存页从原来 4KB 变为 2MB,会大幅增加重写期间父进程内存消耗。同时每次写命令引起的复制内存页单位放大了512倍,会拖慢写操作的执行时间,导致大量写操作慢查询。
参考连接:https://blog.csdn.net/qq_39399966/article/details/101211939