Java技术栈总结:Redis篇

一、数据类型

Redis 自身是一个 Map,其中的所有数据均采用“key:value”的形式存储。

数据类型指的是存储的数据的类型,即 value 部分的类型,key 的部分只能是字符串。

value 部分的数据类型:<String、List、Hash、Set、Zset、HyperLogLog>

  • String(字符串),最基本的类型,用于存储简单的字符串数据; 
  • List(列表) 是双向链表实现,存储的数据都是 String 类型;
  • Set(集合)使用哈希表实现,键是元素的值,value是一个整数,表示键在Set中的排名;
  • Zset (有序集合Sorted Set),每个元素都有一个分数,使用跳表(Skip List)作为底层数据结构。可以存储带有分数的成员,并按照分数对成员进行排序,分数为浮点型。(如何实现排行榜?)
  • Hash(哈希表)键值对的集合,每个键对应一个值。底层为数组加链表,出现哈希冲突使用的是头插法向链表添加数据;Rehash 的过程为渐进式 Rehash,内部维护了两个哈希表 Ht[0]、Ht[1],其中 Ht[0]是一般用到的哈希表,Ht[1]只在 Rehash 的过程中才会用到;
  • HyperLogLog 

1、使用场景

String:缓存、分布式锁(setnx、redission)

List:消息队列

Zset:延迟队列


二、I/O模型

<socket请求--I/O多路复用程序--文件事件分派器--事件处理器>

1、常见的网络I/O模型

  • 阻塞IO(Blocking IO)
  • 非阻塞IO(Nonblocking IO)
  • IO多路复用(IO Multiplexing)

Linux系统一个进程使用内存分为两个部分:内核空间用户空间。用户空间只能执行受限的命令,而不能直接调用系统资源,必须通过内核提供的接口访问。内核空间可以执行特权命令,调用一切资源。

Linux为了提高IO效率,在用户空间和内核空间都加入了缓冲区

写数据时,把用户缓冲数据拷贝到内核缓存区,然后写入设备。读数据时,从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区。

(1)阻塞IO

阻塞IO模型,用户进程的“等待数据和拷贝数据”两个阶段都是阻塞状态。

(2)非阻塞IO

非阻塞IO模型中,用户进程在第一个阶段是非阻塞状态,第二个结算是阻塞状态。虽然第一个状态是非阻塞状态,但是效率并未提高,且在此阶段会出现CPU空转,使得CPU消耗增加。

(3)IO多路复用

I/O多路复用机制:通过单个线程同时监听多个Socket,一旦某个Socket就绪(可读、可写),就能够通知程序进行响应,从而避免无效的等待,充分利用CPU资源。

区别于阻塞IO及非阻塞IO直接调用recvfrom方法获取数据,出现目标数据查询不到而即使其他数据已准备就绪也无法优先处理的情况。这种方式同时监听多个Socket,优先处理就绪的数据。

2、Redis网络模型

IO多路复用监控Socket数据就绪状态的方式包括select、poll、epoll等;Redis的I/O模型默认采用的机制为epoll

区别:select 和 poll 只会通知用户进程有Socket就绪,但是不确定具体的Socket。需要用户进程逐个遍历Socket进行确认。epoll 则会在通知用户进程 Socket 就绪的同时,把就绪的 Socket 写入到用户空间,节省了查找就绪状态数据的时间

Redis网络模型(IO多路复用 + 事件派发机制(事件处理器)):

Redis中提供了多个事件处理器,分别处理不同的网络请求。

  • 连接应答处理器
  • 命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程;
  • 命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件

【Redis采用单线程仍然较快的原因】

纯内存操作,执行速度很快,性能瓶颈是网络延迟而不是执行速度,I/O多路复用实现了网络的高效请求。

  • Redis大部分操作都是在内存中直接完成的,C语言编写,采用的数据结构如哈希表、跳表;
  • 采用单线程,避免不必要的上线文切换;
  • 网络模型使用I/O多路复用机制,使得网络I/O操作能并发处理大量的客户端请求。

Q:解释下I/O多路复用模型

A:I/O多路复用模型是指利用单个线程同时监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,更充分的利用CPU资源。目前IO多路复用主要采用epoll模式实现,会在通知用户进程Socket就绪的同时,把已经就绪的Socket写入到用户空间,不需要逐个遍历Socket来判断哪个就绪。

Redis的网络模型是使用的 I/O多路复用 + 事件处理器 的方式处理多个Socket请求。包括:连接应答处理器、命令请求处理器、命令回复处理器。

Redis6.0后,①在命令请求处理器,将命令的转换使用了多线程,增加转换速度。执行命令仍然是单线程;②在命令回复处理器中使用了多线程处理回复事件


三、持久化 AOF|RDB

AOF写操作追加记录,RDB为某一时刻数据快照。

  • AOF 文件的内容是操作命令;
  • RDB 文件的内容是二进制数据。

1、AOF(append only file)

写操作记录到日志文件,命令执行成功才进行写入。

(1)功能开启

Redis默认是关闭的,需要在redis.conf配置文件中修改配置开启:

# 是否开启AOF功能,默认为no

appendonly yes

# AOF文件

appendfilename "appendonly.aof"

(2)刷盘策略

  • always,同步写回,一旦有写命令执行成功,立即将日志写入磁盘;(满足持久性)
  • everysec,命令执行完,先把日志写入到AOF缓冲区,每隔一秒写一次磁盘(默认方案);
  • no,命令执行完,写入到AOF缓存区,由操作系统决定何时写磁盘。

# 配置示例

appendfsync always

配置项刷盘时机特点
always同步刷盘可靠性高,几乎不丢失数据。性能较差
everysec每秒性能适中。最多丢失1秒的数据
no操作系统控制可靠性差,可能丢失大量数据

(3)bgrewriteaof 优化key多次修改

AOF文件记录过程中,同一个key可能会存在被多次修改的情况。这时AOF中就会记录该key的多条记录数据,而只有最后一条有意义。

可以通过bgrewriteaof命令,让AOF文件执行重写功能,用最少的命令达到相同的效果。

同时,AOF支持在触发一定阈值时自动重写AOF文件,对应的redis.conf配置:

# AOF文件比之前增长超过多少百分比触发重写

auto-aof-rewite-percentage 100

# AOF文件体积达到多大以上触发重写

auto-aof-rewrite-min-size 64mb

2、RDB(Redis BackUp file)

Redis数据备份文件,也被称为Redis数据快照。

把内存数据以快照的形式保存到磁盘上,与AOF相比,它记录的是某一时刻的数据,不是操作。当Redis出现故障重启后,可以从磁盘中读取快照文件,恢复数据。

(1)数据备份操作

【客户端主动备份】

# redis-cli

  • 方式1:save   # 由主进程执行RDB,会阻塞所有命令
  • 方式2:bgsave  # 开启子进程执行RDB,避免主进程受到影响

【系统自动备份】

在redis.conf中,配置,示例:

save 100 1   # 表示在100秒内,至少有一个key出现了修改,则执行bgsave;

save 500 200 # 500秒内,至少有200个key出现了修改,则执行bgsave。

(2)执行原理

bgsave执行开始时,会fork主进程得到子进程,并拷贝主进程的页表,共享主进程的内存数据。完成fork后,读取内存数据并写入RDB文件中。

同步数据的过程中,主进程执行了写操作导致数据变动影响的处理:

  • fork采用了 copy-on-write 技术(写时复制),
    • 当主进程执行读操作时,访问共享内存;
    • 主进程执行了写操作的情况,则会拷贝一份内存中的数据,执行读写操作。

3、性能对比

RDBAOF
持久化方式定时对整个内存做快照记录每一次执行的命令
数据完整性不完整,两次备份之前的数据可能会丢失相对完整,取决于刷盘策略
文件大小有压缩,文件体积小记录命令,文件体积很大
宕机数据恢复速度
数据恢复优先级低,因为数据完整性不如AOF
资源占用高,大量消耗CPU及内存低,主要是磁盘I/O资源,但AOF重写时会占用大量的CPU和内存资源
使用场景可以容忍一定的数据丢失,追求更快的启动速度对数据安全要求较高

4、组合使用

为了兼顾RDB恢复速度快及AOF丢失数据少的优点,Redis4.0开始支持混合使用AOF日志与内存快照,称为混合持久化。

开启混合持久化功能,在配置文件redis.conf中设置:

aof-use-rdb-preamble yes  # yes表示开启

混合持久化工作在 AOF日志重写过程

当开启了混合持久化后,在 AOF 重写日志时,fork 出来的子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录到重写缓冲区里。重写缓冲区里的增量命令会以 AOF 的方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据


四、主从复制

1、主从复制设置:

  • 通过1)在从节点执行“SLAVE OF 主节点ip 端口号”;
  • 或者2)设置“slaveof选项”;

2、旧版主从同步

分为两个步骤:同步、命令传播。

  • 同步:
    • 从服务器向主服务器发送“SYNC命令”;
    • 主服务器收到后执行“BGSAVE”命令,生成一个“RDB”文件,并使用缓冲区记录从现在开始执行的所有命令;
    • BGSAVE命令执行完成后,主向从发送RDB文件,从服务器接收并载入,将自己更新成主服务器执行BGSAVE命令时刻的数据库状态;
    • 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行,更新状态到主服务器当前状态。
  • 命令传播:
    • 主服务器将自身执行的写命令,发送给从服务器执行。

   问题:一旦断开连接,需要从头开始执行,而不是只同步断开期间的命令,效率低。

3、新版主从同步(版本2.8开始)

  • 使用“PSYNC命令”代替了旧版的“SYNC命令”;包括完整重同步部分重同步两种模式,其中完整重同步同旧版的同步过程,用于初次同步;部分重同步用于处理断线后的重连接情况。
  • 断线后重连接,如果条件允许,主服务器可将断线期间执行的写命令发送给从服务器,从服务器接收并执行,从而将状态更新为主服务器当前状态。

断线重连接后的“如果条件允许”,条件是判断从服务器断开时刻的复制偏移量和主服务器的“复制积压缓冲区”数据的关系。如果偏移量之后的数据存在于缓冲区则执行部分重同步,否则全量。

4、高可用性

哨兵 + 主从架构。

主节点负责进行写数据操作,从节点进行读数据操作。主节点写数据成功会直接返回成功的消息通知,但是如果还没有将数据同步给从节点,主节点就宕机的话,从节点成为新的主节点,刚新写入的数据就丢失了。

哨兵的数量为奇数个。


五、过期淘汰策略

Redis 的过期策略以及内存淘汰机制?

1、过期策略:

定时删除、定期删除、惰性删除。

实际使用:<定期 + 惰性>

(1)如何判断已过期

当我们对一个key设置了过期时间,Redis会把该key与过期时间存储到一个字典中(expires dict)。即,过期字典中保存了所有key的过期时间。

当我们查询一个 key 时,Redis 首先检查该 key 是否存在于过期字典中:

  • 如果不在,则正常读取键值;
  • 如果存在,则会获取该 key 的过期时间,然后与当前系统时间进行比对,如果比系统时间大,那就没有过期,否则判定该 key 已过期。

(2)各种过期策略

定时删除策略:设置key的过期时间时,Redis同时创建一个定时事件,当时间到达时,由事件处理器自动删除对应的key。

优点:删除快,及时释放内存空间;缺点:CPU资源消耗大。

定期删除策略:每隔一定的时间,对数据进行扫描(LRU策略),删除过期的key。

优缺点:性能及及占用空间居中。难以确定删除操作执行的时长和频率,频率过高类似定时删除,CPU不友好;频率过低,类似惰性删除,内存不友好。

惰性删除策略:不主动删除过期的key,当出现请求访问key时判断当前key是否过期,如果过期则删除。

优点:CPU资源占用少;缺点:内存释放不及时。

# 定期删除的随机取数与全量扫描

Q:默认是哪种策略?

A:Redis的默认定期删除策略是基于LRU(最近最少使用)算法的,而不是随机取数或全量扫描。当Redis的内存使用率超过配置的最大内存限制时,它会自动删除一些旧的键,以释放内存空间。这种策略称为"LRU"策略,即最近最少使用的键会被删除

然而,Redis并没有直接提供随机取数或全量扫描的定期删除策略。如果你想实现这样的功能,你需要通过自定义脚本或者第三方插件来实现。

Redis的定期删除策略(如基于LRU的策略)通常不会扫描全部的键。相反,它会跟踪每个键最后一次被访问的时间,并根据这个时间戳来决定哪些键是最少使用的。当内存使用率超过配置的最大内存限制时,Redis会根据LRU算法删除那些最长时间未被访问的键,而不是随机选择或者扫描所有键。

2、内存淘汰机制:

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  • allkeys-lru:……,在整个键空间中,移除最近最少使用的key。(这个是最常用的)(间隔时间长)
  • allkeys-lfu:……,在整个键空间中,移除最不经常(最少)使用的key(使用频率低)。
  • allkeys-random:……,在整个键空间中,随机移除某个key。
  • volatile-lru:……,在设置了过期时间的键空间中,移除最近最少使用的key。
  • volatile-lfu:……,在设置了过期时间的键空间中,移除最不经常(最少)使用的key。
  • volatile-random:……,在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:……,在设置了过期时间的键空间中,过期时间早的key优先移除。

Redis 6.0及更高版本,默认策略是volatile-lru。而对于Redis 5.x及更早版本,这个默认策略是allkeys-lru。(另有地方说默认策略是noeviction,待考证)

(1)机制配置

配置文件配置:

maxmemory-policy = allkeys-lru

命令操作:

redis-cli config set maxmemory-policy allkeys-lru

Q:如果数据库有1000万数据 ,Redis只能缓存20w数据,如何保证Redis中的数据都是热点数据 ?

A:使用allkeys-lru(选用最近最少使用的数据淘汰)淘汰策略,留下来的都是经常访问的热点数据。

Q:Redis内存用完会发生什么?

A:主要看配置的淘汰策略是什么。如果是 noeviction ,有新数据写入会直接报错...

3、内存淘汰算法:

  • 先进先出(FIFO)算法;
  • 最近最少使用算法(LRU)算法;
  • 最不常用(LFU)算法;

LRU(Least Recently Used,最近最少使用)和 LFU(Least Frequently Used,最少使用频率)都是缓存淘汰策略,用于在缓存空间满时决定删除哪些数据。它们的区别在于:

  1. 淘汰依据

    • LRU:根据数据最近一次被访问的时间进行淘汰。
    • LFU:根据数据被访问的频率进行淘汰。
  2. 适用场景

    • LRU:适用于数据访问具有时间相关性的情况,比如Web服务器的动态内容缓存。
    • LFU:适用于数据访问具有统计规律的情况,比如搜索引擎的索引缓存。
  3. 实现复杂度

    • LRU:实现简单,只需要维护一个链表即可。
    • LFU:实现复杂,需要维护一个哈希表或类似的数据结构来记录每个数据的访问次数。

总的来说,LRU适合于对时间敏感的应用场景,而LFU适合于对访问频率敏感的应用场景。


六、Redis事务

1、相关命令

涉及命令:MULTI、EXEC、WATCH等;

开启事务:MULTI;

提交事务:EXEC;

2、实现

(1)事务队列

开启事务到提交事务之间的命令都会被放入“事务队列”中,事务队列是一个 multiCmd 类型的数组。以先进先出的方式保存入队的命令。

客户端执行事务提交命令EXEC时,该命令会立即被服务器执行。

服务器会遍历对应客户端的事务队列,执行队列中保存的命令,然后将执行的结果全部返回给客户端。

(2)WATCH命令

一个乐观锁,在EXEC命令执行前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否有被修改的情况。如果有,服务器拒绝执行该事务,并向客户端返回执行失败的空回复。

每个Redis数据库都保存着一个watched_keys”字典,该字典的键为被WATCH命令监视的数据库键,字典的值为一个链表,链表的内容为所有监视该数据库键的客户端

所有对数据库修改的命令,都会触发对 watched_keys 字典的检查,如果有对应的key的话,会把监视该键的所有客户端的 REDIS_DIRTY_CAS 标志打开,表示该客户端事务的安全性已经被破坏。

3、与关系型数据库事务的区别

不支持回滚。

即使在执行过程中出现了错误,出现错误前后的命令也不会受到影响,会全部执行完


七、分布式寻址算法

分布式寻址算法:1)hash算法;2)一致性哈希 + 虚拟节点;3)hash slot(hash槽)算法

  • hash算法:将不同的请求hash碰撞后放到固定的hash桶中;扩缩容可用性低;
  • 一致性hash:将hash值空间组织成一个虚拟的圆环;顺时针查找;使用虚拟节点解决分布不均的问题;
  • hash槽(默认):记录和物理机之间引入了虚拟桶层,记录通过hash函数映射到虚拟桶,记录和虚拟桶是多对一的关系;第二层是虚拟桶和物理机之间的映射,同样也是多对一的关系,即一个物理机对应多个虚拟桶,这个层关系是通过内存表实现的。

八、布隆过滤器

bitmap(位图):可以看做是一个以 bit(位)为单位的数组,数组中的每个单元只能存储数据0或者1(并非是真正的数组)。

布隆过滤器的作用:判断一个元素是否在一个集合中。

误判的情况:在查询某个不存在的数据时,如果该数据经过hash运算对应的值恰好在布隆过滤器的位图上都为1,则会被误判为存在。只会出现不存在的数据被误判为存在,反之不会出现。

误判率:数组越小误判率越大;数组越大的情况误判率就越小,但同时消耗的内存也越大。

注:误判断的情况,可以考虑使用两套布隆过滤器。本身是进行的哈希运算,很难出现两套都冲突的情况。


九、分布式锁

1、特性

互斥性、超时释放、可重入性、高性能及高可用、安全性

2、基本实现

setnx + expire

先用setnx进行锁抢占,再用expire给锁设置一个过期时间

# NX是互斥,EX是超时时间
SET lock value NX EX 10
# 释放锁
DEL key

Redis实现分布式锁如何设置有效时长?

  • 根据业务执行时间预估;
  • 给锁续期(单独开启一个线程);

3、Redission

(1)定义

  • Redisson是Redis官方推荐的Java版的Redis客户端。
  • 基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。
  • 在网络通信上是基于NIO的Netty框架,保证网络通信的高性能。
  • 在分布式锁的功能上,它提供了一系列的分布式锁;如:
    • 可重入锁(Reentrant Lock)
    • 公平锁(Fair Lock)
    • 非公平锁(unFair Lock)
    • 读写锁(ReadWriteLock)
    • 联锁(MultiLock)
    • 红锁(RedLock),不是只在一个Redis实例上创建锁,而是在多个实例(N/2 + 1,其中N为节点数)上加锁。

(2)实现分布式锁

  • Redisson配置;
  • 代码:
    @AutowiredRedissonClient redissonClient;public void redissionLock() throws InterruptedException{// 获取锁(可重入锁)RLock lock = redissonClient.getLock("YOUR_LOCK_NAME");// 获取锁的最大等待时间,时间单位boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);// 最大等待时间,锁的失效时间,时间单位。使用此方法,watchDog 不会生效// boolean isLock = lock.tryLock(10, 30, TimeUnit.SECONDS);if (!isLock) {return;}try {// 执行具体的业务逻辑} finally {lock.unlock();}}

Q:Redisson分布式锁的底层实现?

A:底层实现为 setnx + lua脚本(保证原子性)

Q:Redisson的分布式锁,可以重入吗?

A:可以重入。在Redis存储的时候,使用的是hash结构,key为锁的名称,value包括了持有锁的线程信息加锁的次数。只有持有当前锁的线程可以重入。

Q:Redisson锁可以解决主从数据一致的问题吗?

A:不能解决。如果持有锁的节点发送宕机,锁的信息未同步,恢复后新的节点作为了主节点,这时候其他线程可以加锁成功。

可以使用Redisson提供的红锁解决(N/2 + 1 个节点加锁),但是性能较低。如果业务要求强一致性,可以考虑使用ZooKeeper实现的分布式锁。


十、常见问题

1、*缓存穿透、击穿、雪崩

(1)缓存穿透:

  • 查找的数据系统中不存在,既不在Redis,也不在DB

解决:

  • 1)缓存空对象;(查询的数据为空,仍然把这个空数据进行缓存。{key:1, value:null})
    • 优点:实现简便;
    • 缺点:内存消耗大;可能出现数据不一致(先前不存在的数据,DB中后续已存储,缓存未同步的情况)。
  • 2)布隆过滤器;
    • 预热:添加数据到缓存中的同同时,"添加"到布隆过滤器。

(2)缓存击穿:

  • Redis中数据过期,查找的数据不在Redis,但DB存在。给某个key设置了过期时间,在这个key过期的时候,恰好有大量查询该key的请求过来,这些请求在查询Redis获取数据失败后去查询数据库,可能会导致把DB打垮。

解决:

  • 1)互斥锁;(使用分布式锁)
  • 2)逻辑过期;
    • 假设有一条数据:key:1,value: {"id":5, "name":"zhangfei", "expire":1720069442000};不对这条数据设置过期时间,而是使用Value的一个字段作为是否过期的判断。例如这里的expire字段,对应内容为逻辑过期时间的时间戳。
  • 3)预设热门数据,并实时监测变化调整;

互斥锁能够保证数据的强一致性,性能较差。逻辑过期优先保证高可用,非强一致,性能较好。 

(3)缓存雪崩:

  • 大量数据缓存过期失效,导致查DB。
  • 在同一时间段,大量的key失效,或者Redis服务宕机,导致大量请求访问数据库,给数据库造成访问压力。

解决:

  • 1)失效时间增加随机数;
  • 2)对数据库或者服务,增加过载保护或限流(Nginx、微服务网关);
  • 3)考虑多级缓存(Guava或Gaffeine作为一级缓存,Redis作为二级缓存);
  • 4)针对宕机的情况,可以利用Redis集群提高服务的可用性(哨兵模式、集群模式)。

注:限流一般作为保底策略使用。


【缓存清洗】

  • flushdb:清空当前数据库中的所有 key;
  • flushall:清空整个 Redis 服务器的数据(删除所有数据库的所有 key )

【并发竞争Key】

  • 乐观锁--Redis事务,watch指定keys;
  • 分布式锁;
  • 时间戳--类似数据库的乐观锁,判断处理的时间是否对的上;
  • 消息队列。

2、大 Key

大 key 是值 key 对应的 value 内容很大

一般而言,下面这两种情况被称为大 key:

  • String 类型的值大于 10 KB;
  • Hash、List、Set、ZSet 类型的元素的个数超过 5000个。

(1)可能造成的问题

  • 客户端超时阻塞。Redis命令单线程处理,操作大key时,会比较耗时,从而阻塞Redis。
  • 引发网络阻塞。每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。
  • 阻塞工作线程。如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。
  • 内存分布不均。集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大。

(2)查找大Key

方法1:“ridis-cli  --bigkeys”

注意事项:

  • 最好选择在从节点上执行该命令。因为主节点上执行时,会阻塞主节点;
  • 如果没有从节点,那么可以选择在 Redis 实例业务压力的低峰阶段进行扫描查询,以免影响到实例的正常运行;或者可以使用 -i 参数控制扫描间隔,避免长时间扫描降低 Redis 实例的性能。

不足:

  • 这个方法只能返回每种类型中最大的那个 bigkey,无法得到大小排在前 N 位的 bigkey;
  • 对于集合类型来说,这个方法只统计集合元素个数的多少,而不是实际占用的内存量。但是,一个集合中的元素个数多,并不一定占用的内存就多。因为,有可能每个元素占用的内存很小,这样的话,即使元素个数有很多,总内存开销也不大。

方法2:使用SCAN命令(Redis 5.0及以上)

使用 SCAN 命令对数据库扫描,然后用 TYPE 命令获取返回的每一个 key 的类型。

对于 String 类型,可以直接使用 STRLEN 命令获取字符串的长度,也就是占用的内存空间字节数。

对于集合类型来说,有两种方法可以获得它占用的内存大小:

  • 如果能够预先从业务层知道集合元素的平均大小,那么,可以使用下面的命令获取集合元素的个数,然后乘以集合元素的平均大小,这样就能获得集合占用的内存大小了。List 类型:LLEN​ 命令;Hash 类型:HLEN​ 命令;Set 类型:SCARD​ 命令;Sorted Set 类型:ZCARD 命令;
  • 如果不能提前知道写入集合的元素大小,可以使用MEMORY USAGE 命令(需要 Redis 4.0 及以上版本),查询一个键值对占用的内存空间。

方法3:使用RdbTools工具

使用 RdbTools 第三方开源工具,可以用来解析 Redis 快照(RDB)文件,找到其中的大 key。

比如,下面这条命令,将大于 10 kb 的  key  输出到一个表格文件。

“rdb dump.rdb -c memory --bytes 10240 -f redis.csv”

(3)删除方法

方式一:分批次删除

  • 删除大 Hash,使用 hscan 命令,每次获取 100 个字段,再用 hdel 命令,每次删除 1 个字段。
  • 删除大 List,通过 ltrim 命令,每次删除少量元素。
  • 删除大 Set,使用 sscan 命令,每次扫描集合中 100 个元素,再用 srem 命令每次删除一个键。
  • 删除大 ZSet,使用 zremrangebyrank 命令,每次删除 top 100个元素。ZREMRANGEBYRANK key min max

方式二:异步删除(4.0版本以上)

用 unlink 命令代替 del 来删除。

这样 Redis 会将这个 key 放入到一个异步线程中进行删除,这样不会阻塞主线程。

3、双写一致性问题

多个线程同时操作一条数据,

(1)加锁

RedissClient读写锁,读锁为共享锁,写锁为独占锁。

RReadWriteLock readWriteLock = redissionClient.getReadWriteLock("YOUR_LOCK_NAME");
// 读锁
RLock readLock = readWriteLock.readLock();
try {readLock.lock();...
} finally {readLock.unlock();
}// 写锁
RLock writeLock = readWriteLock.writeLock();
try{writeLock.lock();...} finally {writeLock.unlock();
}

特点:强一致性,性能较低。

(2)异步通知

有操作修改数据的场景,操作修改数据库数据,并发送MQ消息。缓存对应的服务接收到MQ消息后,修改缓存数据。


参考:

Redis过期策略及内存淘汰机制;

什么是可重入锁?详解redis实现分布式重入锁的方法;

Redis大key删除的相关问题;redis随手记-大key删除法;面试官:Redis 大 Key 要如何处理;

Redis删除大key引发的线上事故;

Redis的持久化 ROB+AOF;图解Redis介绍 | 小林coding

https://www.bilibili.com/video/BV1yT411H7YK

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/42175.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MSPM0G3507——编码器控制速度

绿色设置的为目标值100&#xff0c;红色为编码器实际数据 。 最后也是两者合在了一起&#xff0c;PID调试成功。 源码直接分享&#xff0c;用的是CCStheia&#xff0c;KEIL打不开。大家可以看一下源码的思路&#xff0c;PID部分几乎不用改 链接&#xff1a;https://pan.baid…

S32DS S32 Design Studio for S32 Platform 3.5 代码显示行号与空白符

介绍 NXP S32DS&#xff0c;全称 S32 Design Studio&#xff0c;s32 系列芯片默认使用 S32 Design Studio for S32 Platform 作为 IDE 集成开发环境&#xff0c;当前版本 S32 Design Studio for S32 Platform 3.5&#xff0c;IDE 可以简称 s32DS 使用 S32DS&#xff0c;可以认…

Qt 网络编程 网络信息获取操作

学习目标&#xff1a;网络信息获取操作 前置环境 运行环境:qt creator 4.12 学习内容 一、Qt 网络编程基础 Qt 直接提供了网络编程模块,包括基于 TCP/IP 的客户端和服务器相关类,如 QTcpSocket/QTcpServer 和 QUdpSocket,以及实现 HTTP、FTP 等协议的高级类,如 QNetworkRe…

【简单介绍下Memcached】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

昇思25天学习打卡营第08天 | 模型训练

昇思25天学习打卡营第08天 | 模型训练 文章目录 昇思25天学习打卡营第08天 | 模型训练超参数损失函数优化器优化过程 训练与评估总结打卡 模型训练一般遵循四个步骤&#xff1a; 构建数据集定义神经网络模型定义超参数、损失函数和优化器输入数据集进行训练和评估 构建数据集和…

uniapp 去掉小数末尾多余的0

文章目录 在uniapp或者一般的JavaScript环境中&#xff0c;要去掉小数末尾的0&#xff0c;可以使用以下几种方法&#xff1a; 使用parseFloat()函数 let num 123.4500; let result parseFloat(num); console.log(result); // 输出: 123.45字符串处理 将数字转换为字符串&am…

24年沈阳教师招聘报名全流程(电脑版)

公开招聘1876名事业编制工作人员 报名时间&#xff1a;7月8日10:00至7月11日16:00&#xff1b; 报名网站&#xff1a;&#xff1a;沈阳市考试院 报名流程&#xff1a; 1.报名 ①登录沈阳市考试院填写报名信息&#xff0c;上传照片 本人近期免冠2寸正面电子证jian照片&#xff0…

接口依赖-动态参数+数据依赖的代码

怎么写测试用例 接口名:被依赖的返回值的jsonpath表达式&#xff0c;有几个依赖往后写就可以 代码 如果caseinfo里的getisdep等于yes&#xff0c;并且&#xff0c;depkey不为空 用正则解析 在testrun里增加判断 判断的内容是正则表达式&#xff0c;先去匹配&#xff0c;再去…

2024年世界人工智能大会(WAIC)各大佬的精彩发言

2024年世界人工智能大会&#xff08;WAIC&#xff09;在上海举行&#xff0c;受到了广泛关注和参与。以下是大会首日的主要观点和议题的总结&#xff1a; AI 应用落地&#xff1a;大会讨论了AI应用如何落地&#xff0c;即如何在当前阶段利用大模型技术实现实际应用。 AI 安全&…

flask缓存、信号的使用

【 一 】flask-ache ​ 它为 Flask 应用程序提供了缓存支持。缓存是 Web 应用程序中非常常见的做法&#xff0c;用于存储频繁访问但不太可能经常更改的数据&#xff0c;以减少对数据库或其他慢速存储系统的访问&#xff0c;从而提高应用程序的性能和响应速度。 ​ Flask-Cach…

041基于SSM+Jsp的高校校园点餐系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

昇思MindSpore学习笔记4-05生成式--Pix2Pix实现图像转换

摘要&#xff1a; 记录昇思MindSpore AI框架使用Pix2Pix模型生成图像、判断图像真实概率的原理和实际使用方法、步骤。包括环境准备、下载数据集、数据加载和处理、创建cGAN神经网络生成器和判别器、模型训练、模型推理等。 一、概念 1.Pix2Pix模型 条件生成对抗网络&#x…

科研绘图系列:R语言两组数据散点分布图(scatter plot)

介绍 展示两组数据的散点分布图是一种图形化表示方法,用于显示两个变量之间的关系。在散点图中,每个点代表一个数据点,其x坐标对应于第一组数据的值,y坐标对应于第二组数据的值。以下是散点图可以展示的一些结果: 线性关系:如果两组数据之间存在线性关系,散点图将显示出…

HUAWEI VRRP 实验

实验要求&#xff1a;在汇聚交换机上SW1和SW2中实施VRRP以保证终端网关的高可靠性(当某一个网关设备失效时&#xff0c;其他网关设备依旧可以实现业务数据的转发。) 1.在SW1和SW2之间配置链路聚合&#xff0c;以提高带宽速度。 2.PC1 访问远端网络8.8.8.8 &#xff0c;优先走…

视频汇聚/安防监控/GB28181国标EasyCVR视频综合管理平台出现串流的原因排查及解决

安防视频监控系统/视频汇聚EasyCVR视频综合管理平台&#xff0c;采用了开放式的网络结构&#xff0c;能在复杂的网络环境中&#xff08;专网、局域网、广域网、VPN、公网等&#xff09;将前端海量的设备进行统一集中接入与视频汇聚管理&#xff0c;视频汇聚EasyCVR平台支持设备…

Git-Unity项目版本管理

目录 准备GitHub新建项目并添加ssh密钥Unity文件夹 本文记录如何用git对unity 项目进行版本管理&#xff0c;并可传至GitHub远端。 准备 名称版本windows11Unity2202.3.9.f1gitN.A.githubN.A. GitHub新建项目并添加ssh密钥 GitHub新建一个repositorywindows11 生成ssh-key&…

【python】python母婴数据分析模型预测可视化(数据集+论文+PPT+源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

(阿里云在线播放)基于SpringBoot+Vue前后端分离的在线教育平台项目

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

阿里通义音频生成大模型 FunAudioLLM 开源!

01 导读 人类对自身的研究和模仿由来已久&#xff0c;在我国2000多年前的《列子汤问》里就描述了有能工巧匠制作出会说话会舞动的类人机器人的故事。声音包含丰富的个体特征及情感情绪信息&#xff0c;对话作为人类最常使用亲切自然的交互模式&#xff0c;是连接人与智能世界…

uniapp报错--app.json: 在项目根目录未找到 app.json

【问题】 刚创建好的uni-app项目&#xff0c;运行微信小程序控制台报错如下&#xff1a; 【解决方案】 1. 程序根目录打开project.config.json文件 2. 配置miniprogramRoot&#xff0c;指定小程序代码的根目录 我的小程序代码编译后的工程文件目录为&#xff1a;dist/dev/mp…