字符串:SET/GET/MSET/MGET/APPEND/SETRANGE/GETRANGE/STRLEN
数值:INCR
bitmap:SETBIT/BITCOUNT/BITPOS/BITOP
常常用来做计数器这类自增自减的功能,可用在粉丝数、微博数等。bitmap可以用来记录用户一年中是否登录的数据
可以当作栈、队列、数组、阻塞队列
LPUSH/RPUSH/LPOP/RPOP/LRANGE/LINDEX/LSET/LREM/LINSERT/LLEN
应用场景可以有微博的关注列表、粉丝列表、消息列表等。lrange函数,可以从某个元素开始读取多少个元素,可用来实现分页功能。
HSET/HMSET/HGET/HMGET/HKEYS/HVALS/HGETAL
适合存储对象类型信息,例如个人信息、商品信息等。
SADD/SMEMBERS/SREM/SINTER/SINTERSTORE/SUNION/SDIFF/SRANDMEMBER
因为set集合支持交集、并集操作,因此适合做共同好友等功能
使用跳跃表和字典来实现
ziplist:(1)有序集合保存的元素数量小于128个(2)有序集合保存的所有元素成员的长度都小于64字节,同时满足上述条件会使用ziplist编码(这两个条件的上限值是可以手动改的)
skiplist:不满足上述两种条件的列表对象使用skiplist编码
ZADD/ZRANGE/ZREVRANGE/ZSCORE/ZUNIONSTORE
游戏排名、微博热点话题等使用场景。
Redis的zset使用了跳跃表+字典作为底层数据结构。
跳跃表(skiplist) 是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
跳跃表支持平均O(logM)、最坏0(N) 复杂度的节点查找,还可以通过顺序性操作来批量处理节点。
在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树。

Redis中的跳跃表由zSkipList和zSkipListNode两个结构组成
https://blog.csdn.net/qq_38022739/article/details/125822653
Redis一共提供了8种淘汰策略,默认的策略为noeviction
LRU(2种)、随机淘汰(2种)、选择即将要过期的淘汰、LFU(2种)、禁止
简单来说,RDB是保存一份数据快照,数据快照是一份经过压缩的二进制文件。而AOF是保存一份写命令,当要恢复时重新执行写命令。
RDB持久化将某个时间点上的数据库状态保存到一个RDB文件中,RDB文件是一个经过压缩的二进制文件。有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE。SAVE命令会阻塞Redis服务器进程,BGSAVE命令会派生出一个子进程。
Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令(例如 [save 900 1] 表示服务器在900秒之内,对数据库进行了至少1次修改BGSAVE命令就会被执行)。
服务器中维持着一个dirty计数器,以及一个lastsave属性。
AOF持久化保存数据库状态的方法是将服务器执行的SET、SADD、RPUSH三个命令保存到AOF文件中。AOF持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。
AOF文件的更新频率通常比RDB文件的更新频率高,服务器会优先使用AOF来还原数据库状态。
Redis提供了AOF文件重写(rewrite)功能。Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新AOF文件不会包含任何浪费空间的冗余命令,所以新AOF文件的体积通常会比旧AOF文件的体积要小得多。
AOF重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有AOF文件进行任何读入、分析或者写入操作。
Redis将AOF重写程序放到子进程里执行,这样做可以同时达到两个目的
为了解决数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区(另外还有个AOF缓冲区)。在执行BGREWRITEAOF命令时,AOF重写缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新AOF文件的末尾,使得新的AOF文件保存的数据库状态与现有的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作。
AOF缓冲区的内容会定期被写入和同步到AOF文件中,这里需要注意区别于AOF重写缓冲区
文件事件(file event):
Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接,而文件事件就是服务器对套接字操作的抽象。服务器与客户端(或者其他服务器)的通信会产生相应的文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通信操作。
文件事件是对套接字操作的抽象,每当一个套接字准备好执行连接应答(accept)、写人、读取、关闭等操作时,就会产生一个文件事件。因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。
文件事件处理器由四个部分组成,它们分别是套接字、I/O 多路复用程序、文件事件分派器( dispatcher),以及事件处理器。
I/O多路复用程序负责监听多个套接字,并向文件事件分派器传送那些产生了事件的套接字。

I/O多路复用程序会将所有产生事件的套接字都放到一个队列里面,然后通过这个队列,以有序( sequentially)、同步( synchronously)、每次一个套接字的方式向文件事件分派器传送套接字。当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字。

Redis的I/O多路复用程序的所有功能都是通过包装常见的select、epoll、evport和kqueue这些I/O多路复用函数库来实现的

I/O多路复用程序可以监听多个套接字的ae.h/AE_READABLE 事件和ae.h/AE_WRITABLE事件,这两类事件和套接字操作之间的对应关系如下:
I/O多路复用程序允许服务器同时监听套接字的AE_READABLE事件和AE_WRITABLE事件,如果一个套接字同时产生了这两种事件,那么文件事件分派器会优先处理AE_READABLE事件,等到AE_ READABLE 事件处理完之后,才处理AE_WRITABLE 事件。
这也就是说,如果一个套接字又可读又可写的话,那么服务器将先读套接字,后写套接字。
这个处理器用于对连接服务器监听套接字的客户端进行应答。
当Redis服务器进行初始化的时候,程序会将这个连接应答处理器和服务器监听套接字的AE_READABLE事件关联起来,当有客户端用sys/socket.h/connect函数连接服务器监听套接字的时候,套接字就会产生AE_READABLE事件,引发连接应答处理器执行。
这个处理器负责从套接字中读人客户端发送的命令请求内容。
当一个客户端通过连接应答处理器成功连接到服务器之后,服务器会将客户端套接字的AE_READABLE 事件和命令请求处理器关联起来,当客户端向服务器发送命令请求的时候,套接字就会产生AE_READABLE事件,引发命令请求处理器执行,并执行相应的套接字读入操作。在客户端连接服务器的整个过程中,服务器都会一直为客户端套接字的AE_READABLE事件关联命令请求处理器。

这个处理器负责将服务器执行命令后得到的命令回复通过套接字返回给客户端。
当服务器有命令回复需要传送给客户端的时候,服务器会将客户端套接字的AE_WRITABLE事件和命令回复处理器关联起来,当客户端准备好接收服务器传回的命令回复时,就会产生AE_WRITABLE 事件,引发命令回复处理器执行,并执行相应的套接字写入操作。当命令回复发送完毕之后,服务器就会解除命令回复处理器与客户端套接字的AEWRITABLE事件之间的关联。


PSYNC命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)两种模式:
其中完整重同步用于处理初次复制情况:完整重同步的执行步骤是通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步。
而部分重同步则用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。
部分重同步功能由以下三个部分构成:
执行复制的双方——主服务器和从服务器会分别维护一个复制偏移量:
通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态:

复制积压缓冲区是由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB。
当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面。
主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。

当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:
除了复制偏移量和复制积压缓冲区之外,实现部分重同步还需要用到服务器运行ID( run ID):
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。
当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID:
通过向从服务器发送SLAVEOF命令,我们可以让一个从服务器去复制一个主服务器:
SLAVEOF < master_ ip> < master_ port>
从服务器首先要做的就是将客户端给定的主服务器IP地址以及端口保存到服务器状态的masterhost属性和masterport属性里面:


虽然主从服务器成功建立起了套接字连接,但双方并未使用该套接字进行过任何通信,通过发送PING命令可以检查套接字的读写状态是否正常。
因为复制工作接下来的几个步骤都必须在主服务器可以正常处理命令请求的状态下才能进行,通过发送PING命令可以检查主服务器能否正常处理命令请求。

在身份验证步骤之后,从服务器将执行命令向主服务器发送从服务器的监听端口号。

在这一步,从服务器将向主服务器发送PSYNC命令,执行同步操作,并将自己的数据库更新至主服务器数据库当前所处的状态。
当完成了同步之后,主从服务器就会进入命令传播阶段,这时主服务器只要一直将自己执行的写命令发送给从服务器,而从服务器只要一直接收并执行主服务器发来的写命令,就可以保证主从服务器一直保持一致了。
在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:
REPLCONF ACK < replication offset>
其中replication_ offset 是从服务器当前的复制偏移量。
发送REPLCONF ACK命令对于主从服务器有三个作用:
Sentinel (哨岗、哨兵)是Redis的高可用性( high availability) 解决方案:由一个或多个Sentinel实例( instance)组成的Sentinel系统( systemn)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
Sentinel本质上只是一个运行在特殊模式下的Redis服务器。当一个Sentinel启动时,它需要执行以下步骤:
Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的当前信息。

当Sentinel发现主服务器有新的从服务器出现时,Sentinel除了会为这个新的从服务器创建相应的实例结构之外,Sentinel还会创建连接到从服务器的命令连接和订阅连接。

在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送命令。命令中包含了Sentinel和主服务器有关的参数信息。
当Sentinel与一个主服务器或者从服务器建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送命令。
对于每个与Sentinel连接的服务器,Sentinel既通过命令连接向服务器的频道发送信息,又通过订阅连接从服务器的频道接收信息。

对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,这些信息会被用于更新其他Sentinel对发送信息Sentinel的认知,也会被用于更新其他Sentinel对被监视服务器的认知。

在默认情况下,Sentinel 会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。
Sentinel配置文件中的down-after-milliseconds选项指定了Sentinel判断实例进人主观下线所需的时间长度:如果一个实例在down-after-milliseconds毫秒内,连续向Sentinel返回无效回复,那么Sentinel会修改这个实例所对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,以此来表示这个实例已经进人主观下线状态。
当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel进行询问,看它们是否也认为主服务器已经进入了下线状态(可以是主观下线或者客观下线)。当Sentinel从其他Sentinel那里接收到足够数量(自定义配置)的已下线判断之后,Sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。
当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。
以下是Redis选举领头Sentinel 的规则和方法:
在选举产生出领头Sentinel之后,领头Sentinel将对已下线的主服务器执行故障转移操作,该操作包含以下三个步骤:
领头Sentinel会将已下线主服务器的所有从服务器保存到一个列表里面,然后按照以下规则,一项一项地对列表进行过滤:
一个Redis集群通常由多个节点(node)组成,在刚开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。
连接各个节点的工作可以使用CLUSTER MEET命令来完成:
CLUSTER MEET
Redis服务器在启动时会根据cluster-enabled配置选项是否为yes来决定是否开始服务器的集群模式,为yes则开启服务器的集群模式成为一个节点,为no则开启服务器的单机(stand alone)模式成为一个普通Redis服务器。
Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384(2^14)个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。
当数据库中的16384个槽都有节点在处理时,集群处于上线状态(ok);相反地,如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)。
通过向节点发送CLUSTER ADDSLOTS命令,可以将一个或多个槽指派(assign)给节点负责:
CLUSTER ADDSLOTS [slot …]
节点会将自己的slot数据通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽。
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理数据库键属于哪个槽,并检查这个槽是否指派给了自己:

Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。
重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:

ASK错误和MOVED错误都会导致客户端转向,它们的区别在于:
Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。
集群中的各个节点通过发送和接收消息(message)来进行通信,我们称发送消息的节点为发送者( sender),接收消息的节点为接收者( receiver )。
节点发送的消息主要有以下五种:
缓存雪崩:指的是缓存中大量key在同一时间失效,或者缓存服务器宕机导致的大量请求直接访问数据库。
使用redis集群+队列限流+随机过期时间
缓存击穿:热点key过期,大量访问请求到数据库。
设置热点key永不过期+限流
缓存穿透:请求不存在的key,会导致请求一定需要去访问数据库。
对key进行有效性判断,返回key-null保存在缓存中
脑裂:在主从集群中,同时有两个主节点他们都能接收写请求。而脑裂最直接影响的就是客户端不知道往哪个主节点写入数据,结果就是不同的客户端往不同的主节点写入数据。而且严重的会导致数据丢失。
产生脑裂的原因:
redis提供了两个配置项来限制主库的请求处理,分别是min-slaves-to-write 和 min-slaves-max-lag
min-slaves-to-write:这个配置项设置了主库能进行数据同步的最少从库数量
min-slaves-max-lag:这个配置项设置了主从库间进行数据复制时,从库给主库发送的ACK消息的最大延迟。简单地说,设置参数少于n个从服务器,或从服务器延迟都大于n秒则拒绝执行新的写命令。
有了这两个配置就可以轻松解决脑裂问题了,首先既然原主库是假故障,它在假故障期间是无法响应哨兵心跳的,也不能和从库进行同步,自然也就无法和从库进行 ACK 确认了。这样一来,min-slaves-to-write 和 min-slaves-max-lag 的组合要求就无法得到满足,原主库就会被限制接收客户端请求,客户端也就不能在原主库中写入新数据了。等到新主库上线时,就只有新主库能接收和处理客户端请求,此时,新写的数据会被直接写到新主库中。而原主库会被哨兵降为从库,即使它的数据被清空了,也不会有新数据丢失。
延时双删策略最终将redis删除掉,等下一次查询的时候会把最新的数据缓存到redis中,虽然解决了缓存不一致性,但是牺牲了性能,

先更新数据库再删缓存可能会导致以下问题

但由于读的效率通常比写的效率快很多,所以大部分是以下情况。能保证双写一致性