redis是一个开源的、key-value的内存数据库

redis数据库

每个redis服务器都会创建一个redisServer结构体类型的变量,存储服务器的各种信息。

数据库相关的信息保存在redisServer中的 redisDb *db 数组中,数组中的每个元素都是一个redisDb结构体变量,代表一个数据库。db数组中有多少个元素,就代表该redisServer有多少个数据库,一般默认16个。一般情况下,默认使用0号数据库。

数据库redisDb结构:

typedef struct redisDb{
	//字典类型,保存数据库的所有键值对(即数据),称作键空间
	//键空间的键就是数据库的键,每个键都是一个字符串对象
	//键空间的值就是数据库的值,每个值可以是字符串对象,列表对象,哈希表对象,集合对象,有序集合对象
	dict *dict;
	//字典类型,保存数据库键的过期时间,成为过期字典
	//过期字典的键是一个指针,指向键空间的某个键对象(数据库键)
	//过期字典的值是一个long long类型的整数,保存了键所指向的数据库键的过期时间(ms精度的unix时间戳)
	dict *expires;
	...
}

redis过期策略

redis采用惰性删除定期删除相结合的过期键删除策略。

惰性删除是指,每次从键空间获取键时,都检查该建是否过期,如果过期就删除,没过期就返回。

定期删除是指,每隔一段时间,就检查数据库,删除里面的过期键。删除多少过期键以及检查多少数据库,则由算法决定。redise的策略是,在规定时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。

redis持久化

RDB持久化

  • RDB持久化功能可以将redis某个时间点上的数据库状态保存到一个RDB文件中,该文件是一个经过压缩的二进制文件;
  • RDB持久化可以手动触发,也可以根据服务器配置定期执行。手动触发的话,SAVE命令会阻塞Redis服务器进程,BGSAVE命令会fork一个子进程来创建RDB文件,不会影响服务器进程处理命令。
  • 服务器启动时,会先检查是否开启了AOF持久化功能(AOF文件的更新频率通常比RDB高),如果开启,优先使用AOF文件还原数据库状态,如果未开启,再载入RDB文件。

AOF持久化

  • AOF(Append Only FIle)持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的;
  • AOF文件中的所有命令都以Redis命令请求协议的格式保存;
  • AOF重写——读取数据库中的键值对,然后用一条命令记录键值对,代替之前记录这个键值对的多条命令,这就是重写原理。

redis复制

旧版复制功能

分为同步命令传播两个阶段:

  1. slave向master发送SYNC命令,master生成RDB文件,同时从现在开始记录命令到缓冲区;

  2. slave接收RDB文件,根据RDB文件同步数据;

  3. master将缓冲区中的命令发送给slave,slave状态同步至与master状态一致;

  4. 后续master执行命令后,同步传送给slave——命令传播

缺点

这种方式的缺点是,如果slave在命令传播阶段突然断开与master的连接,几秒后又重新建立连接,那么这时候需要重新执行上述过程,同步所有数据。其实,断开连接之前的数据是没必要再同步的。这样浪费了cpu、IO、带宽等资源。

新版复制功能

新增了部分重同步的功能,就是说,上述slave断开重连的情况,可以只同步断开后的命令。

新版复制用PSYNC命令代替SYNC命令,支持完整重同步部分重同步。其中,完整重同步与老的同步机制类似。这里主要说下部分重同步

部分重同步

部分重同步通过主从服务器的复制偏移量主服务器的复制积压缓冲区服务器运行ID这3部分实现。

  1. 主从服务器都会维护一个复制偏移量offset,主服务器每传播N个字节,offset值加N,从服务器收到这N个字节后,也把自己的offset加N;

  2. 主服务器维护一个固定长度的先进先出队列,默认1MB,近期传播的命令会保存在这里,并且队列中的每个字节都有一个编号,就是复制偏移量offset。

  3. 从服务器重新连接后,会发送之前保存的主服务器的ID,如果与当前连接的主服务器ID相同,则进行部分重同步,如果不同,则说明当前主服务器并不是之前的,所以需要从新全部同步(ps: 每个redis服务器都有自己的运行ID,是在服务器启动时自动生成的,有40个随机的十六进制字符组成)

另外,如果在命令传播过程中,master发送的命令slave没有收到怎么办?比如,master这边的offset偏移了40,而slave没收到这40字节,所以offset就不会偏移,这时主从服务器的状态就不一致了。

为了解决这个问题,redis2.8版本新增了心跳检查功能,slave以每秒一次的频率向master发送REPLCONF ACK 命令,告诉master自己当前的offset是多少,如果master发现slave和自己的offset不一致,就会把丢掉的那些命令重新发送一次。