Redis怎么测,我哪里知道!

性能测试角度

缓存增加/更新功能是否正确,查看缓存数据是否正确

  1. 验证缓存增加/更新功能是否正确:

    在测试中,首先设置一个测试的键值对(Key-Value pair),并将其存入缓存中。然后,使用获取数据的函数或命令从缓存中获取该键对应的值。 验证获取到的值与预期的值是否相同,以确认缓存增加/更新功能是否正确

  2. 使用后门接口工具:使用 Redis 命令行客户端工具例如 redis-cli,直接访问 Redis 缓存并执行相关命令来验证数据的正确性。

  3. 使用命令行:通过登录 Redis 并使用命令行工具,例如使用 GET 命令来直接查看缓存数据的正确性,比如 GET key_name

举例:使用 Redis 命令行客户端工具 redis-cli 进行验证。

$ redis-cli
127.0.0.1:6379> SET key_name value
OK
127.0.0.1:6379> GET key_name
"value"

在此示例中,我们使用了 SET 命令将键 key_name 和相应的值 value 存入 Redis 缓存中。然后使用了 GET 命令来获取该键对应的值,返回结果为 "value",与预期的值一致。

查看缓存数据的正确性:

  • 使用缓存提供的监控工具或命令,如 Redis 的 INFO 命令,来获取有关缓存状态和数据的信息。

  • 验证相关的键值对是否存在,并检查其值是否正确。

举例:使用 Redis 的 INFO 命令查看键值对的详细信息。

$ redis-cli
127.0.0.1:6379> INFO

该命令将返回 Redis 缓存的详细信息,包括键值对的数量、缓存使用情况等。通过检查相关键是否存在,并验证其值与预期是否相同,可以确认缓存数据的正确性。

# Server
redis_version:3.2.3                  # Redis 的版本
redis_git_sha1:00000000              # Redis 的版本
redis_git_dirty:0
redis_build_id:9e93d0c7997bcfef
redis_mode:standalone                # 运行模式:单机(集群)
os:Linux 2.6.32-431.el6.x86_64 x86_64 # 操作系统
arch_bits:64                          # 操作系统位数
multiplexing_api:epoll               # redis所使用的事件处理机制
gcc_version:4.4.7                    # gcc版本号
process_id:1606                      # 当前 Redis 服务器进程id
run_id:17e79b1966f1f891eff203a8e496151ee8a3a7a7
tcp_port:7001                        # 端口号
uptime_in_seconds:4360189            # 运行时间(秒)
uptime_in_days:50                    # 运行时间(天)
hz:10                                # redis内部调度(进行关闭timeout的客户端,删除过期key等等)频率,程序规定serverCron每秒运行10次。
lru_clock:5070330                    # Redis的逻辑时钟
executable:/usr/local/bin/redis-server          # 启动脚本路径
config_file:/opt/redis3/conf/redis_7001.conf    # 启动指定的配置文件路径

# Clients
connected_clients:660                # 连接的客户端数量
client_longest_output_list:0         # 当前连接的客户端当中,最长的输出列表
client_biggest_input_buf:0           # 当前连接的客户端当中,最大输入缓存
blocked_clients:0                    # 阻塞的客户端数量

# Memory
used_memory:945408832               # 使用内存(B)
used_memory_human:901.61M           # 使用内存(MB)  
used_memory_rss:1148919808          # 系统给redis分配的内存(即常驻内存),这个值和top命令的输出一致
used_memory_rss_human:1.07G
used_memory_peak:1162079480         # 内存使用的峰值
used_memory_peak_human:1.08G        
total_system_memory:6136483840      # 整个系统内存
total_system_memory_human:5.72G
used_memory_lua:122880              # Lua脚本存储占用的内存
used_memory_lua_human:120.00K       
maxmemory:2147483648                # Redis实例的最大内存配置
maxmemory_human:2.00G
maxmemory_policy:allkeys-lru        # 当达到maxmemory时的淘汰策略
mem_fragmentation_ratio:1.22        # used_memory_rss/used_memory的比例。一般情况下,used_memory_rss略高于used_memory,当内存碎片较多时,则mem_fragmentation_ratio会较大,可以反映内存碎片是否很多
mem_allocator:jemalloc-4.0.3        # 内存分配器

# Persistence   
loading:0                                 # 服务器是否正在载入持久化文件
rdb_changes_since_last_save:82423954      # 离最近一次成功生成rdb文件,写入命令的个数                      
rdb_bgsave_in_progress:0                  # 服务器是否正在创建rdb文件           
rdb_last_save_time:1560991229             # 最近一次成功rdb文件的时间戳               
rdb_last_bgsave_status:ok                 # 最近一次成功rdb文件的状态           
rdb_last_bgsave_time_sec:-1               # 最近一次成功rdb文件的耗时            
rdb_current_bgsave_time_sec:-1            # 若当前正在创建rdb文件,指当前的创建操作已经耗费的时间                
aof_enabled:0                             # aof是否开启
aof_rewrite_in_progress:0                 # aof的rewrite操作是否在进行中            
aof_rewrite_scheduled:0                   # rewrite任务计划,当客户端发送bgrewriteaof指令,如果当前rewrite子进程正在执行,那么将客户端请求的bgrewriteaof变为计划任务,待aof子进程结束后执行rewrite        
aof_last_rewrite_time_sec:-1              # 最近一次aof rewrite耗费时长              
aof_current_rewrite_time_sec:-1           # 若当前正在执行aof rewrite,指当前的已经耗费的时间                
aof_last_bgrewrite_status:ok              # 最近一次aof bgrewrite的状态         
aof_last_write_status:ok                  # 最近一次aof写入状态  

# 开启aof后增加的一些info信息
-----------------------------  
aof_current_size:0                 # aof当前大小
aof_base_size:0                    # aof上次启动或rewrite的大小
aof_pending_rewrite:0              # 同上面的aof_rewrite_scheduled
aof_buffer_length:0                # aof buffer的大小
aof_rewrite_buffer_length:0        # aof rewrite buffer的大小
aof_pending_bio_fsync:0            # 后台IO队列中等待fsync任务的个数
aof_delayed_fsync:0                # 延迟的fsync计数器 
-----------------------------           

# Stats
total_connections_received:15815        # 自启动起连接过的总数。如果连接过多,说明短连接严重或连接池使用有问题,需调研代码的连接设置
total_commands_processed:502953838      # 自启动起运行命令的总数
instantaneous_ops_per_sec:7             # 每秒执行的命令数,相当于QPS
total_net_input_bytes:532510481889      # 网络入口流量字节数
total_net_output_bytes:1571444057940    # 网络出口流量字节数
instantaneous_input_kbps:0.37           # 网络入口kps
instantaneous_output_kbps:0.59          # 网络出口kps
rejected_connections:0                  # 拒绝的连接个数,由于maxclients限制,拒绝新连接的个数
sync_full:1                             # 主从完全同步成功次数
sync_partial_ok:0                       # 主从部分同步成功次数
sync_partial_err:0                      # 主从部分同步失败次数
expired_keys:4404930                    # 自启动起过期的key的总数
evicted_keys:0                          # 使用内存大于maxmemory后,淘汰的key的总数
keyspace_hits:337104556                 # 在main dictionary字典中成功查到的key个数
keyspace_misses:22865229                # 同上,未命中的key的个数
pubsub_channels:1                       # 发布/订阅频道数
pubsub_patterns:0                       # 发布/订阅模式数
latest_fork_usec:707                    # 上次的fork操作使用的时间(单位ms)
migrate_cached_sockets:0                # 是否已经缓存了到该地址的连接
slave_expires_tracked_keys:0            # 从实例到期key数量
active_defrag_hits:0                    # 主动碎片整理命中次数
active_defrag_misses:0                  # 主动碎片整理未命中次数
active_defrag_key_hits:0                # 主动碎片整理key命中次数
active_defrag_key_misses:0              # 主动碎片整理key未命中次数


# Replication
role:master                           # 当前实例的角色master还是slave
connected_slaves:1                    # slave的数量
master_replid:8f81c045a2cb00f16a7fc5c90a95e02127413bcc      # 主实例启动随机字符串
master_replid2:0000000000000000000000000000000000000000     # 主实例启动随机字符串2
slave0:ip=172.17.12.251,port=7002,state=online,offset=506247209326,lag=1    # slave机器的信息、状态
master_repl_offset:506247209478       # 主从同步偏移量,此值如果和上面的offset相同说明主从一致没延迟,与master_replid可被用来标识主实例复制流中的位置。
second_repl_offset                    # 主从同步偏移量2,此值如果和上面的offset相同说明主从一致没延迟
repl_backlog_active:1                 # 复制缓冲区是否开启
repl_backlog_size:157286400           # 复制缓冲区大小
repl_backlog_first_byte_offset:506089923079     # 复制缓冲区里偏移量的大小
repl_backlog_histlen:157286400        # 此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小

# CPU
used_cpu_sys:6834.06                  # 将所有redis主进程在核心态所占用的CPU时求和累计起来
used_cpu_user:8282.10                 # 将所有redis主进程在用户态所占用的CPU时求和累计起来
used_cpu_sys_children:0.11            # 后台进程的核心态cpu使用率
used_cpu_user_children:0.91           # 后台进程的用户态cpu使用率

# Cluster
cluster_enabled:0       # 实例是否启用集群模式

# Keyspace      # 各个数据库(0-15)的 key 的数量,带有生存期的 key 的数量,平均存活时间
db0:keys=267906,expires=109608,avg_ttl=3426011859194
db1:keys=182,expires=179,avg_ttl=503527626
db8:keys=6,expires=0,avg_ttl=0
db15:keys=2,expires=0,avg_ttl=0

关于后门接口工具的使用,可以根据具体的需求进行设计和开发,在系统中添加一个后门接口,用于直接访问缓存数据或执行相关操作。通常情况下,为了保证系统安全性,后门接口应该只对授权用户开放,并具备严格的身份验证和权限控制机制。

缓存删除

缓存有效时,验证相关业务功能:

  • 在业务逻辑中使用缓存数据,例如读取用户信息或者商品信息。

  • 首先确保缓存中存在这些数据,然后通过读取缓存中的数据来验证业务功能的正确性。

举例:使用 Redis 进行验证,假设我们需要缓存用户信息。

首先将用户信息存入 Redis 缓存中:

$ redis-cli
127.0.0.1:6379> SET user:123 '{"id": 123, "name": "John", "age": 25}'
OK

在相关业务逻辑中,首先尝试从缓存中获取用户信息,并验证数据是否正确:

import redis

r = redis.Redis()
user_id = 123

# 尝试从缓存中获取用户信息
cached_user = r.get(f'user:{user_id}')
if cached_user is not None:
    # 缓存中存在用户信息
    user_info = json.loads(cached_user)
    print(user_info)
else:
    # 缓存中不存在用户信息,从数据库中获取并缓存
    user_info = db.get_user_info(user_id)
    r.set(f'user:{user_id}', json.dumps(user_info))
    print(user_info)

通过验证从缓存中获取的数据与预期的用户信息是否一致,可以确认缓存有效时相关业务功能的正确性。

缓存被删除时,验证相关业务功能:

  • 在业务逻辑中通过缓存删除函数或命令删除相应的缓存数据。

  • 确认缓存数据被删除后,再次执行相关业务逻辑,验证系统是否能够正确处理缓存数据缺失的情况。

举例:使用 Redis 进行验证,假设需要删除之前的用户信息。

删除缓存中的用户信息:

$ redis-cli
127.0.0.1:6379> DEL user:123
(integer) 1

在业务逻辑中再次尝试从缓存中获取用户信息,并验证系统的表现:

import redis

r = redis.Redis()
user_id = 123

# 尝试从缓存中获取用户信息
cached_user = r.get(f'user:{user_id}')
if cached_user is not None:
    # 缓存中存在用户信息
    user_info = json.loads(cached_user)
    print(user_info)
else:
    # 缓存中不存在用户信息,从数据库中获取并缓存
    user_info = db.get_user_info(user_id)
    r.set(f'user:{user_id}', json.dumps(user_info))
    print(user_info)

通过验证当缓存数据被删除后,系统是否能够从数据库中重新获取数据,并将其缓存起来,可以确认缓存删除时相关业务功能的正确性。

缓存过期失效的验证:

  • 在设置缓存数据时,通过指定过期时间来使缓存数据在一定时间后失效。

  • 使用相应的命令或工具查看缓存数据的过期时间,确保其与预期一致。

举例:在 Redis 中设置缓存数据的过期时间和查看剩余过期时间。

设置一个键值对,并指定过期时间为 60 秒:

$ redis-cli
127.0.0.1:6379> SETEX key_name 60 value
OK

查看键 key_name 的剩余过期时间:

$ redis-cli
127.0.0.1:6379> TTL key_name
(integer) 55

在此示例中,通过使用 SETEX 命令设置了一个键值对 key_name,并将其过期时间设置为 60 秒。然后使用 TTL 命令查看该键的剩余过期时间,返回结果为 55,说明剩余 55 秒过期。

超量淘汰机制:

超量淘汰机制是在缓存达到预设的上限时,为了腾出空间给新的数据,自动删除一些旧的或不常用的缓存数据的策略。该机制可以确保缓存系统保持在可接受的容量范围内,并提供更高效的缓存管理。

在实际应用中,有多种超量淘汰机制可供选择,以下是一些常见的淘汰算法:

  1. 最近最少使用(Least Recently Used,LRU):基于数据项最近被访问的时间来决定淘汰顺序。如果一个数据项最近被访问过,那么它可能在未来也会被频繁访问,因此最久未被访问的数据项会被优先淘汰。

  2. 最不经常使用(Least Frequently Used,LFU):基于数据项被访问的频率来决定淘汰顺序。如果一个数据项被访问频率较低,那么它可能是不常用的数据,因此访问频率最低的数据项会被优先淘汰。

  3. 随机(Random):随机选择要淘汰的数据项,没有考虑数据项的访问模式或频率。这种方法相对简单,但不能很好地适应不同数据访问模式的情况。

下面是一个示例,展示了如何使用 LRU 策略进行超量淘汰:

假设我们有一个缓存系统,容量上限为 100,当前已经存储了 100 个数据项,并且每个数据项都有对应的访问时间戳。

当新的数据要加入缓存时:

  1. 如果缓存中有空闲位置,直接将新数据加入缓存。

  2. 如果缓存已满,根据 LRU 策略找到最久未被访问的数据项,并将其从缓存中删除,腾出空间给新的数据。

例如,我们有以下数据项及其对应的访问时间戳(最近的访问时间戳表示最新的访问):

Copy CodeKey     Value    Access Time
-----------------------------
A       DataA    2023-08-12
B       DataB    2023-08-14
C       DataC    2023-08-13
D       DataD    2023-08-10
E       DataE    2023-08-11

当前缓存已满,容量上限为 5。如果要新增一个数据项 F,我们会发现数据项 D 的访问时间较早(2023-08-10),因此根据 LRU 策略,我们会淘汰数据项 D,将 F 存入缓存:

Copy CodeKey     Value    Access Time
-----------------------------
B       DataB    2023-08-14
C       DataC    2023-08-13
E       DataE    2023-08-11
F       DataF    2023-08-16

通过超量淘汰机制,缓存系统可以在达到上限时自动淘汰旧数据,以保持缓存的可用性和高效性

要测试超量淘汰机制的效果,可以按照以下步骤进行:

  1. 设定缓存容量和超量淘汰策略:首先确定缓存系统的容量上限和超量淘汰策略,比如使用LRU、LFU或随机等算法。

  2. 创建测试用例:根据具体需求,构建一组测试数据,包括数据项的键值对和访问模式。这些数据会被用来填充缓存和模拟数据访问。

  3. 填充缓存:根据设定的缓存容量,逐个将测试数据项添加到缓存中。确保缓存已经达到容量上限,并且包含了不同访问频率的数据项。

  4. 模拟数据访问:按照预设的访问模式,模拟数据的读取和写入操作。可以按照一定的规律或者随机方式访问不同的数据项。

  5. 监测淘汰情况:在模拟数据访问过程中,监测被淘汰的数据项。记录哪些数据项被淘汰,以及它们的访问时间。

  6. 分析结果:根据实际测试情况,分析超量淘汰机制的效果。评估缓存系统的性能,例如缓存命中率、淘汰策略的准确性等。

通过测试,可以评估超量淘汰机制的有效性和适用性。测试过程中可以尝试不同的场景和参数设置,以获得更全面的结果。请注意,在真实应用中,还需要考虑并发访问、缓存更新等因素对超量淘汰的影响。

缓存穿透:

假设有一个用户权限验证功能,我们可以构造一个不存在于缓存和数据库中的用户 ID 进行访问,然后查看系统对于该无效查询的处理方式,比如返回一个空值或者使用布隆过滤器来防止无效查询。

缓存穿透是指在缓存系统中,频繁访问不存在的数据,导致这些数据无法被缓存,每次都要请求数据库或其他数据源。为了测试缓存穿透问题,可以按照以下步骤进行:

  1. 设置一个模拟的缓存系统:首先,需要设置一个模拟的缓存系统,可以使用内存缓存或者其他缓存技术来实现。

  2. 创建测试用例:构建一组测试数据,包括已存在于缓存中的数据和不存在于缓存中的数据。这些数据项的键值对应该模拟真实的数据请求。

  3. 模拟缓存查询过程:根据测试数据,模拟缓存查询的过程。对于已存在于缓存中的数据项,可以直接从缓存中获取;对于不存在于缓存中的数据项,需要模拟查询数据库或其他数据源的过程。

  4. 监测缓存命中情况:在模拟查询过程中,监测缓存命中情况。记录哪些数据项能够从缓存中获取到,哪些数据项需要访问数据源。

  5. 分析结果:根据实际测试情况,分析缓存穿透的问题。观察无法被缓存的数据项数量,评估缓存系统的性能和效率。

以下是一个示例来说明如何测试缓存穿透问题:

假设我们有一个缓存系统,使用哈希表作为内存缓存实现。缓存容量为100,存储了一些数据项。

测试数据如下:

  • 已存在于缓存中的数据项:Key1、Key2、Key3

  • 不存在于缓存中的数据项:Key100、Key200、Key300

模拟查询过程:

  1. 查询 Key1:可以从缓存中直接获取到值。

  2. 查询 Key100:由于在缓存中不存在该数据项,需要从数据源(例如数据库)中获取。

  3. 查询 Key2:可以从缓存中直接获取到值。

  4. 查询 Key200:同样由于在缓存中不存在,需要从数据源中获取。

  5. 查询 Key3:可以从缓存中直接获取到值。

  6. 查询 Key300:仍然在缓存中不存在,需要从数据源中获取。

分析结果: 观察数据项 Key100、Key200和Key300,它们都无法从缓存中获取到值,而需要频繁地访问数据源。这就是缓存穿透的问题。

要解决缓存穿透问题,可以考虑使用布隆过滤器(Bloom Filter)等技术来过滤不存在的数据项,或者在缓存层面进行缓存空对象(Null Object)的设置。

缓存雪崩:

缓存雪崩是指在缓存系统中,大量缓存失效,导致所有的请求都直接访问底层数据源,从而给数据库或其他数据源带来巨大的压力。为了测试缓存雪崩问题,可以按照以下步骤进行:

  1. 设置一个模拟的缓存系统:首先,需要设置一个模拟的缓存系统,可以使用内存缓存或其他缓存技术来实现。

  2. 创建测试用例:构建一组测试数据,这些数据项需要具有相似的过期时间,以模拟缓存过期的情况。确保这些数据项在同一时间段内会过期。

  3. 模拟缓存失效:让这些测试数据项在同一时间段内全部失效,模拟缓存雪崩的情况。可以通过手动设置过期时间或者在测试代码中模拟缓存失效。

  4. 模拟请求访问:在缓存失效后,模拟大量请求同时访问这些失效的数据项。可以并发地发送多个请求或者在短时间内连续发送大量请求。

  5. 监测数据库或其他数据源的压力:在模拟请求访问的过程中,监测数据库或其他数据源的请求量和响应时间。观察是否出现了大量请求集中访问底层数据源的情况。

  6. 分析结果:根据实际测试情况,分析缓存雪崩问题的严重程度。观察数据库或其他数据源的负载情况,评估缓存系统的鲁棒性和容错能力。

以下是一个示例来说明如何测试缓存雪崩问题:

假设我们有一个缓存系统,使用Redis作为缓存实现。缓存中存储了一些具有相似过期时间的数据项。

测试数据如下:

  • 数据项1:过期时间为10分钟

  • 数据项2:过期时间为10分钟

  • 数据项3:过期时间为10分钟

模拟缓存失效: 设置这些数据项在同一时间段内过期,例如设置它们的过期时间为当前时间前5分钟。

模拟请求访问: 在缓存失效后,模拟大量的并发请求都访问这些失效的数据项,例如同时发送100个并发请求。

监测数据库或其他数据源的压力: 观察数据库的请求数量是否突然增加,响应时间是否显著增加。如果数据库无法承受如此大的请求压力,可能出现性能下降或服务不可用的情况。

分析结果: 如果数据库在测试过程中受到巨大压力或出现性能问题,表明缓存雪崩的问题可能存在。此时,需要对缓存系统的容错能力和缓存失效策略进行优化,例如设置不同的过期时间、引入热点数据随机过期等。

请注意,缓存雪崩问题通常是在大规模并发访问场景下才会出现,因此在测试中需要模拟相应的并发负载。

Redis 缓存服务停掉:

如果 Redis 缓存服务停掉,你可以按照以下步骤进行测试:

  1. 确认 Redis 服务已停止:确保 Redis 服务已成功停止,无法与之通信。

  2. 准备测试环境:在缓存服务停掉的情况下,确保系统的其他组件或服务能够正常运行,并且能够自动切换到备用方案或处理缓存失效的策略。

  3. 模拟请求访问:模拟大量的并发请求访问需要从 Redis 缓存中获取数据的接口。可以使用压力测试工具(如Apache JMeter、Gatling等)生成并发请求,测试系统在缓存服务停止的情况下的性能和稳定性。

  4. 监测系统行为:监测系统的日志和指标,查看是否存在错误日志、超时或异常情况。

  5. 验证备用方案或缓存失效策略:如果系统在缓存服务停止后能够正常响应请求,那么可以验证备用方案或缓存失效策略的有效性。例如,系统可能会自动切换到数据库查询,或者使用其他缓存服务替代 Redis。

举个例子来说明:

假设我们有一个电子商务网站,使用 Redis 缓存来存储商品信息。当 Redis 缓存服务停掉时,我们希望系统能够继续正常运行。

在测试中,我们首先确认 Redis 服务已停止,然后准备好其他必要的系统组件。

接下来,使用压力测试工具模拟大量并发请求访问获取商品信息的接口。这些请求会自动调用系统的缓存读取逻辑,并监测系统的日志和指标。

如果系统能够正常响应请求,并且没有出现错误日志、超时或异常情况,那么说明系统的备用方案或缓存失效策略有效。例如,系统可能会自动从数据库中查询商品信息,或者使用备用的缓存服务(如Memcached)提供商品信息。

通过测试,我们可以验证系统在 Redis 缓存服务停掉的情况下的稳定性和可靠性。

缓存超时:

可以设置较短的超时时间,然后观察系统是否会重新生成缓存数据或者使用备份策略。

缓存数据被误修改后,快速恢复到指定版本:

可以尝试修改缓存数据,并验证系统是否具备回滚或者恢复功能。

缓存数据被误删除后,快速恢复数据:

可以尝试删除缓存数据,并验证系统是否具备从数据库中快速恢复丢失的数据。

Redis 功能测试角度:

  1. 验证数据在 Redis 中生效时的读取是否正确。

  2. 验证当数据在 Redis 中不存在时,是否能够从数据库中正确读取并写入 Redis,并返回给上层。

  3. 验证数据既不在 Redis 也不在数据库中时的表现是否正常。

  4. 验证在删除数据时,Redis 和数据库中的数据是否一致。