如何解决Redis缓存与数据库不一致
1、什么是缓存一致性分为两种情况,如果缓存中存在数据,那么缓存中的数据与数据库数据一致,如果缓存中没有数据 ,那么数据库中的数据就要是最新的。可能针对第二点有的人会有一些疑问,我的理解是,如果数据的修改是在缓存中进行,当缓存满后这些数据被淘汰时才写入数据库,然后清除这部分数据。如果写入数据库发生问题,而缓存又被清除,那么这里就发生了缓存不一致的问题,因为数据库中的数据不是最新的。
2、缓存不一致的原因以下几种情况会导致缓存不一致:
1、如果业务采用异步写回策略,即上面提到的,修改数据时只将数据在缓存中修改,并不去修改数据库,等到数据从缓存中淘汰,再写入数据库。这种策略下如果写入数据库异常,就会导致不一致。
2、删除数据时(不考虑并发)。假设应用先删除缓存中的数据,然后再删除数据库中的数据,此时如果缓存删除成功,然后数据库删除失败,那么下次访问,会先走缓存,缓存未命中,然后去数据库查询,但是数据库查到的是旧值,导致不一致。
如果是先删除数据库中的数据,再删除缓存,此时一种情况数据库删除成功,缓存删除失败,那么会导致数据库中的值是新的,而缓存中的值是旧的,而查询又先走缓存,也是不一致的情况 ...
Redis哨兵
是哨兵是Redis高可用性解决方案:由一个或多个哨兵实例组成的哨兵系统可以监视一个或多个主服务器以及主服务器对应的所有从服务器。当主服务下线,哨兵会发现并发起切换,选取一个从服务器升级为主服务器。
如果server1下线后又重新上线,那么哨兵会让它成为新的主服务器的从服务器。
启动并初始化哨兵哨兵其实也是一台Redis服务器,不过他比较特殊,并不处理请求,所有它的启动和初始化和普通的有所不同。它不需要加载RDB和AOF文件,
Redis的主从复制
在redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,被复制的服务器就是主服务器,另一个就是从服务器。
旧版复制功能的实现旧版指的是2.8版本以前的复制,分为两个操作,同步和命令传播。
同步将从服务器的状态更新至主服务器当前所处的状态。
同步过程如下:
1、从服务器发起同步请求(发送SYNC命令)。
2、主服务器在后台生成RDB文件,并使用一个缓冲区记录生成RDB文件时产生的新数据。
3、当生成完成后,将RDB文件发送给从服务器。从服务器接收并载入RDB文件。
4、将缓冲区数据发送给从服务器,从服务器接收并写入。
命令传播主服务器状态被修改,导致主从状态不一致,用命令传播使他们一致。
这个并不会像上面那样生成RDB文件,只会发送一条命令。
比如刚刚同步完数据,目前主从数据一致。但是主库执行了一条del命令,然后主从就不一致了,把这条del命令发给从库的行为就叫做命令传播。
旧版的缺陷当第一次全量同步时,可以很好的完成该工作。但是当全量同步中途出错,导致同步结束,就会有一个问题,当从库重连后,需要重新进行全量同步,还需要生成新的RD ...
Redis服务器
请求命令的执行过程客户端给服务器发送一个命令请求的过程如下:
1、客户端向服务器发送命令请求
2、服务器接受并处理客户端请求,在数据库中进行设置操作,并产生回复。
3、服务器将命令回复发送给客户端
4、客户端收到回复命令并显示给用户。
发送命令请求客户端输入请求后,客户端会将这个请求命令转换成协议格式,然后通过连接到服务器的套接字,将协议格式发送给服务器。
读取命令请求读取命令时,服务器会执行一下操作:
1、读取协议格式的命令,将其保存到客户端状态的输入缓冲区中。
2、对输入缓冲区的内容进行解析,提取其中包含的命令,以及命令的个数,保存到客户端状态的argv和argc的属性当中。
3、调用命令执行器,执行命令。
具体结构如下:
命令执行器(1):查找命令实现执行器要做的第一件事就是根据客户端状态的argv[0]参数,在命令表中查找指定的命令,并将命令保存在客户端状态的cmd属性中。
一个命令表的结构如下:
设置客户端状态如下:
命令执行器(2):执行预备操作到这里,服务器已经将客户端执行命令所需要的函数,参数以及参数个数都收集到了。但是在真正执行命令时,还需要一些预备操作:
1、 ...
Redis客户端
Redis是典型的一对多服务器程序,一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,服务器可以处理请求并回复。
redis中,所有的客户端信息都保存在redisServer的clients结构体中。
12345678910111213141516171819202122struct redisServer { // 一个数组,保存着服务器中所有的数据库 redisDb *db; // 服务器数据库的数量 int dbnum; // 记录了保存条件的数组 struct saveparam *saveparam; // 修改计数器 long long dirty; // 上一次执行保存的时间 time_t lastsave; // AOF缓冲区 sds aof_buf; // 一个链表,保存了所有客户端状态 list *clients;};
一个具体的结构如下:
客户端属性客户端属性分为两类:
一类是比较普 ...
Redis事件
Redis服务器是事件驱动程序,服务器需要处理以下两类事件:
1、文件事件。
Redis服务器通过套接字与客户端或者其他Redis服务器进行连接,而文件事件就是服务器对套接字的抽象。服务器与客户端或其他服务器的通信会产生相应的文件事件,而服务器就是通过监听并处理这些事件来完成网络通信的。
2、时间时间。
Redis服务器中一些操作需要在给定时间点执行,而时间事件就是服务器对这类定时操作的抽象。
文件事件文件事件处理器采用I/O多路复用程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答,读取,写入,关闭等操作时,与操作对应的文件事件就会产生,这时文件事件处理器就会根据套接字关联好的事件处理器来处理这些事件。
文件事件处理器的构成有四个部分:
1、套接字
2、I/O多路复用程序
3、文件事件分派器
4、事件处理器
具体结构如下:
其中,I/O多路复用程序监听多个套接字,并向文件事件分派器传递那些产生事件的套接字。尽管多个文件事件会并发出现,但是I/O多路复用程序总是将所有产生事件的 ...
AOF持久化设计
RDB持久化会直接保存某一时刻的数据快照,而AOF持久化是直接记录Redis执行过的命令来记录数据库状态。
AOF持久化的实现AOF持久化功能可以分为命令追加,文件写入,文件同步这三个步骤。
命令追加如果AOF功能处于开启状态,当Redis执行了一个写命令后,会将执行的写命令追加到aof_buf缓冲区末尾。
1234567891011121314151617181920// 这个redisServer保存了书中从前到后所展现过的所有结构struct redisServer { // 一个数组,保存着服务器中所有的数据库 redisDb *db; // 服务器数据库的数量 int dbnum; // 记录了保存条件的数组 struct saveparam *saveparam; // 修改计数器 long long dirty; // 上一次执行保存的时间 time_t lastsave; // AOF缓冲区 sds aof_buf;};
文件写入Red ...
Redis的RDB持久化设计
什么是RDB持久化RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
RDB文件的创建与载入redis中有两个命令可以生成RDB文件,分别是SAVE命令以及BGSAVE命令。
SAVE命令会阻塞redis进程,直到RDB文件生成后,redis才可以继续处理请求。
BGSAVE命令会委派一个子进程,由子进程来创建RDB文件,然后主进程继续处理请求。
RDB文件的载入是在服务器启动时自行载入,没有命令。
注意:如果同时开启AOF和RDB这两种持久化方法,那么会优先使用AOF。
执行BGSAVE时服务器状态在执行BGSAVE命令时,为了避免竞争,服务器在此期间收到SAVE命令和BGSAVE命令会直接拒绝。
而BGREWRITEAOF不能和BGSAVE命令一起执行。
如果此时服务器正在执行BGSAVE命令,那么他会把BGREWRITEAOF延迟到BGSAVE命令执行完在执行。如果在执行BGREWRITEAOF,那么BGSAVE命令会被拒绝。
自动间隔设计redis允许用户设置每隔一段时间执行一次BGSAVE命令。使用save命令即可。具体操作如下:
12// 意味着每900秒,有 ...
Redis数据库结构
具体结构12345678910111213141516171819struct redisServer { // 一个数组,保存着服务器中所有的数据库 redisDb *db; // 服务器数据库的数量 int dbnum;}typedef struct redisClient { // 记录客户端当前正在使用的数据库 redisDb *db; } redisClient;typedef struct redisDb { // 数据库键空间,保存着数据库中的所有键值对 dict *dict; } redisDb;
一个具体结构如下:
总的来说,就是一个redis实例,会有一个redisServer结构体,该结构体中db是一个数组,数组每一个元素代表一个数据库,每个db结构体就是一个具体的存放键值对的数据库。每一个redisClient结构体代表了一个客户端,该客户端指针会指向redisServer结构体db数组中的一个,代表使用该数据库。其中dict就 ...
Redis对象
Redis中有那些对象redis中有简单动态字符串(SDS),双端链表,字典,压缩列表,整数集合等数据结构,但是redis并没有直接采用这些数据结构来构成键值对数据库,而是基于这些数据结构创建了一个对象系统。包含了字符串对象,列表对象,哈希对象,集合对象,有序集合对象。而且redis对象系统还实现了基于引用计数器的内存回收机制,并且通过引用计数技术实现了对象共享。
redis中每个对象都是由一个redisObject结构表示,该结构中和保存数据有关的三个属性如下:
12345678910typedef struct redisObject { // 类型 unsigned type; // 编码 unsigned encoding; // 指向底层实现数据结构的指针 void *ptr;}
type这个属性记录了对象的类型,该类型可以是下表中的一个。
每一个数据的键,总是一个字符串对象,而值可以是上表中的任意一个。
在redis中,使用TYPE命令可以显示一个键对应值的type
encoding记录了对象所使用 ...