数据类型
一个数据类型都对应了很多种底层数据结构。以List为例,什么情况下是双向链表,反之又在什么情况下是压缩列表呢?还是说是并存状态?
1、Hash 和 ZSet 是数据量少采用压缩列表存储,数据量变大转为哈希表或跳表存储
2、但 List 不是这样,是并存的状态,List 是双向链表 + 压缩列表
key过期相关
请问Redis里面的惰性删除指的是什么,我看网上说是在访问到这个key的时候才执行删除才是惰性删除,但是这个专栏里面又表达的像惰性删除其实就是异步删除?
删除过期key是定时清理+懒惰删除。
懒惰删除默认是在主线程删除,并释放key内存的。
但在4.0+版本又做了优化,释放key内存可以放到了异步线程中去做了(lazy-free),目的是可以减少主线程释放内存的耗时,提升主线程处理性能。
也就是说4.0之前,懒惰删除就是同步删除。4.0优化后,懒惰删除就是同步删除全局hash表的键值对+子线程程执行释放内存,但需要开启lazy-free配置才生效。
有百万的key在一段时间过期,比如2个小时,如果应用订阅了key失效事件,在两个小时内,这些通知事件都会发出来吗?
不一定,如果没有请求到过期的key,那么Redis清理过期key是懒惰删除,懒惰删除有可能导致虽然key已经过期了,但还没有被扫描到,自然也就不会被清理。
maxmemory相关
我的Redis的maxmemory是设置4G,但我的服务器是8G,那么在Redis启动时,是直接分配给4G内存空间给Redis还是按需分配呢?如果我maxmemory是4G,但我的Redis数据是5G,那么Redis启动时是加载4G数据,还是加载4G数据+1G SWAP?
1、没数据的Redis,启动时按需申请内存,不会一下申请maxmemory内存的。
2、设置了maxnemory,Redis必定不会让你超过maxmemory,但slave除外。
如果不超过maxmemory,但是系统内存快满了,这种会刷到swap吧?
开了swap,会把内存数据换到swap上,如果不开swap,Redis进程会被OOM。
rehash相关
Redis里的hash容量到一定程度的时候,会做渐进式的rehash,Redis有没有提供一些可以配置,让我可以指定hash的大小,这样可以防止hash在较大的情况下,发生多次rehash的情况?
没有配置,hash扩容是定好的规则。到达什么阈值,开始扩容,没办法自己控制。
但6.0+版本提了一个新的函数,如果负载因子不超过1.618,在某些情况会限制rehash。
限制rehash虽然损失了查询性能,但可以有效防止淘汰过多数据(实例内存超过了maxmemory)。
Redis如何保证哈希表在扩容时的原子操作呢?如果把数据复制到新哈希表失败的话,原有的哈希表数据岂不是还存在,这个时候恢复后数据会向哪张哈希表写数据?
如果第一次拷贝后,哈希表2的哈希桶1再次超过了装载因子,但哈希表1中哈希桶2还有数据。这种情况也会去扩容哈希表1么?那原本没有渐进处理的数据是重新rehash放进来还是不会变?当然这种情况可能比较极端了
1、都是内存操作,内存没问题一般不会失败。
2、下一次rehash想要开始,必须等上一次完成
老师的文章里说渐进式rehash将拷贝分摊到客户端的多次请求上,是不是可以理解为请求命中了哈希表1的key,就把表1的数据rehash再分配到表2。那万一客户端迟迟不命中表1的某个key,表1的数据岂不是一直都不会迁移到表2?
每处理客户端请求,如果需要rehash,每次请求都会触发迁移数据,也就是说只要有请求进来,即使没有命中哈希表1的key,也会触发迁移一部分数据到哈希表2。
扩容的时候,假如哈希表1大小5g,那么哈希表2假如为原来的1.5倍,就是7.5g,如果Redis内存限制在15g,是不是会有一些内存因为扩容机制,没有得到利用呢?
如果限制Redis内存15g情况下,哈希表1已经到了10g,发现需要扩容,扩容为两倍,发现内存不够的情况下,应该怎么处理呢?
扩容是为了搞一个大一点的哈希桶,所以申请的内存只是新哈希桶所占内存,并不会申请新数据内存。
那即使每次申请的内存容量小,但总有个时间点,会发现内存不够了,但是还没到达限制的内存点,所以就干脆不申请了,继续在旧的哈希表里插入数据,最后通过lru再释放内存。
不是。如果实例设置了maxmemory最大10g内存,已经用了9.9g,扩容申请新的哈希桶,需要200m,Redis会照常申请,开始rehash扩容。
然后Redis发现,实例现在占用的内存,超过了maxmemory,那Redis会触发数据淘汰,开始淘汰删除key,直到把内存降到10g以下,也就是说,在这期间需要淘汰100m的key。
淘汰100m的key,有什么后果?后果就是,会阻塞住整个Redis(淘汰删除key是在主线程中执行的),这里是一个坑,需要格外注意rehash申请内存,撞上了正好超过maxmemory上限,就会引发此问题。
这个问题在3.x-6.0版本一直存在,好像在6.0+以上版本,才修复了这个问题,具体可以查阅6.0版本更新记录确认一下。
如果内存超过了maxmemory,但没有设置淘汰策略,会发生什么?
新写入的数据,会给客户端返回写入失败。
那什么情况下,Redis才会OOM?
只有机器内存无法申请到的时候:
- 整个机器内存不够了
- NUMA架构下,某一个内存节点内存不够,又配置的不允许去其他内存节点申请内存
Redis在RDB或AOF重写时是不允许进行rehash的,如果现在进行rehash,又触发了RDB或AOF重写条件会怎么处理呢?
这个问题非常好。
1、如果正在RDB和AOF rewrite,Redis默认会关闭rehash,等RDB和AOF rewrite完成后,Redis再打开rehash开关。但是有一个例外,如果全局哈希表冲突概率已经很大(有一个阈值),超过这个阈值,也会强制触发rehash(不管是否在RDB和AOF rewrite)
2、如果正在rehash,是允许RDB和AOF rewrite的。
3、为什么RDB和AOF rewrite期间,默认不允许rehash?因为RDB和AOF rewrite时,父进程rehash需要申请新的哈希表,这会造成父进程大量COW,影响父进程的性能
4、为什么rehash期间,允许RDB和AOF rewrite?因为rehash已经开始,说明新的哈希表内存已经申请完成了,之后的rehash过程只是迁移哈希桶下的数据指针,不涉及到内存申请了,所以RDB和AOF rewrite没影响。
持久化
RDB
为啥RDB 要 fork 子进程而不是线程?
1、先想一下RDB的目的是什么?就是把内存数据持久化到磁盘上,而且只持久化截止某一时刻的数据即可,不关心之后的数据怎么改(内存快照)
2、性能:如果用子线程做的话,主线程写,其他线程读,然后子线程数据写磁盘,有资源竞争,需要加锁,加锁会降低Redis性能,而且在实现上很复杂,成本高。
3、基于以上考虑,fork一个子进程来搞,最经济,成本也最低。因为fork子进程,操作系统把这些事都做好了,有内存快照数据,没有锁竞争,子进程怎么写磁盘也不会影响父进程,还有COW不影响主进程写数据,一举多得。
如果上一次生成RDB快照还没执行完,又触发了持久化策略,这个时候是顺序执行等上一次持久化完成?还是并行处理?
等上一次RDB执行完,才能触发执行下一次RDB。
关于Copy On write问题:数据持久化fork子进程时,子进程不会一次copy所有数据,而是在修改时触发Copy On write。假设主线程中有1000条数据,fork创建子进程后,主线程有请求新增了100条,修改了200条,这些内存是如何在主进程和子进程分配的?
1、首先要理解 Copy On Write 含义:即写时复制,谁写谁复制
2、fork子进程,此时的子进程和父进程会指向相同的地址空间,当父进程有新的写请求进来,它想要修改数据,那么它就把需要修改的key的内存,拷贝一份出来,再修改这块新内存的数据,此时父进程内存地址就会指向这个新申请的内存空间
3、在这期间,子进程不会修改任何数据,所以不会分配任何新的内存,它依旧指向父进程那些数据的内存地址空间,这个过程是操作系统层面做好的。
那fork期间会阻塞父进程吗?为什么会阻塞?
1、fork完成之前,会阻塞父进程,主要是父进程需要拷贝进程中的内存页表给子进程,每个进程都要有自己的内存页表,所以这个父子进程无法共享,必须要拷贝一份
2、拷贝内存页表也需要花费时间,进程占用的内存越大,拷贝时间越久
RDB写入的时候,通过主线程fork出bgsave子进程时,进行写入RDB文件,此时主线程也可以接受的写操作,那么主线程接收新的写操作,bgsave子进程还会再把这个数据,写入到RDB文件吗?
不会。RDB的目的是,只要一份内存快照,即只要fork那一瞬间,父进程所拥有的数据,fork完成后子进程指向父进程的所有内存数据地址空间,所以就与父进程共享数据了,此时子进程把这些数据scan出来,持久化到磁盘就可以了,不需要关心父进程有没有写入新数据。
子进程做RDB期间,父进程写入新数据,父进程做Copy On Write申请新的内存,那子进程完成RDB后,进程退出,内存回收是怎样的?
子进程退出时,如果它指向的内存数据,没有被父进程修改过(对于这块数据,父进程没有做COW),那么这块内存数据,还是归父进程所有,子进程不会回收。
如果在子进程RDB期间,父进程有新数据写入或修改,对一部分key的内存做了COW,那这些key的内存,父子进程各自独立,子进程退出时,就会回收它指向的这些内存空间。
AOF
AOF 中开启 always 刷盘策略也会存在数据丢失吗?
可能会。Redis 是先操作内存,后写AOF磁盘日志。比如 Redis 内存执行完了,去刷盘的时候宕机了就会导致数据丢失。
Redis在bgsave或rewriteAOF期间引起CPU飚高,有应对方案吗?
这是正常情况,在这期间会消耗比较多的CPU,如果实例比较大,持续时间越久。因为在这期间,子进程需要把进程中的所有数据都扫出来,然后写到磁盘上,这个扫描过程是需要耗费CPU资源的。
AOF假如写在内存A上,然后fork子进程进行AOF重写,因为copy on write机制,AOF重写子进程也是指向内存A。如果此时有新的key写入,父进程将其写入在新拷贝的内存B上,然后父子进程的内存逐渐分离。AOF重写子进程写完后将其替换AOF日志文件,然后释放内存A。父进程随后就一直使用内存B,这样理解对吗?
正确。
AOF重写的时候,如果重写缓冲区满了,怎么处理?是不是直接放弃本次的重写了?
AOF重写缓冲区不会满,是个链表,只要内存不超过设置的maxmemory。
如果超过maxmemory,执行配置的淘汰策略。
AOF配置为每秒刷盘,有可能阻塞Redis,影响性能吗?
有可能的。
AOF 配置为每秒刷盘,具体逻辑是这样的:
1、Redis 主线程把命令写到 AOF page cache(调用 write 系统调用)
2、Redis 后台线程每间隔 1 秒,把 AOF page cache 持久化到磁盘(调用 fsync 系统调用)
如果 2 执行时,迟迟没有成功,那么 1 执行时就会阻塞住,原因是在操作同一个 fd 时,fsync 和 write互斥的,一方必须等待另一方完成。
步骤 2 执行不成功的原因在于:机器的磁盘 IO 负载非常高(可能有别的程序在疯狂写磁盘,把磁盘带宽占满了),此时 1 在执行时,就会阻塞等待,从而影响到了主线程,进而影响整个 Redis 性能。
具体可参见 Redis 源码 aof.c,搜索:Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
主从复制
主从节点全量复制,会同步RDB文件。然后主节点继续接受写请求,这些写命令会存到复制缓冲区。RDB传输完成后,再把复制缓冲区的命令,发到各个从节点执行,那在发复制缓冲区里的命令时如果主节点又接受了写请求,这些新的写请求是怎么发给从节点的?
全量同步过程:
1、slave向master发起同步请求,slave会告知master自己的offset,如果是第一次,offset=-1表示需要全量同步
2、slave连上了master,master会给slave分配一个client buffer(这个就是复制缓冲区replication buffer)
3、master fork子进程dump RDB文件,dump完成后告诉父进程,父进程把RDB发给slave(通过slave的socket发过去)
4、在master dump RDB期间,master所有写命令,都写到这个client buffer(replication buffer),先积压在这
5、RDB发送完成,master把client buffer(replication buffer)积压的命令,都发给slave(写slave socket),这样主从数据就追平了
6、之后master写请求,还是都写到client buffer(replication buffer),然后实时传播给slave,主从保持一致
那backlog buffer干嘛的?
1、只要master下面有slave存在,master就会分配一块内存,这块内存就是backlog buffer
2、只要master有写命令进来,也都会写一份到backlog buffer里,但这个buffer是固定大小的环形缓冲区,写满就会覆盖旧数据
3、它的作用在于,主从复制意外中断了,slave再次向master发起同步请求,slave会告知master要从哪开始复制(offset),master在backlog buffer里找,数据能否接得上,接得上就把差异的增量数据发给slave,发的过程也是先写到上面所说的client buffer(replication buffer),然后写slave socket达到主从同步
4、如果接不上,走全量同步
重点:
1、replication buffer:别纠结这个名字,它就是master给client分配的一个buffer,因为是用于主从同步,所以大家才这么叫它,它的意义在于master和slave保持数据一致的传播通道
2、backlog buffer:主要作用在于,主从断开后,是否能增量同步
如果RDB很大,传输给从库时间很长,那replication buffer会不会积压很多数据,那这个buffer内存会不会无限大?需不需要清理?
不会清理。这个buffer大小是可配置的,如果超过了配置的大小,主库会强制断开这个从库的连接,这就会导致主从同步中断。
配置项:client-output-buffer-limit replica 256mb 64mb 60 这个是默认的buffer大小(低版本叫 client-output-buffer-limit slave)。
所以如果RDB很大,这个buffer要配置大一些,防止写请求太大,主从同步失败。
如果主从同步失败,从库又会发起全量复制请求,很有可能又会因为buffer超限导致失败,引发复制风暴。
repl-diskless-sync配置项要不要开启?开启后,主从全量数据复制,不会生成RDB文件,而是master直接通过网络socket,把全量数据发给slave。主从节点是内网部署的,是不是使用这种配置项更好?
关于这个配置,可以看一下配置文件关于这一项配置注释说明,写得非常清晰了。简单讲如下:
1、repl-diskless-sync = no,表示主从全量同步,master会先在磁盘上生成RDB文件,然后master读这个文件,把这个RDB文件数据发给slave,达到主从全量同步
2、repl-diskless-sync = yes,表示master不会在磁盘生成RDB文件,而是直接把全量数据,通过socket发给slave,这种也叫无盘复制
各有什么优劣?
1、用磁盘RDB文件的方式做全量同步,好处是在RDB文件写磁盘没有完成之前,在这期间,如果还有其他slave请求全量同步,那么这些slave都可以直接复用这个RDB数据,master只做一次RDB即可
2、如果采用无盘复制,master不生成RDB文件在磁盘,缺点是,master给一个slave开始传输全量数据了,其他slave又连上来需要全量复制,master还需要扫一遍整个实例,然后给这些slave发数据,没办法复用
3、所以,使用无盘复制时,Redis还提供了一个配置repl-diskless-sync-delay,表示开始给slave传输数据之前,等待一会再发数据给slave,如果此时有其他slave连上来请求全量同步,那么在这等待的期间,就可以兼顾其他slave,减少扫描整个实例的次数,降低同步成本
什么情况下会导致主从数据不一致?
5.0以下版本,slave如果提前master超过maxmemory,那么slave自己会淘汰数据,此时主从数据不一致。
5.0增加了一个配置replica-ignore-maxmemory,可以控制是否让slave淘汰数据,默认不淘汰,可以为了保证主从完全一致。
那什么情况下slave会提前master超过maxmemory?
比如slave快要达到maxmemory时,在slave上执行monitor命令,此时这个执行monitor的client输出缓冲区会占用很多内存(实例QPS比较大的情况下很明显),有可能导致slave提前超过maxmemory,需要格外注意。
询问一个Redis数据丢失的问题,如下图:
第一种是主从同步的数据丢失,第二种是哨兵选举导致的脑裂的数据丢失。这是网上找到的解决方案,想问下实战是这样解决的吗?这样会导致主库不能写操作,我感觉是个很危险的行为。
看你具体业务场景,如果主从不一致,对业务影响很大,宁可业务不可用也要保证一致性,就可以用这种方案,缺点是业务有损失。
如果需要优先保证业务可用性,只能降低一致性的要求。然后从运维层面保证主从同步的可靠性,降低出问题的概率。
场景1出问题的原因是,从库有问题,或者主从网络存在问题。那最好解决从库问题和主从之间的网络问题。
从另一个角度说,主从即使不一致,如果Redis只是当做缓存来用,后面还有数据库兜底,所以对业务基本没影响。如果没有数据库兜底,那你就需要把握好业务了,评估丢数据的损失,从运维层面规避降低出问题的概率。
场景2脑裂的问题,细节很多。Redis主从集群本身不是分布式强一致的,所以遇到这种问题也没办法,还是得运维层面做好,例如哨兵怎么部署,要不要和master在一台机器之类的。
能应对脑裂的集群,内部都是有共识协议保证的。比如master脑裂被孤立了,自己退下来不让写入类似这种规则,但是Redis没有。
AOF和RDB同时开启,那么数据恢复是按照什么来恢复的?是4.0之前使用AOF来恢复,4.0之后是按照RDB来恢复吗?
如果同时开启,恢复时优先使用AOF恢复。因为AOF数据比RDB全。
哨兵
现在Redis哨兵集群中有有三个哨兵,Redis一主两从。现在主从中有一个主挂了,然后哨兵会选一个主哨兵去切换。问题是,下一次Redis主节点挂了后,还会再次选一个主哨兵去切换吗?还是说由上一次的主哨兵去切换呢?
每次都会选一次哨兵领导者去执行切换。
那每次选会不会有时间损耗,从而影响性能?
哨兵选举很快的。哨兵判定主挂了,这期间也需要时间。而哨兵选举的时间,跟这个时间比,几乎可以忽略不计了。
问题场景:1、发生脑裂(主库假故障,主从切换期间,旧主库恢复和客户端通信,客户端把数据写到旧主库,主从切换完成,旧主库降为从库,清数据,全量同步)。
2、主库真故障,主从切换期间丢失数据
这两种情况不是都会丢失主从切换期间的数据吗?两者区别是不是主从切换完成到旧主库变成从库这段期间,是新主库服务还是新主库和旧主库同时服务?
1、场景1发生时,业务应用不报错,以为写成功了,过一会却查不到数据了,结果不符合预期。
2、场景2主库挂了,写请求直接失败,用户可以感知到,自己可以重试,数据是符合预期的。
宁可让2发生,也不要1,1排查起来很困难的。
锁
锁被误释放的问题:锁未过期之前,在什么情况下锁会被误释放?
一个客户端,自己加锁后,执行业务逻辑,但自己却阻塞了很久,然后锁过期了,这个时候别的客户端就可以获取到锁,然后阻塞的客户端执行业务结束了,再去删除锁的时候,会释放别人的锁,那别人的锁就相当于失效了。
redis 主从部署,主库执行 setnx 加锁,然后主库挂了,这个命令还未同步到从库,会导致什么结果?
由于命令还未同步到从库,所以这时会导致锁失效(从库顶上来,对于客户端来说却没有成功加锁)。
所以Redis作者才提出了Redlock来避免这种情况,可查一下资料了解下Redlock原理,并不复杂。
事物
lua脚本可以会保证原子性吗?实际测试中,我给了错误的参数,部分命令运行成功,部分命令运行失败,最终还是不符合原子?
lua只保证了隔离性,并不保证原子性。Redis和MySQL的原子性,不是一回事。
最近面试被问到Redis执行 lua脚本如何保证原子性的?我说执行lua脚本,实际就是开启了一个事物,就保证了原子性。面试官觉得我答的不完全对。这个要怎么答?
因为Redis处理请求是单线程的,单线程可以保证执行lua脚本时不会被别的请求打断(隔离性)。
分片集群
Redis cluster 添加新节点,是不是要手动迁移数据到新节点中, 集群能自动转移数据过去么?
需要手动触发迁移数据。
Slot 2 正在从实例 2 往实例 3 迁移,key1 已经迁移过去,key3 和 还在实例 2。如果客户端向实例 2 请求 key3时,因为key 3还在实例2中,那么此时客户端是需要等待实例 2把key3迁移到实例 3,才能获取到key3的数据吗?
key还在原来的实例,直接返回。
现在实际生产中,是Redis cluster用的多,还是codis用的多?
前期codis多,因为Redis cluster不稳定。现在cluster是主流了,用的越来越多了,而且codis已经不维护了。
建议直接上 Redis cluster,没有历史包袱。
Redis Cluster的数据迁移是在主线程中进行的吗,也就是说在迁移某个key的是时候,这个key所对应slot的源节点和目标节点都无法响应任何操作?还是说仅仅是这个slot里面的数据被阻塞,其他slot能被正常访问?
一个key迁移过程中,整个source和target实例都会阻塞,如果一个key很小,迁移时几乎不影响性能,如果是bigkey,会增加阻塞时间,影响性能。
缓存和数据库一致性
先修改了数据库中的值后,为什么删除缓存中数据比更新缓存中数据好呢?
如果去更新缓存,更新过程中数据源又被其他请求再次修改的话,缓存又要面临处理多次赋值的复杂时序问题。所以直接失效缓存,等下次用到该数据时自动回填,期间无论数据源中的值被改了多少次都不会造成任何影响。
缓冲区
客户端缓冲区有个问题,服务器端处理请求的速度过慢,例如,Redis 主线程出现了间歇性阻塞,无法及时处理正常发送的请求,导致客户端发送的请求在缓冲区越积越多。这个我有点没理解,抛开阻塞不说,如果客户端传过来一个大key,大于32k,这个时候客户端缓冲区流溢出了吗?还有如果不溢出,那么报文不完整,Redis如何处理这个请求呢?
Redis 的客户端输入缓冲区大小的上限阈值,在代码中就设定为了 1GB。也就是说,Redis 服务器端允许为每个客户端最多暂存 1GB 的命令和数据。1GB 的大小,对于一般的生产环境已经是比较合适的了。一方面,这个大小对于处理绝大部分客户端的请求已经够用了;另一方面,如果再大的话,Redis 就有可能因为客户端占用了过多的内存资源而崩溃。
client的output buffer特别高,导致占用内存非常大,可能的原因?
例如client执行了某个命令后,server返回大量数据,但client没有及时去读取和处理这些数据,也没有断开这个连接,导致server发给client的数据积压在输出缓冲区中。
或者client执行pipeline,一次发太多数据给server了,server返回给client的数据也非常多,client来不及处理就积压在缓冲区了,这是典型的业务使用问题,client需要控制pipeline发送命令的数量。
如何查看每一个client的output buffer有多大?
执行client list命令,可以看到每个client的omem,即是client output buffer。
性能问题排查和调优
如何排查Redis变慢问题,如何性能调优?
直接看我写的这篇文章,全网最全性能分析教程:mp.weixin.qq.com/s/Qc4t_-_pL…(Redis为什么变慢了?一文讲透如何排查Redis性能问题 | 万字长文)
其它
如何理解Redis数据持久化、主从复制、哨兵、分片集群它们之间的联系?
看我写的这篇文章,把这几个知识点串联起来,通俗易懂:mp.weixin.qq.com/s/q79ji-cgf…
为什么master和slave执行sacn命令,返回的结果和数量不一样?
master和slave的全局哈希表,哈希桶的分布可能是不同的,而且scan扫描的结果也是无序的。
不能只看结果,要重点看cursor返回是不是0,不是0需要继续执行scan,直到返回0才拿到了全部数据。而且一次最多返回count元素,有可能少于count,关键看cursor的值。
最近面试被问到Redis执行 lua脚本如何保证原子性的?我说执行lua脚本,实际就是开启了一个事物,就保证了原子性。面试官觉得我答的不完全对。这个要怎么答?
因为Redis处理请求是单线程的,单线程可以保证执行lua脚本时不会被别的请求打断(隔离性)。
Redis集群的节点的大小2-4g最合适是吗?
这是个经验值,意思是越小越不容易发生阻塞风险,比如实例很小,执行RDB就很快。
但是实例很小,那部署的节点数量会比较多,维护成本高一些。我个人觉得10G以下问题也不大,这样节点数也不是很多,维护起来方便些。
重点注意的是,实例越大,实例阻塞风险越大,而且维护会变得越来越困难,出问题的概率也大。
我理解的pipeline 好像只会减少网络传输的时间,并不能保证发送的一批命令不会被其他命令穿插执行?如果pipeline提交的命令较多呢?
还是不一样的,pipline关注的是打包把多个命令发到服务端。事务关注的如何保证ACID这些。
Pipeline一次发多个命令,服务端解析一个命令,执行一次。而事务是必须收到exec才会执行。
在一定程度上,pipline能实现和事务一样的效果,但是同样这么操作,如果遇到执行一半命令,Redis崩溃了,pipeline执行了多少命令就是多少,但事务因为有multi标记,在AOF恢复的时候可以把执行一半的命令撤销掉,这是不一样的地方。另外事务可以配合watch使用,pipeline不行。
使用pipeline批量发数据给Redis,是不是功能上跟multi/exec一样了?
multi/exec可以一个个发命令到服务端,也可以打包一次全发过去,一次全发就是配合了pipeline使用的。
关于CPU绑核:一般业务场景下,应该用不到绑核吧?而且swap线上一般都是关闭的吧?
除非追求更好的性能,一般不绑核,绑核对于DBA要求高,了解原理才可以操作,否则会达到反效果。另外,Swap线上不一定是关的,很多服务器默认都是开的。
Redis6.0多线程处理,是先把socket连接放入全局队列,然后阻塞,让io多线程解析处理,然后主线程处理读写命令,写到缓冲区,再阻塞,交给io多线程放入socket缓冲区,然后,主线程清空全局队列,返回。我的疑问是,这个全局队列在命令处理期间,始终只有一个socket吗,如果不是的话,那么最后清空全局队列,会不会把其他没完成的socket也清理了呢。
所有请求处理完成后,才会清理的。
为什么推荐只使用 db0,从而减少 SELECT 命令的消耗,select命令为什么会坑?select命令是客户端发送过来执行的,想知道如果一个客户端固定用一个db,并且用连接池,不会每次都执行select吧?
除非客户端连接池,1个db建一个连接操作Redis,如果是一个连接会操作多个db的话,每次执行时,肯定需要先执行一次SELECT命令的。如果QPS很高的话,执行SELECT命令也是消耗。
另一方面,既然数据要存在不同db下,目的就是为了做隔离,其实更好的方式是拆分实例,不同实例保存不同业务线的数据,这样每个实例承担的QPS也变高了,后期也方便DBA运维。
最后,Redis cluster只支持使用db0,如果你后期想往cluster上迁移,使用了多个db就会很麻烦,还得拆分数据,所以只建议使用db0,减少SELECT消耗,也便于迁移到Redis cluster。
操作系统的net.core.somaxconn如果设置为512,而Redis客户端最大允许10000连接,这个客户端连接是不是受限于somaxconn,最多不会超过512呢?
客户端向服务端建立一个TCP连接时,服务端先把连接放到半连接队列,然后再放到全连接队列(TCP三次握手的细节很多,这里简化了,你可以去查细节),somaxconn是用于控制这两个队列最大长度的。
这些队列有什么用?有队列的好处是,当多个连接同时打到服务端时,服务端只能一个个处理连接,还没处理到的连接不能丢弃吧?所以服务端有这样一个队列来缓冲,把连接都放到这里来,应用层accept时就从全连接队列拿一个出来进行交互。
至于Redis配置文件设置的最大连接数,Redis服务端每拿出来连接和客户端交互,都可以在应用层记录现在服务的连接数,如果服务的连接数已经超过了配置的,那么就可以直接拒绝掉。你看到的配置文件连接数限制,是在这控制的。
一个是TCP层的,一个是应用层的。
布隆过滤器第一次还是会直接访问缓存,缓存没有再访问DB上,如果未获取到数据,就在布隆过滤器上进行添加,下次有相同的请求的时候,直接屏蔽该请求。是这个做法吗?
不是,请求进来先查布隆过滤器,布隆没有,直接返回。布隆存在,查缓存,查DB,同时在布隆里设置标记数据存在。查缓存和查DB,看你缓存是否有数据,有的话只查缓存就可以,不需要查DB。
示例代码:
if not bloom_filter.exists(user_id): return null user = query_cache(user_id) if not user: user = query_db(user_id) set_cache(user_id, user) bloom_filter.set(user_id) return user 复制代码
重点:
1、如果是新业务,直接按照上面伪代码写就可以
2、如果是老业务,想要上一层布隆过滤无效请求,需要扫数据库把已存在的数据刷到布隆里
3、当然,每次新增数据也同步设置标记到布隆里