目录
一、认识Redis
二、Redis 5大数据类型
三、Redis SDS底层数据结构解析
四、链表数据结构解析
五、列表数据结构
1、压缩列表
2、快速列表
六、Redis字典
七、Redis整数集合
八、Redis跳表
九、Redis对象
十、Redis使用场景
最近入门学习Redis数据库,从最简单的内容学习,然后总结记录于此,以备忘记,也与大家共享!
Windows安装如图:
下载地址:
Windows:https://github.com/microsoftarchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.zip
Linux:http://download.redis.io/releases/redis-6.0.6.tar.gz
Server端:
Client端:
作为一款高性能、开源的NoSQL数据库,Redis主要采用键值对形式存储数据,用C语言开发,采用BSD协议,功能强大、支持多种数据类型,属于非关系型数据库。
区别与Mysql、SQLServer、Oracle等关系型数据库。因此,Redis的特点让它具有许多优势:
- 支持多种编程语言,Such as Java、C、C++、Python、PHP、Go、Lua等;
- 包括丰富的数据类型,Such as String、List、Set、Hash、Sorted Set;
- 读写速度快,性能高。官方数据:读速度110000次/s,写速度81000次/s,意味着数据都存储在内存中进行读写;
- 持久化。Redis的重要特点就是实现了持久化,定时将内存中的数据保存到磁盘,重启服务器时候再次加载到内存中,持久化方式有AOF和RDB;
- 简单且功能强大。可以实现消息订阅发布、Lua脚本、数据库事务等。Redis是单线程工作,所有操作都是原子性的,使用简单。
- 实现高可用主从复制。
- 实现分布式集群和高可用,分别是Redis Cluster和Redis Sentinel。
- 支持多种数据结构。Such Hash、Set、位图等。
下面简单介绍Redis的 5大数据类型。
Redis 5 大数据类型的操作命令具体可见:http://www.redis.cn/commands.html
1、字符串String
字符串是Redis中最基本的数据类型,是二进制安全的。String类型的键最大存储512M的数据。
支持的数据包括:二进制数据、序列化后数据、JSON化的对象等。
2、哈希Hash
Hash类型是一个String类型的域和值的映射表,常用来存储对象信息。每个哈希表可以存储2^32-1个键值对,相当于40亿个数据。
3、列表List
Redis的列表相当于简单字符串列表,按插入顺序排序,可以操作将一个元素插入表头或表尾。同样可以存储2^32-1个元素。
4、集合Set
Set数据类型是String类型的无序集合,每个元素都是唯一的。集合通过哈希表来实现,增删查效率高,复杂度O(1)。一个集合最大容量存储2^32-1个元素。
5、有序集合Sorted Sort
有序集合是String类型的集合,其中每个元素都对应一个double类型的值。Redis根据这个值大小对元素进行排序,同样通过哈希表来实现,增删查效率高,复杂度O(1)。一个集合最大容量存储2^32-1个元素。
Redis由C语言编写,底层实现具有C语言的语法和特点。对于C中的字符串类型并没有直接使用,而是自己构建了SDS(简单动态字符串)类型,作为Redis默认字符串。
SDS部分源码:
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
从底层代码可以看出,定义了多种结构体来适应不同的需求 。
C的字符串和SDS区别:
/* Node, List, and Iterator are the only data structures used currently. */
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
typedef struct listIter {
listNode *next;
int direction;
} listIter;
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
PORT_ULONG len;
} list;
C语言中没有内置链表结构,Redis自己构建了链表,采用双向链表,由多个离散分配的节点组成,之间通过指针相连,每个节点都有一个前驱节点和一个后继节点,除了头节点(无前驱)和尾节点(无后继)。
#ifndef _ZIPLIST_H
#define _ZIPLIST_H
#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1
unsigned char *ziplistNew(void);
unsigned char *ziplistMerge(unsigned char **first, unsigned char **second);
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);
unsigned char *ziplistIndex(unsigned char *zl, int index);
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p);
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p);
unsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, PORT_LONGLONG *lval);
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen);
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);
unsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num);
unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);
unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);
unsigned int ziplistLen(unsigned char *zl);
size_t ziplistBlobLen(unsigned char *zl);
#ifdef REDIS_TEST
int ziplistTest(int argc, char *argv[]);
#endif
#endif /* _ZIPLIST_H */
Redis压缩列表由列表键和哈希键底层实现。
当列表中包含元素较少,且值较小,Redis采用压缩列表实现,它是一种顺序型数据结构。
快速列表是有压缩列表组成的双向链表,链表的每个节点以压缩列表保存数据。
列表定义如下:
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
PORT_ULONG count; /* total count of all entries in all ziplists */
unsigned int len; /* number of quicklistNodes */
int fill : 16; /* fill factor for individual nodes */
unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
快速列表节点:
typedef struct quicklistNode {
struct quicklistNode *prev;
struct quicklistNode *next;
unsigned char *zl;
unsigned int sz; /* ziplist size in bytes */
unsigned int count : 16; /* count of items in ziplist */
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
Redis字典是一种用于保存Redis键值对的数据结构,也是由Redis自己构建,Redis数据库底层也是由字典实现,CURD操作建立在字典之上。
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
typedef struct dictType {
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
PORT_ULONG size;
PORT_ULONG sizemask;
PORT_ULONG used;
} dictht;
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
PORT_LONG rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;
当一个集合只包含整数值元素,且元素数量不多时,Redis采用该数据结构实现集合键底层。
#ifndef __INTSET_H
#define __INTSET_H
#include
typedef struct intset {
uint32_t encoding;
uint32_t length;
int8_t contents[];
} intset;
intset *intsetNew(void);
intset *intsetAdd(intset *is, int64_t value, uint8_t *success);
intset *intsetRemove(intset *is, int64_t value, int *success);
uint8_t intsetFind(intset *is, int64_t value);
int64_t intsetRandom(intset *is);
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);
uint32_t intsetLen(intset *is);
size_t intsetBlobLen(intset *is);
#ifdef REDIS_TEST
int intsetTest(int argc, char *argv[]);
#endif
#endif // __INTSET_H
跳表是一种有序的数据结构,支持快速节点查找,批量处理节点。如果有序集合的元素众多且元素值为较长字符串,Redis使用跳表作为该有序集合的底层实现。
Redis的 以上数据结构,实际上并没有直接使用,而是创建了一个个对象系统,包括字符串对象、列表对象等。Red is对象系统实现基于引用计数技术的内存回收机制,当某个对象不在使用时候,系统会自动回收该对象占用的内存空间。
robj *dupStringObject(robj *o) {
robj *d;
serverAssert(o->type == OBJ_STRING);
switch(o->encoding) {
case OBJ_ENCODING_RAW:
return createRawStringObject(o->ptr,sdslen(o->ptr));
case OBJ_ENCODING_EMBSTR:
return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));
case OBJ_ENCODING_INT:
d = createObject(OBJ_STRING, NULL);
d->encoding = OBJ_ENCODING_INT;
d->ptr = o->ptr;
return d;
default:
serverPanic("Wrong encoding.");
break;
}
}
robj *createQuicklistObject(void) {
quicklist *l = quicklistCreate();
robj *o = createObject(OBJ_LIST,l);
o->encoding = OBJ_ENCODING_QUICKLIST;
return o;
}
robj *createZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_LIST,zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
robj *createSetObject(void) {
dict *d = dictCreate(&setDictType,NULL);
robj *o = createObject(OBJ_SET,d);
o->encoding = OBJ_ENCODING_HT;
return o;
}
robj *createIntsetObject(void) {
intset *is = intsetNew();
robj *o = createObject(OBJ_SET,is);
o->encoding = OBJ_ENCODING_INTSET;
return o;
}
robj *createHashObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_HASH, zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
robj *createZsetObject(void) {
zset *zs = zmalloc(sizeof(*zs));
robj *o;
zs->dict = dictCreate(&zsetDictType,NULL);
zs->zsl = zslCreate();
o = createObject(OBJ_ZSET,zs);
o->encoding = OBJ_ENCODING_SKIPLIST;
return o;
}
robj *createZsetZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_ZSET,zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
讲了这么多的Redis理论知识点,优势何以见得呢?
在实际应用中,各大网站、系统都可引入Redis,而且应用广泛,解决了关系数据库无法解决的问题。
Redis使用场景:
- 做缓存。Redis最常见的使用场景。它不需要每次重新生成数据,且缓存速度和查询速度快。缓存新闻内容、商品信息或购物车等。
- 做计数器。Redis操作具有原子性。可以用来记录转发数、评论数。
- 实现消息队列系统。支持模式匹配,可实现消息订阅发布。阻塞队列命令可实现秒杀、抢购等活动。
- 实时系统、消息系统。Redis set功能可用来查看用户具体操作,实现实时系统。
- 亿万级别排行榜。这也是Redis的一个重要应用,利用有序集合,对亿万用户进行实时排名,这得益于Redis的读写速度。
- 大型社交网络。如QQ、微博等,用户的浏览和聊天都需要Redis的支持。
这一篇是小白最近入门学习Redis数据库,从最简单的内容学习记录,也与大家共享!Redis作为一款高性能、开源的NoSQL数据库,区别与Mysql、SQLServer、Oracle等关系型数据库。因此,Redis的特点让它具有许多优势。 还有许多重要的地方在这里没有记录,比如持久化具体实现原理AOF、RDB,Redis集群等,以后学习之后有空在记录!
如果觉得不错欢迎“一键三连”哦,点赞收藏关注,有问题直接评论,交流学习!
我的CSDN博客:https://blog.csdn.net/Charzous/article/details/114546348