当前位置: 首页 > news >正文

【Redis】Redis常见原理和数据结构

Redis


什么是redis

redis是一款基于内存的k-v数据结构的非关系型数据库,读写速度非常快,常用于缓存,消息队列、分布式锁等场景

redis的数据类型

string:字符串 缓存对象,分布式ID,token,session,数字自增,分布式锁,JWT

list:列表 消息队列

hash:哈希 缓存对象 购物车

set:集合 缓存对象 点赞可能认识的人,共同好友。社交功能 收藏夹

zset:有序集合 排行榜

geo:地理 地理位置附近的人,滴滴

hyperloglog:基数统计 网站pu统计

stream:流 消息队列

bitmap:位图 登录状态,连续签到,二值统计

Redis还支持事务,发布-订阅,切片集群,主从复制,哨兵,持久化,内存淘汰机制,lua脚本,过期删除策略

为什么使用redis作为mysql的缓存

高性能

高并发

常见数据类型的底层实现

数据类型3.07.0
stringSDSSDS
list双向链表,压缩列表quicklist
hash压缩列表,哈希表listpack,哈希表
set哈希表,整数集合哈希表,整数集合
zset压缩列表,跳表listpack,跳表

元素数量小于512,元素长度小于64字节

string:简单动态字符串

list:quicklist(压缩列表,双向链表)

hash:listpack(压缩列表,哈希表)

set:元素少于512个且为整数,整数集合;否则哈希表

zset:元素小于128,元素值小于64字节时压缩列表;否则跳表

7.0后压缩列表又listpack代替

Redis线程模型


redis是单线程吗

先指出什么是redis的单线程:redis的单线程指的是接受客户端请求,解析请求,处理请求,进行数据读写操作,发送请求结果是由主线程处理的

然后分析redis程序不是单线程:启动redis程序,不仅由主线程还有后台线程,关闭处理文件,aof刷盘,lazyfree线程(异步释放内存)

redis采用单线程为什么还那么快

redis内存操作

I/O多路复用机制

单线程模型,避免多线程频繁的创建和切换

redis之前为什么是单线程模型

一方面:redis性能瓶颈不在于cpu,而在于网络IO,多线程也仅仅是处理网络IO时使用多线程,指令处理过程还是单线程。
另一方面:多线程频繁的创建销毁和切换也是不小的cpu开销,增加了系统复杂性,还需要考虑加锁场景。aaaaaa

Redis持久化

==AOF日志:==执行完一条写命令,就将命令追加到server.aof_biuf缓冲区,然后通过系统write()调用将aof_buf缓冲区的数据写入到aof文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区page_cache,等待内核将数据写入磁盘。在,redis重启时,会读取aof文件中的命令,进行数据恢复。

这个过程是先执行命令再去存储命令:一方面避免了对命令的语法检查,另一方面不会阻塞写命令运行;但是可能会发生数据丢失和阻塞后续命令

AOF写回策略有三种:

alway:立即写回磁盘

severysec:每秒写入一次

no:有内核控制写入时机

写回策略写回时机优点缺点
always立即写回可靠性高,几乎不会丢失数据磁盘压力大,性能开销大
everysec每秒写回性能适中宕机时最多损失1s内数据
no内核决定性能好宕机时可能损失最多数据

AOF日志过大时会触发AOF重写:读取当前数据库中所有的键值对,记录到新的aof文件,从后将文件重命名替换原来的aof文件,先生成后替换模式避免了源文件发生污染。

重写AOF过程是由后台子进程bgrewriteaof完成的:1.避免了主进程阻塞,主进程可能继续处理请求 2.使用主进程而不是主线程,多线程之间会共享内存,修改共享内存数据时需要加锁来保证数据安全。而父子进程是以只读方式共享内存数据的,当一方发生写操作时就会触发写时复制,形成两个独立数据副本就不用加锁来保证数据安全

如果AOF日志重写过程中主线程处理了写请求,那么写请求命令会被同时写进AOF缓冲区和AOF重写缓冲区,当子进程完成AOF重写后,主进程会将AOF重写缓冲区的所有内容追加到新的AOF文件中,改名覆盖原来文件。

RDB快照:记录某一个瞬间的内存数据。这种方法重启时恢复数据比AOF重启快得多。AOF重启是将命令重新执行一遍,速度非常慢

RDB快照又save和bgsave两个命令控制。

save:主线程进行RDB快照生成,会阻塞主线程

bgsave:创建一个子进程来处理生成RDB文件

Redis 的快照是全量快照,也就是说每次执行快照,都是把内存中的「所有数据」都记录到磁盘中。所以执行快照是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多

RDB执行快照的时候,数据还是可以被修改的,写时复制技术。执行bgsave命令时会fork一个子进程,会将主线程的页表复制一份给子进程,这两份页表指向同一片内存区域,当一方尝试进行写操作时就会触发写时复制,创建两个独立的数据副本,RDB快照继续对副本进行快照,主线程又可以进行写命令操作。

混合持久化:RDB快照重启时恢复快,但是宕机会损失较多数据;AOF重启式恢复较慢,但是宕机时损失数据较少。这样既保证了重启数据恢复速度又降低了数据丢失风险。

混合持久化工作在AOF日志重写的过程中,当执行aof日志重写时,主进程fork的子进程就会先将与主进程共享的数据以RDB快照的形式存入aof文件,然后主进程记录在aof重写缓冲区的数据会以aof日志的形式追加到aof文件,这样就形成了一个混合持久化文件,然后替换原来的aof文件。这样重启时会先加载保存大量数据的rdb文件,然后加载较少的aof文件,使得重启数据恢复数据较快。因为原来的一部分命令以快照的形式存储了起来。这个重启是指数据全部加载完毕,因为rdb里面的key可能被修改,入药aof文件进行修正,这样才算重启全部完成。

持久化方式数据重启
AOF宕机数据丢失少重启数据恢复较慢
RDB宕机丢失数据较多重启数据恢复快
混合持久化宕机数据丢失较少重启数据较快

Redis如何实现高可用

主从复制

主从复制就是将一台redis服务器同步数据到多台从服务器,就是一主多从的模式,并且主从服务器之间采用读写分离的模式。

主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,从服务器一般只读,并接受主服务器同步的写操作命令。

所有的写操作都是由主服务器完成的,主从服务器的数据是一致的,无法保证强一致性。在从节点进行写操作会报错,因为从节点默认是只读模式

哨兵模式

哨兵模式是为了解决Redis主从模式主服务器宕机时需要手动恢复的问题,哨兵模式可以监控主从服务器,并提供主从节点故障转移功能。

切片集群

当redis缓存数据达到一台服务器无法缓存的时候,就需要使用redis切片集群方案。将数据散发到不同的服务器上。

Redis Cluster采用hash槽来处理数据与节点之间的映射关系。一个切片集群共有16384个hash槽。

具体过程发了为两步:

  • 根据键值对的 key,按照 CRC16 算法 计算一个 16 bit 的值。
  • 再用 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽。

具体映射方案:

  • 平均分配: 在使用 cluster create 命令创建 Redis 集群时,Redis 会自动把所有哈希槽平均分布到集群节点上。比如集群中有 9 个节点,则每个节点上槽的个数为 16384/9 个。
  • 手动分配: 可以使用 cluster meet 命令手动建立节点间的连接,组成集群,再使用 cluster addslots 命令,指定每个节点上的哈希槽个数。

集群脑裂导致数据丢失问题

主从架构中,主节点与所有从节点因为网络波动失去联系,但与客户端依旧保持连接,客户端依旧向这个主节点写数据,但是这些数据无法同步给从节点,哨兵发现主节点失联重新选举主节点,这样一个集群就出现了两个主节点。当原来主节点重连时,会降级为从节点,并且清空数据与主节点进行数据同步。那么失联那段时间写入的数据就会丢失。

解决方案:当主节点下线或者通信时间超时的总数量小于阈值的时候,紧张主节点写入数据,客户端返回错误。

min-slaves-to-write 主节点至少需要与几个节点连接,小于这个数禁止写数据

min-slaves-max-lag 主从数据复制和同步延迟不能超过多少秒,超时就会禁止写入数据

即使原主库是假故障,期间也无法响应哨兵心跳和从库进行同步,自然也就无法和从库进行 ACK 确认了。这样一来,min-slaves-to-write 和 min-slaves-max-lag 的组合要求就无法得到满足,原主库就会被限制接收客户端写请求,客户端也就不能在原主库中写入新数据了。

等到新主库上线时,就只有新主库能接收和处理客户端请求,此时,新写的数据会被直接写到新主库中。而原主库会被哨兵降为从库,即使它的数据被清空了,也不会有新数据丢失。

Redis过期删除和内存淘汰策略

如果key设置了过期时间就会把key带上过期时间存入到过期字典中。

过期删除:惰性删除,定时删除,定期删除 ,redis使用定期删除和惰性删除结合的策略。

定时删除:顶个计数器,到期自动删除

惰性删除:等查询到key时去判断是否过期

定期删除:过一段时间随机抽取20个键,如果超过五个就会再抽取20个,执行时间有一个阈值默认是25ms

过期删除策略优点缺点
定时删除能及时清理过期键值会占用大量的cpu资源
惰性删除占用很少的系统资源,对cpu时间最友好key不被访问一直存在,造成内存浪费
定期删除限制执行时长和频率,削减对cpu的占用,减少了过期🗡的内存消耗难以去确定执行的时长和频率

Redis持久化时会对过期🗡做什么处理

RDB文件生成阶段:RDB文件生成期间会对key进行检查,过期的🗡不会被保存到新的RDB文件中。
RDB文件加载阶段:主服务器:会对🗡进行检查,过期键不会被加载到内存中;从服务器:接收到主服务器的RDB文件不会对键的状态进行检查,从节点在执行只读操作时,如果是有expire设定的key,则会根据自己机器上的时钟来判断是否已过期,如果未过期,则返回给客户端。但从节点本身不执行删除操作,而是会等待后面的del同步操作。

AOF重写阶段:会对键值进行过期检查,过期的不会被保留到新生成的aof文件 (aof文件过大)
AOF写入阶段:如果过期键没被删除,aof文件会保留此过期键,当过期间被删除后,会向aof文件显式追加一条删除命令 (生成aof)

Redis主从复制时,对过期键如何处理

只有主服务器进行过期扫描,从库过期键依靠主服务器控制,主服务器会向AOF文件追加del命令,同步到从库。

Redis内存满了会发生什么

就会触发内存淘汰机制

不做数据淘汰:满了就无法进行写操作命令,但是可以执行删除查询等操作 。maxmemory可以设置最大运行内存

执行数据淘汰:

1.对过期时间中数据进行淘汰:volatile-random,volatile-ttl,volatile-lru,volatile-lfu

2.对所有键进行数据淘汰: allkeys-random,allkeys-lru,allkeys-lfu

LRU 算法和 LFU 算法有什么区别?

传统的LRU算法基于链表实现,链表中的元素按照操作顺序从前往后排列,最新操作的元素会一道链表头部,删除末尾元素。

redis没有采用传统的方法,因为这样需要维护一个链表,会带来额外的空间开销,并且存储数据非常多的时候链表会很大,大量访问时就会产生很多移动操作,极大的耗费性能。

redis在对象头中添加了一个24位的lru字段用于记录最后一次访问的时间,随机淘汰时就是比较这个时间来决定是否淘汰,这样就不用维护链表节省了空间,提高了效率和性能。但是当一次性读取大量的数据,而这些数据只会被读取一次,却能保存相当长的时间,造成缓存污染。

LFU 算法会记录每个数据的访问次数。当一个数据被再次访问时,就会增加该数据的访问次数。这样就解决了偶尔被访问一次之后,数据留存在缓存中很长一段时间的问题。

redis在对象头中添加一个24位的lfu字段,高16位用来记录上次访问的时间戳ldt,低八位用来记录拼凑logc,这个是按数学概率来进行增加削减的。

Redis缓存设计

如何避免缓存雪崩,缓存击穿,缓存穿透

缓存雪崩:过期时间更加均匀,比如给过期时间加上随机数值;不设置过期时间,由后台服务决定更新时机

缓存击穿:设置互斥锁,如双重检查锁;不设置过期时间,有后台决定更新时机;进行限流,一次只允许几个请求

缓存穿透:对于空请求返回一个默认值;使用布隆过滤器;限制非法请求。

Redis缓存是策略,动态缓存热点数据

数据存储受限,系统并不是将所有的数据都添加到缓存中,而只是将其中一部分热点数据缓存起来。

动态热点缓存的策略是:根据数据最新访问时间来进行排名,并过滤到不常用的数据,只留下经常访问的数据。

在 Redis 中可以用 zadd 方法和 zrange 方法来完成排序队列和获取 200 个商品的操作。

常见的缓存更新策略

Write Back:写回策略

Write Back(写回)策略在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。

redis没有异步更新数据库的功能

常用在计算机体系结构中的设计,比如 CPU 的缓存、操作系统中文件系统的缓存

Cache Aside:旁路缓存策略

写:更新数据库数据,然后更新缓存数据

读:如果缓存命中,直接返回数据;缓存不在就去数据库查找,然后更新缓存

不能先删除缓存再更新数据库:会出现缓存和数据库数据不一致性的问题

适合读多写少的场景。

Write / Read Through:写穿读穿策略

read through:先查询缓存中数据是否存在,如果存在则直接返回,如果不存在,则由缓存组件负责从数据库查询数据,并将结果写入到缓存组件,最后缓存组件将数据返回给应用。

write through:当有数据更新的时候,先查询要写入的数据在缓存中是否已经存在:

  • 如果缓存中数据已经存在,则更新缓存中的数据,并且由缓存组件同步更新到数据库中,然后缓存组件告知应用程序更新完成。
  • 如果缓存中数据不存在,直接更新数据库,然后返回;

本地缓存可以这种策略;分布式缓存无法实现,因为没有此类功能

Redis如何实现延迟队列

使用场景:

  1. 订单超时未支付自动关闭
  2. 外卖十分钟没有接单会自动取消

Redis可以使用有序集合ZSet来实现延迟消息队列,ZSet的score属性可以赋值给延迟执行的时间,轮询比对利用arangebyscore来查询所有待处理任务,循环执行即可

Redis的大Key问题如何处理

大key:

String类型的值大于10kb

Hash,List,Set,ZSet类型元素超过5000个

大key带来的问题:

  1. 操作大key比较耗时,客户端阻塞,可能响应超时
  2. 获取大key产生的流量较大,可能引发网络阻塞
  3. del删除大key引发主线程阻塞
  4. 集群模型在slot分片均匀的情况下会产生数据和查询倾斜的问题,部分大key的几点内存占用多,QPS也比较大

如何找到大key

  1. redis-cli -a “密码” --bigkeys 从节点或者小流量时期,该检测会阻塞主线程 ; 方法只会返回bigest key无法返回前n个数据;集合类型 只会统计menber数量,不会计算占用大小。

  2. scan 命令 scan是基于游标的迭代器,scan命令对数据库扫描,然后type获取类型,根据不同类型使用不同命令来获取长度

    string: STRLEN来获取字符串长度
    集合类型:MEMORY USAGE来查询

  3. RdbTools工具
    RdbTools 第三方开源工具,可以用来解析 Redis 快照(RDB)文件,找到其中的大 key
    rdb dump.rdb -c memory --bytes 10240 -f redis.csv

如何删除大key
不能直接删除大key:在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序。所以,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞,如果主线程发生了阻塞,其他所有请求可能都会超时,超时越来越多,会造成 Redis 连接耗尽,产生各种异常。

分批删除大Key:删除大 Hash,使用 hscan 命令,每次获取 100 个字段,再用 hdel 命令,每次删除 1 个字段。
删除大 List,通过 ltrim 命令,每次删除少量元素。
删除大 Set,使用 sscan 命令,每次扫描集合中 100 个元素,再用 srem 命令每次删除一个键。
删除大 ZSet,使用 zremrangebyrank 命令,每次删除 top 100个元素。
异步删除:用 unlink 命令代替 del 来删除, Redis 会将这个 key 放入到一个异步线程中进行删除,这样不会阻塞主线程。

redis管道有什么作用

管道技术可以解决多个命令执行时的网络等待,它是把多个命令整合到一起发送给服务器端处理之后统一返回给客户端,这样就免去了每条命令执行后都要等待的情况,从而有效地提高了程序的执行效率。但使用管道技术也要注意避免发送的命令过大,或管道内的数据太多而导致的网络阻塞。

管道技术本质上是客户端提供的功能,而非 Redis 服务器端的功能。

Redis支持事务回滚吗?

Redis 中并没有提供回滚机制,虽然 Redis 提供了 DISCARD 命令,但是这个命令只能用来主动放弃事务执行,把暂存的命令队列清空,已经执行过的命令结果也不会删除,起不到回滚的效果。

如何使用redis实现分布式锁

分布式锁用于控制分布式场景下某一个资源只能被某一个应用所使用。
Redis 的 SET 命令有个 NX 参数可以实现「key不存在才插入」,所以可以用它来实现分布式锁:

Redis 节点实现分布式锁时,对于加锁操作,我们需要满足三个条件

  • 加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作,但需要以原子操作的方式完成,所以,我们使用 SET 命令带上 NX 选项来实现加锁;
  • 锁变量需要设置过期时间,以免客户端拿到锁后发生异常,导致锁一直无法释放,所以,我们在 SET 命令执行时加上 EX/PX 选项,设置其过期时间;
  • 锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作,所以,我们使用 SET 命令设置锁变量值时,每个客户端设置的值是一个唯一值,用于标识客户端;
SET lock_key unique_value NX PX 10000 

解锁的过程就是将 lock_key 键删除(del lock_key),要保证执行操作的客户端就是加锁的客户端。
解锁的时候,我们要先判断锁的 unique_value 是否为加锁客户端,是的话,才将 lock_key 键删除。

解锁是有两个操作,需要 Lua 脚本来保证解锁的原子性,因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性。

if redis.call("get",KEY[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

基于rediss的分布式锁优点:

  1. 性能高效
  2. redis本身提供了setnx方法,实现简单
  3. redis可以集群部署,能够避免单点故障

缺点:

  1. 超时时间不好设置。如果锁的超时时间设置过长,会影响性能,如果设置的超时时间过短会保护不到共享资源。看门狗机制去自动续约。
  2. Redis 主从复制模式中的数据是异步复制的,这样导致分布式锁的不可靠性。主节点获取到锁后还没同步就宕机了,新的主节点还能获取到锁。就失效了

分布式锁算法 Redlock(红锁):是让客户端和多个独立的 Redis 节点依次请求申请加锁,如果客户端能够和半数以上的节点成功地完成加锁操作,那么我们就认为,客户端成功地获得分布式锁,否则加锁失败。这种算法对于集群设备之间的时序和系统时钟苛刻要求。

  • 有界网络延迟(可以保证数据包始终在某个保证的最大值内到达 延迟),
  • 有界进程暂停(换句话说,硬实时约束,通常只有 在汽车安全气囊系统等中找到),以及
  • 有界时钟错误

系统 有 5 个 Redis 节点(A、B、C、D 和 E)和 2 个客户端(1 和 2)。如果时钟在一个上会发生什么 的 Redis 节点向前跳跃?

  1. 客户端 1 在节点 A、B、C 上获得锁定,由于网络问题,无法访问 D 和 E。

  2. 节点 C 上的时钟向前跳跃,导致锁过期。

  3. 客户端 2 在节点 C、D、E 上获得锁定,由于网络问题,无法访问 A 和 B。

  4. 客户端 1 和 2 现在都认为他们掌握着锁。

  5. 客户端 1 请求锁定节点 A、B、C、D、E。

  6. 当对客户端 1 的响应在传输中时,客户端 1 进入停止世界的 GC。

  7. 所有 Redis 节点上的锁都会过期。

  8. 客户端 2 在节点 A、B、C、D、E 上获得锁定。

  9. 客户端 1 完成 GC,并收到来自 Redis 节点的响应,指示其成功 获取了锁(当进程 暂停)。

  10. 客户端 1 和 2 现在都认为他们掌握着锁。

如何进行分布式锁定 — Martin Kleppmann 的博客

详解Redis底层数据结构

Dict

SDS

SDS,简单动态字符串

结构组成:

  1. len:字符串长度
  2. alloc:分配的空间大小,修改字符串时去判断已分配空间是否满足,否则就去扩容,避免了缓冲区溢出
  3. flags:结构类型,SDS支持多种结构类型,根据数据大小灵活分配合适类型,比如一个人的年龄用byte存储足够,那就不需要使用long或者int类型
  4. buf数组:存放具体数据

SDS与传统c语言的char类型对比有何优势?

  1. 传统的char必须以 \0 结尾,SDS通过len来标记出buf数据长度,即使中间有\0也不会中途被判结束,因此避免了二进制安全问题,并且能够存储图片、视频等二进制数据
  2. 通过len字段记录数据长度,更够避免在数据修改时造成缓冲区溢出,支持自动扩容
  3. 使用flags灵活选取合适的SDS数据结构类型,取消了结构体对齐填充,节省了空间

双向链表

就是自定义节点实现的双向链表,包含前驱节点,后继节点,value,同时封装List,包含了链表长度,头节点,尾节点。

优点:

  1. 定义了pre和next,获取某个节点的前置节点和后置节点只需要O(1)的复杂度,
  2. len属性表示节点数量,可以直接获取链表长度缺点:

缺点:

  1. 链表利用的是碎片化地址,无法很好的利用到CPU缓存,只有数组能。
  2. 需要保存额外字段,增加了内存开销

压缩列表

在这里插入图片描述

  • zlbytes: 整个压缩列表占用的字节总数
  • zltail:列表尾的偏移量
  • zllen:节点数量
  • zlend:压缩列表结束点
  • prevlen: 前一个节点长度
  • encoding:编码方式
  • data:节点数据

查询首尾节点元素只需要o(1)复杂度,查询其他节点只能遍历O(n)复杂度,因此压缩列表不适合存储较多数据

如果前一个节点长度小于254,prevlen只需要1个字节,如果大于254就需要5个字节

当插入或者更新节点时,如果空间不够就会内存重分配,如果插入节点元素较大,可能导致后续元素的prvlen都发生变化,进而造成连锁更新,原因就在于增加的4个字节

Listpack

在这里插入图片描述

listpack 头包含两个属性,分别记录了 listpack 总字节数和元素数量,然后 listpack 末尾也有个结尾标识。图中的 listpack entry 就是 listpack 的节点了。

每个 listpack 节点结构如下:

在这里插入图片描述

主要包含三个方面内容:

  • encoding,定义该元素的编码类型,会对不同长度的整数和字符串进行编码;
  • data,实际存放的数据;
  • len,encoding+data的总长度;

可以看到,listpack 没有压缩列表中记录前一个节点长度的字段了,listpack 只记录当前节点的长度,当我们向 listpack 加入一个新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题

跳表

zset 结构体里有两个数据结构:一个是跳表,一个是哈希表。这样的好处是既能进行高效的范围查询,也能进行高效单点查询。

哈希表

Redis采用拉链法解决Hash冲突,将Hash冲突的元素通过链表串联存储

Redis哈希表结构包含了两个哈希表,当hash表数据增多时便会触发rehash过程,

此时将ht[1]初始化为ht[0]二倍大小,让后将ht[1]中数据rehash后存入ht[2],最后互换地址

由于rehash过程涉及大量数据的拷贝,可能会造成阻塞,影响功能,因此采用渐进式rehash过程,客户端请求驱动rehash过程,也就是有请求就处理迁移一点数据。

QuickList

在这里插入图片描述

双向链表和压缩列表的组合,QuickList就是一个链表,而链表中的每个元素又是一个压缩链表

压缩列表的不足,虽然压缩列表是通过紧凑型的内存布局节省了内存开销,但是因为它的结构设计,如果保存的元素数量增加,或者元素变大了,压缩列表会有「连锁更新」的风险,一旦发生,会造成性能下降。

quicklist 解决办法,通过控制每个链表节点中的压缩列表的大小或者元素个数,来规避连锁更新的问题。因为压缩列表元素越少或越小,连锁更新带来的影响就越小,从而提供了更好的访问性能。

quicklist 会控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来规避潜在的连锁更新的风险,但是这并没有完全解决连锁更新的问题。

整数集合

当Set全部包含整数并且数量不多时就会使用整数集合

typedef struct intset {//编码方式uint32_t encoding;//集合包含的元素数量uint32_t length;//保存元素的数组int8_t contents[];
} intset;

整数集合的升级操作

当新元素类型比所有元素的类型都要长时,整数集合就会升级,拓展空间,能节约内存资源

整数集合不支持降级操作,主要是频繁的升降级会消耗CPU资源,

本文自小林Coding总结而来,感谢小林图解Redis

相关文章:

【Redis】Redis常见原理和数据结构

Redis 什么是redis redis是一款基于内存的k-v数据结构的非关系型数据库,读写速度非常快,常用于缓存,消息队列、分布式锁等场景。 redis的数据类型 string:字符串 缓存对象,分布式ID,token,se…...

3个Tips,用“AI”开启新生活

相信最近,很多朋友们都回归到了忙碌的生活节奏中。生活模式的切换,或多或少会带来身体或情绪状况的起伏。新技术正在为人们生活的方方面面带来便利。3个小Tips或许能让你也从新技术中获益,从身到心,用“AI”开启新生活。 关”A…...

【ROS | OpenCV】在ROS中实现多版本OpenCV、cv_bridge共存:安装与配置指南

在 Ubuntu 20.04 中,ROS Noetic 默认安装的 OpenCV 版本为 4.2.0。如果您需要确认系统中已安装的 OpenCV 版本,可以使用以下命令: sudo find / -iname "*opencv*"然而,许多开源算法都是基于 OpenCV 3 编写的&#xff0…...

Docker容器化技术(docker-compose示例:部署discuz论坛和wordpress博客,使用adminer管理数据库)

安装docker-compose [rootservice ~]# systemctl stop firewalld [rootservice ~]# setenforce 0 [rootservice ~]# systemctl start docker[rootservice ~]# wget https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64创建目录 [rootse…...

微分学<6>——Taylor公式

索引 Taylor公式Taylor公式的定性分析定理6.1 Taylor公式(Peano余项) Taylor公式的定量分析定理6.2 Taylor公式(Lagrange余项) Taylor公式 Taylor公式的定性分析 定理6.1 Taylor公式(Peano余项) 若函数 f ( x ) f\left ( x \right ) f(x)在 x 0 x_{0} x0​处的 n n n阶导数均…...

检索增强生成(RAG)应用的构建:LangChain与LlamaIndex的比较与选择

对于我要做RAG应用,我应该使用两者中的哪一个。或者说还是都使用? 在人工智能领域,检索增强生成(RAG)应用正变得越来越受欢迎,因为它们能够结合大型语言模型(LLMs)的自然语言处理能力…...

免费PDF转换和编辑工具 PDFgear 2.1.4

PDFgear是一款功能强大的 PDF 阅读及转换软件。 它支持多种文件格式的转换和编辑,同时还提供了丰富的功能模块,如签名、表单填写等,方便用户进行多样化的操作。 该软件界面简洁美观,操作简单易懂,适合不同层次的用户…...

uniapp,导航栏(切换项)有多项,溢出采取左滑右滑的形式展示

一、实现效果 当有多项的导航&#xff0c;或者说切换项&#xff0c;超出页面的宽度&#xff0c;我们采取可滑动的方式比较好一些&#xff01;并且在页面右边加个遮罩&#xff0c;模拟最右边有渐变效果&#xff01; 二、实现代码 html代码&#xff1a; <!-- 头部导航栏 --…...

计算机网络面经-什么是IPv4和IPv6?

前言 Internet协议&#xff08;IP&#xff09;是为连接到Internet网络的每个设备分配的数字地址。它类似于电话号码&#xff0c;是一种独特的数字组合&#xff0c;允许用户与他人通信。IP地址主要有两个主要功能。首先&#xff0c;有了IP&#xff0c;用户能够在Internet上被识别…...

彻底讲透:如何写sql能够有效的使用到复合索引?

在MySQL中&#xff0c;有效的使用复合索引需要确保查询条件按照索引定义的列顺序进行。以下是一个具体的例子&#xff1a; 假设我们有一个sales表&#xff0c;它有四个字段&#xff1a;customer_id、product_category、sale_date和amount。为了优化包含这些字段查询的性能&…...

在Spring Boot中如何处理跨域请求(CORS)?

什么是跨域&#xff1f; 跨域&#xff08;Cross-Origin Resource Sharing&#xff0c;CORS&#xff09;是一种机制&#xff0c;它允许在 Web 页面上运行的脚本能够请求从不同源&#xff08;域名、协议或端口&#xff09;的资源。在浏览器安全策略中&#xff0c;有一条称为同源…...

好就业三种专业#信息安全#云计算#网络工程

一、信息安全专业 根据2021年网络安全宣传周白皮书的观察结果&#xff0c;网络安全产业对于人才的需求正以高速增长的趋势呈现&#xff0c;当前网络安全行业存在着巨大的人才缺口&#xff0c;平均供求比例约为1:2。这一现象导致了资深人才的储备不足&#xff0c;并且新人才的培…...

electron-builder打包

打包配置&#xff1a; "build": {"appId": "cc11001100.electron.example-001", // 程序包名"copyright": "CC11001100", // 版权相关信息"productName": "example-001", // 安装包文件名"direct…...

SQLiteC/C++接口详细介绍sqlite3_stmt类(四)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;三&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;五&#xff09; 7. sqlite3_bind_parameter_count函数 sqlite3_bind_param…...

微信小程序自定义组件

微信小程序中的自定义组件是指在微信小程序中创建的可重用的、可复用的组件&#xff0c;它可以被多个页面使用。自定义组件可以帮助我们提高开发效率&#xff0c;提高代码的可维护性和可重用性。以下是微信小程序中自定义组件的使用方法&#xff1a; 一. 创建自定义组件 首先…...

python练习3

用户登录注册案例 while True: print("\t\t\t英雄商城登录界面\n") print("~*"*38) print("\t\t\t1.用户登录\n") print("\t\t\t2.用户注册\n") print("\t\t\t3.退出系统\n") print("~*"*38) choice input("…...

docker离线安装并修改存储目录

docker下载 根据cpu选择不同版本&#xff0c;正常x86就选x86_64 下载地址&#xff1a;https://download.docker.com/linux/static/stable/ docker安装 tar -zxvf arm-docker-25.0.4.tgz sudo cp docker/* /usr/bin/ rm -rf docker/* mkdir /etc/docker vi /etc/docker/daemo…...

【云原生 • Kubernetes】认识 k8s、k8s 架构、核心实战

文章目录 Kubernetes基础概念1. 是什么2. 架构2.1 工作方式2.2 组件架构 3. k8s组件创建集群步骤一 基础环境步骤二 安装kubelet、kubeadm、kubectl步骤三 主节点使用kubeadm引导集群步骤四 副节点加入主节点步骤五 部署dashboard Kubernetes核心实战1. 资源创建方式2. Namespa…...

墨菲安全在软件供应链安全领域阶段性总结及思考

向外看&#xff1a;墨菲安全在软件供应链安全领域的一些洞察、思考、行动 洞察 现状&挑战&#xff1a; 过去开发安全体系是无法解决软件供应链安全问题的&#xff1b;一些过去专注开发安全领域的厂商正在错误的引导行业用开发安全思维解决软件供应链安全问题&#xff0c;治…...

智慧公厕:卫生、便捷、安全的新时代厕所变革

在城市快速发展的背景下&#xff0c;公共厕所的建设和管理变得越来越重要。智慧公厕作为厕所变革的一项全新举措&#xff0c;通过建立公共厕所全面感知监测系统&#xff0c;以物联网、互联网、大数据、云计算、自动化控制技术为支撑&#xff0c;实现对公共厕所的智能化管理和运…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...