刚开始看redis..总结一下看到的东西,有些话是参考网上的,有些话是类似自己白话说的,若有不对请指正。
Redis
redis常用五种数据结构:string hash list set sortedset
String:(字符串)
一个key对应一个value,是二进制安全的
typedef char *sds;
struct sdshdr {
int len;// 记录 buf 数组中已使用字节的数量 等于 SDS 所保存字符串的长度
int free;// 记录 buf 数组中未使用字节的数量
char buf[];// 字节数组,用于保存字符串
};
一个sds实际申请的内存: sizeof(sdshdr)+len+free+1 刚开始free为0,当需要增长时根据策略进行动态增长
List:(列表/双向链表)
typedef struct listNode {
struct listNode *prev; //指向前一个节点
struct listNode *next; //指向后一个节点
void *value;//值
} listNode;//节点
typedef struct list {
listNode *head;//双向链表的头节点
listNode *tail;//双向链表的尾节点
void *(*dup)(void *ptr);//复制
void (*free)(void *ptr);//释放
int (*match)(void *ptr, void *key);//匹配
unsigned long len;//链表长度
} list;//双向链表/列表
typedef struct listIter {
listNode *next;//指向列表的某个节点
int direction;//迭代方向
} listIter;//访问链表的迭代器
Hash:(哈希)
是一个string类型的field和value的映射表,适用于储存对象
Set:(集合)
底层实现是哈希,一般情况使用0号哈希表,如果在rehash时,则会同时使用0号和1号哈希表
哈希表采用链地址法解决键冲突,自动rehash收缩或者扩展哈希表
typedef struct dictEntry {
void *key; //键
union {
void *val;
uint64_t u64;
int64_t s64;
} v; //值
struct dictEntry *next;//指向下一个节点
} dictEntry;//哈希节点
typedef struct dictht {
dictEntry **table; //桶 哈希节点类型
unsigned long size;//指针数组大小
unsigned long sizemask;//指针数组掩码,用于计算索引值
unsigned long used;//hash表现有节点数量
} dictht;//哈希表
typedef struct dict {
dictType *type; //类型处理函数
void *privdata; //类型处理函数私有值
dictht ht[2];//两个hash表
int rehashidx; //rehash标示,为-1表示不在rehash,不为0表示正在rehash的桶
int iterators; //当前正在运行的安全迭代器数量
} dict;//集合有2个哈希表
Rehash:创建比ht[0]->table更大的ht[1]->table,size为大于use*2的2的指数,开始值为4,然后把ht[0]的键值对迁移到ht[1]中,将ht[0]中数据清空,将新的ht[1]替代ht[0]。
进行rehash的条件:
自然 rehash : ratio >= 1 ,且变量 dict_can_resize 为真。
强制 rehash : ratio 大于变量 dict_force_resize_ratio (目前版本中, dict_force_resize_ratio 的值为 5 )。
Sorted set:(有序集合)
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
typedef struct zskiplistNode {
robj *obj; // 成员对象
double score;// 分值
struct zskiplistNode *backward; // 后退指针
struct zskiplistLevel {
struct zskiplistNode *forward; // 前进指针
unsigned int span;// 跨度
} level[]; // 层
} zskiplistNode;//有序集合的节点
typedef struct zskiplist {
struct zskiplistNode *header, *tail; // 表头节点和表尾节点
unsigned long length;// 表中节点的数量
int level;// 表中层数最大的节点的层数
} zskiplist;//有序集合
数据库:
Redis客户端默认目标数据库为0号数据库
数据库由dict(保存键值对)和expires(保存键的过期时间)两字典组成
RDB持久化
服务器中的非空数据库以及他们的键值对统称为数据库状态。
RDB持久化可将数据库状态保存在磁盘,可手动执行也可定期执行。通过该文件可还原数据库状态。
RDB文件是经过压缩的二进制文件,对于不同类型的键值对,RDB会以不同方式来保存。
SAVE或者BGSAVE可生成rdb文件。
SAVE会阻塞服务器进程,到创建完成前都不能处理任何命令请求。BGSAVE会派生出一个子进程来创建RDB,父进程就可以执行其他命令了。
服务器启动时如果检测到RDB文件就会载入,如果开启了AOF持久化功能,则优先用AOF文件。
AOF持久化
AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态。
实现:命令追加、文件写入、文件同步
命令追加:服务器执行写命令过后,会将写命令追加到服务器状态缓冲区的末尾。
文件写入与同步:在每次结束一个事件循环前,会考虑将缓冲区的内容写入AOF文件。
发布和订阅:
SUBSCRIBE:客户端可以订阅一个或多个频道,成为这些频道的订阅者。每当有客户端向这些频道发消息的时候,频道的所有订阅者都可以收到这条消息。
PSUBSCRIBE:客户端可以订阅一个或多个模式,成为这些模式的订阅者。每当有客户端向这些频道发消息的时候,订阅频道以及与这个频道相匹配的模式的订阅者都会收到消息。
事务:redis用multi和exec打包事务
以multi开始exec结束,服务器执行事务期间不会中断去执行其他命令。
redis不支持回滚机制,若一个事务在入队命令过程中,出现了命令不存在或者格式错误则服务器拒绝执行此事务。