Redis是一个key-value存储系统,现在在各种系统中的使用越来越多,大部分情况下是因为其高性能的特性,被当做缓存使用。Redis由于其丰富的数据结构也可以被应用到其他场景。Redis是一个K-V的非关系型数据库(NoSQL),常见的NoSQL数据库有:K-V数据库如Redis、Memcached,列式数据库如大数据组件HBase,文档数据库如mogoDB。Redis应用广泛,尤其是被作为缓存使用。
Redis的具有很多优势:
参考:
Redis 原理及应用(1)–数据类型及底层实现方式
关于redis,学会这8点就够了
关于Redis的问题看这篇就够了
Redis 3.0新特性
参考:
Redis 原理及应用(2)–持久化方式、集群管理、事务及与Memcached的对比
分布式缓存Redis之与Memcached的比较
Redis与Memcached的比较 ,然后选择了Redis
redis和memcached的优缺点及区别
更多:
为什么说Redis是单线程的以及Redis为什么这么快!
Redis常见题目问答
字符串(String),散列(Hash),列表(List),集合(Set),有序集合(Sorted Set)
更多:
Redis各种数据类型应用场景
Redis是由C语言编写的。Redis支持5种数据类型,以K-V形式进行存储,K是String类型的,V支持5种不同的数据类型,分别是:string,list,hash,set,sorted set,每一种数据结构都有其特定的应用场景。从内部实现的角度来看是如何更好的实现这些数据类型。Redis底层数据结构有以下数据类型:简单动态字符串(SDS),链表,字典,跳跃表,整数集合,压缩列表,对象。接下来,就探讨一下Redis是怎么通过这些数据结构来实现value的5种类型的。
定义:
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
}
用途:
好处:
定义:
typedef struct listNode{
struct listNode *prev;
struct listNode * next;
void * value;
}
typedef struct list{
//表头节点
listNode * head;
//表尾节点
listNode * tail;
//链表长度
unsigned long len;
//节点值复制函数
void *(*dup) (void *ptr);
//节点值释放函数
void (*free) (void *ptr);
//节点值对比函数
int (*match)(void *ptr, void *key);
}
图示:
用途:
好处:
定义:
字典,又称为符号表(symbol table)、关联数组(associative array)或映射(map),是一种用于保存键值对的抽象数据结构。
在字典中,一个键(key)可以和一个值(value)进行关联,字典中的每个键都是独一无二的。
typedef struct dict {
// 类型特定函数
dictType *type;
// 私有数据
void *privedata;
// 哈希表
dictht ht[2];
// rehash 索引
in trehashidx;
}
typedef struct dictht {
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值
unsigned long sizemask;
//该哈希表已有节点的数量
unsigned long used;
}
typeof struct dictEntry{
//键
void *key;
//值
union{
void *val;
uint64_tu64;
int64_ts64;
}
struct dictEntry *next;
}
我们存入里面的key 并不是直接的字符串,而是一个hash 值,通过hash 算法,将字符串转换成对应的hash 值,然后在dictEntry 中找到对应的位置。
这时候我们会发现一个问题,如果出现hash 值相同的情况怎么办?Redis 采用了链地址法来解决hash冲突。这与hashmap的实现类似
解决hash冲突:采用链地址法来实现。
扩充Rehash:随着对哈希表的不断操作,哈希表保存的键值对会逐渐的发生改变,为了让哈希表的负载因子维持在一个合理的范围之内,我们需要对哈希表的大小进行相应的扩展或者压缩,这时候,我们可以通过 rehash(重新散列)操作来完成。其实现方式和hashmap略有不同,因为dict有两个hash表dictht,所以它是通过这两个dictht互相进行转移的(dictht ht[2]的原因)。
Rehash操作渐进式,rehash 操作并不是一次性、集中式完成的,而是分多次、渐进式地完成的。采用渐进式rehash 的好处在于它采取分而治之的方式,避免了集中式rehash 带来的庞大计算量。
用途:
定义:
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速查找访问节点的目的。跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素,效率和平衡树媲美 ——查找、删除、添加等操作都可以在O(logn)期望时间下完成。
Redis 的跳跃表 主要由两部分组成:zskiplist(链表)和zskiplistNode (节点)
typedef struct zskiplistNode{
//层
struct zskiplistLevel{
//前进指针
struct zskiplistNode *forward;
//跨度
unsigned int span;
} level[];
//后退指针
struct zskiplistNode *backward;
//分值
double score;
//成员对象
robj *obj;
}
1、层:level 数组可以包含多个元素,每个元素都包含一个指向其他节点的指针。level数组的每个元素都包含:前进指针:用于指向表尾方向的前进指针,跨度:用于记录两个节点之间的距离
2、后退指针:用于从表尾向表头方向访问节点
3、分值和成员:跳跃表中的所有节点都按分值从小到大排序(按照这个进行排序的,也就是平衡二叉树(搜索树的)的节点大小)。成员对象指向一个字符串,这个字符串对象保存着一个SDS值(实际存储的值)
typedef struct zskiplist {
//表头节点和表尾节点
structz skiplistNode *header,*tail;
//表中节点数量
unsigned long length;
//表中层数最大的节点的层数
int level;
}zskiplist;
图示:
从结构图中我们可以清晰的看到,header,tail分别指向跳跃表的头结点和尾节点。level 用于记录最大的层数,length 用于记录我们的节点数量。
跳跃表是有序集合的底层实现之一,主要有zskiplist 和zskiplistNode两个结构组成
用途:
更多:
Redis中的跳跃表
redis(五)跳跃表
跳跃表的原理及实现
定义:
整数集合是集合建(sets)的底层实现之一,当一个集合中只包含整数,且这个集合中的元素数量不多时,redis就会使用整数集合intset作为集合的底层实现。他其实就是一个特殊的集合,里面存储的数据只能够是整数,并且数据量不能过大。
typedef struct intset{
//编码方式
uint32_t enconding;
// 集合包含的元素数量
uint32_t length;
//保存元素的数组
int8_t contents[];
}
整数集合是集合建的底层实现之一.
整数集合的底层实现为数组,这个数组以有序,无重复的范式保存集合元素,在有需要时,程序会根据新添加的元素类型改变这个数组的类型。
定义:
压缩列表是列表键(list)和哈希键(hash)的底层实现之一。当一个列表键只有少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,那么Redis 就会使用压缩列表来做列表键的底层实现。
用途:
更多:
Redis基础及底层实现
定期删除(定期删除一些过期的key)+惰性删除(查询到该key时删除该过期key)
Redis所有的键都可以设置过期属性,内部保存在过期字典中。由于进程内保存了大量的键,维护每个键精准的过期删除机制会导致消耗大量的CPU,对于单线程的Redis来说成本过高,因此Redis采用惰性删除和定时任务删除机制实现过期键的内存回收。
更多:
Redis内存回收策略
当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,Redis支持6种策略(当数据过期后,并不会马上被删除依赖于上面提到的惰性删除和定时任务删除过期数据,删除后占用内存才会减少):
20万用户同时访问一个热点Key,如何优化缓存架构