前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Redis性能测试小结

Redis性能测试小结

原创
作者头像
后台技术汇
发布2024-11-17 22:53:07
发布2024-11-17 22:53:07
9800
代码可运行
举报
文章被收录于专栏:后台技术汇后台技术汇
运行总次数:0
代码可运行

“好事”文章分享

开始之前,我们一起学习一下,社区其他小伙伴的文章:《Linux系统之iotop命令的基本使用》

文章名:《Linux系统之iotop命令的基本使用》 作者:江湖有缘

评价:

iotop是Linux下的实时磁盘I/O监控工具,类似于top,但专注于显示进程的磁盘读写情况。需root权限运行,通过命令行参数可定制显示内容和行为,有助于诊断磁盘I/O瓶颈。结合其他监控工具,提供全面的系统性能视图

前言

我们简单回顾一下Redis性能的相关概念。

Redis集群部署

可以参考以下几篇文章:

  • 主从模式

深刻理解Redis集群(中):Redis主从数据同步模式

  • 哨兵模式

深刻理解Redis集群(下):Redis 哨兵(Sentinel)模式

  • AOF&RDB同步数据

深刻理解Redis集群(上):RDB快照和AOF日志

Redis监控工具Media

使用说明

(1)启动程序后填写 Redis 服务器信息进行连接:

(2)连接后便可以对键和键值进行增删改查操作:

(3)Medis 还支持直接执行终端命令:

Redis性能配置参数

要开启Redis的慢查询日志功能,你需要修改Redis的配置文件(通常是redis.conf),并进行以下配置:

配置慢查询日志

修改redis.conf文件:

  • 设置慢查询的时间阈值:
    1. 使用slowlog-log-slower-than参数来设置命令执行时间的阈值(单位为微秒)。例如,设置为10000微秒(即10毫秒):
代码语言:txt
复制
slowlog-log-slower-than 10000
  • 设置慢查询日志的最大长度:
    1. 使用slowlog-max-len参数来设置慢查询日志的最大条目数。例如,设置为128条:
代码语言:javascript
代码运行次数:0
复制
slowlog-max-len 128
  • 保存配置文件的更改后,重启Redis服务器以使配置生效
  • 设置最大内存值:(为了方便测试性能)
代码语言:javascript
代码运行次数:0
复制
maxmemory 21mb
  • 设置数据淘汰策略
代码语言:javascript
代码运行次数:0
复制
maxmemory-policy allkeys-lru
缓存数据淘汰策略

在Redis的redis.conf文件里,maxmemory-policy配置项用于设置当达到最大内存限制时,Redis如何选择要移除的数据。以下是各个淘汰策略的详细说明:

淘汰策略选项
  • volatile-lru:使用近似LRU算法淘汰设置了过期时间的键。
  • allkeys-lru:使用近似LRU算法淘汰任意键。
  • volatile-lfu:使用近似LFU算法淘汰设置了过期时间的键。
  • allkeys-lfu:使用近似LFU算法淘汰任意键。
  • volatile-random:随机淘汰设置了过期时间的键。
  • allkeys-random:随机淘汰任意键。
  • volatile-ttl:淘汰剩余过期时间最短的键。
  • noeviction:不淘汰任何数据,当内存不足时返回错误。
策略解释
  • LRU (Least Recently Used):最近最少使用,优先淘汰最长时间未被访问的键。
  • LFU (Least Frequently Used):最不经常使用,优先淘汰访问频率最低的键。
  • TTL (Time To Live):剩余生存时间,优先淘汰即将过期的键。
配置示例

redis.conf文件中,你可以设置如下配置:

代码语言:javascript
代码运行次数:0
复制
maxmemory-policy allkeys-lru

这表示当Redis达到最大内存限制时,将使用近似LRU算法淘汰任意键。

注意事项
  • 这些淘汰策略都是基于近似算法实现的,可能会有一定的误差。
  • 当没有合适的键可以淘汰时,Redis会在需要更多内存的写操作上返回错误。
  • 默认策略是noeviction,即不淘汰任何数据,当内存不足时返回错误。

通过合理配置maxmemory-policy,可以确保Redis在高负载情况下仍能稳定运行,并根据应用需求优化内存使用。

Redis性能压测demo

并发大字段写入读取压测
代码语言:txt
复制
package com.bryant.controller.redis;

import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Random;

@RestController
@RequestMapping("/redis/performance")
public class PerformanceController {

    @Resource(name = "userRedisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    private static final int _1MB = 1024 * 1024;
    private static final int _100MB = 1024 * 1024 * 100;

    @PostMapping("/write2Cache")
    public void write(
            @RequestParam(value = "id", defaultValue = "0") int id,
            @RequestParam(value = "isBigObject", defaultValue = "false") boolean isBigObject) {
        for (int i = id; i < 100; i++) {
            byte[] _1M = new byte[_100MB];
            BigObject bigObject = new BigObject(_1M);
            String key = "1M:" + i;
            if (isBigObject) {
                redisTemplate.opsForValue().set(key, bigObject);
            } else {
                redisTemplate.opsForValue().set(key, bigObject.toString());
            }
        }
    }

    @GetMapping("/readFromCache")
    public void read() {
        for (int i = 0; i < 100; i++) {
            redisTemplate.opsForValue().get("1M:" + i);
        }
    }

}

class BigObject implements Serializable {

    private static final long serialVersionUID = 589939600983368050L;

    private byte[] a;

    public BigObject(byte[] a) {
        this.a = a;
    }

    public byte[] getA() {
        return a;
    }

    public void setA(byte[] a) {
        this.a = a;
    }

}

keys指令和scan指令

(1)keys指令扫描容易引发性能问题

阻塞操作

  • KEYS 命令是一个阻塞操作,它会阻塞 Redis 服务器直到命令执行完毕。这意味着在 KEYS 命令执行期间,Redis 无法处理其他客户端的请求,导致服务不可用

时间复杂度

  • KEYS 命令的时间复杂度是 O(N),其中 N 是数据库中的 key 数量。如果数据库中有大量的 key,KEYS 命令的执行时间会非常长。

内存消耗

  • KEYS 命令会将所有的 key 加载到内存中,这可能导致内存消耗过大,甚至引发内存溢出问题。

(2)scan指令使用不当,也容易引发性能问题

SCAN 命令的基本语法如下:

代码语言:javascript
代码运行次数:0
复制
SCAN cursor [MATCH pattern] [COUNT count]
  • cursor:游标的初始值为 0,表示开始遍历。每次调用 SCAN 命令后,会返回一个新的游标值,用于下一次迭代。
  • MATCH pattern:可选参数,用于指定匹配的模式,类似于 KEYS 命令中的模式匹配功能。
  • COUNT count:可选参数,用于指定每次迭代返回的 key 的数量。这个值只是一个建议,实际返回的数量可能会有所不同。

代码语言:javascript
代码运行次数:0
复制
scan 命令 时间复杂度为 O(n)
理论上这个O(n) 很多的是靠 count来进行指定. 
并且理论上一个键值对的消耗时间为 1 微秒左右. 

虽然他可以在 count 值的微秒数之内返回, 
但是如果要遍历所有的键值对, 并且多个client同时遍历
那么时间损耗是非常恐怖的. 

又因为redis是单线程io复用的模型. 所以他会导致其他的核心业务卡断. 

如果非必要建议不要使用scan命令, 在高并发,大量key的场景下性能表现并不好.

(3)对keys扫描优化

当使用到keys和scan,其本质就是对缓存的key进行遍历+操作(删除等),我们可以从下面几个方面入手:

  1. 提高查询性能
    1. 不通过keys或者scan的逐一遍历,而是通过哈希结构精确定位到需要的key值;哈希表在存储和查询时更加高效。
  2. 使用集合(Sets)或有序集合(Sorted Sets)
    1. 对存储的key,如果需要存储唯一值或需要排序的场景,可以使用集合(Sets)或有序集合(Sorted Sets)数据结构。
  3. 使用 Lua 脚本
    1. 由于keys或者scan的遍历和操作,都会触发客户端和服务端的大量CPU性能消耗,那么不如用Lua脚本,Lua 脚本在 Redis 中是原子执行的,可以减少网络开销、减少客户端的非必要开销,并提高服务端执行指令的性能。
  4. 使用 Redis 集群
    1. Redis 集群可以将数据分布在多个节点上,提高查询性能和扩展性。

缓存数据存储优化
  1. 所有的键值对除非有特殊需求,否则一律加上超时时间,避免长期驻留内存
  2. keys和scan慎重使用

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • “好事”文章分享
  • 前言
    • Redis集群部署
    • Redis监控工具Media
      • 使用说明
    • Redis性能配置参数
      • 配置慢查询日志
      • 缓存数据淘汰策略
      • 淘汰策略选项
      • 策略解释
      • 配置示例
      • 注意事项
    • Redis性能压测demo
      • 并发大字段写入读取压测
      • keys指令和scan指令
      • 缓存数据存储优化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档