入门Redis,从底层数据结构及应用原理开始

编程入门 行业动态 更新时间:2024-10-09 06:24:59

入门Redis,从底层<a href=https://www.elefans.com/category/jswz/34/1769880.html style=数据结构及应用原理开始"/>

入门Redis,从底层数据结构及应用原理开始

目录

一、认识Redis

二、Redis 5大数据类型

三、Redis SDS底层数据结构解析

四、链表数据结构解析

五、列表数据结构

1、压缩列表

2、快速列表

六、Redis字典

七、Redis整数集合

八、Redis跳表

九、Redis对象

十、Redis使用场景


一、认识Redis

最近入门学习Redis数据库,从最简单的内容学习,然后总结记录于此,以备忘记,也与大家共享!

Windows安装如图:

下载地址:

Windows:.2.100/Redis-x64-3.2.100.zip

Linux:.0.6.tar.gz

Server端:

Client端:

作为一款高性能、开源的NoSQL数据库,Redis主要采用键值对形式存储数据,用C语言开发,采用BSD协议,功能强大、支持多种数据类型,属于非关系型数据库。

区别与Mysql、SQLServer、Oracle等关系型数据库。因此,Redis的特点让它具有许多优势:

  1. 支持多种编程语言,Such as Java、C、C++、Python、PHP、Go、Lua等;
  2. 包括丰富的数据类型,Such as String、List、Set、Hash、Sorted Set;
  3. 读写速度快,性能高。官方数据:读速度110000次/s,写速度81000次/s,意味着数据都存储在内存中进行读写;
  4. 持久化。Redis的重要特点就是实现了持久化,定时将内存中的数据保存到磁盘,重启服务器时候再次加载到内存中,持久化方式有AOF和RDB;
  5. 简单且功能强大。可以实现消息订阅发布、Lua脚本、数据库事务等。Redis是单线程工作,所有操作都是原子性的,使用简单。
  6. 实现高可用主从复制。
  7. 实现分布式集群和高可用,分别是Redis Cluster和Redis Sentinel。
  8. 支持多种数据结构。Such Hash、Set、位图等。

下面简单介绍Redis的 5大数据类型。

二、Redis 5大数据类型

Redis 5 大数据类型的操作命令具体可见:.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 SDS底层数据结构解析

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区别:

  1. C的字符串API是二进制不安全的,可能存在缓冲区溢出,只保存文本数据,可以使用string.h的所有库函数,修改一次长度就分配一次内存,获取字符串长度的时间复杂度O(n)。
  2. SDS的API是二进制安全的,不存在缓冲区溢出,可以保存文本/二进制数据,使用部分string.h的库函数,修改N次长度分配内存次数<=N,获取长度为O(1).

四、链表数据结构解析

/* 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自己构建了链表,采用双向链表,由多个离散分配的节点组成,之间通过指针相连,每个节点都有一个前驱节点和一个后继节点,除了头节点(无前驱)和尾节点(无后继)。

五、列表数据结构

1、压缩列表


#ifndef _ZIPLIST_H
#define _ZIPLIST_H#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1unsigned 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采用压缩列表实现,它是一种顺序型数据结构。

2、快速列表

快速列表是有压缩列表组成的双向链表,链表的每个节点以压缩列表保存数据。

列表定义如下:

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自己构建,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整数集合

当一个集合只包含整数值元素,且元素数量不多时,Redis采用该数据结构实现集合键底层。


#ifndef __INTSET_H
#define __INTSET_H
#include <stdint.h>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使用跳表作为该有序集合的底层实现。

九、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使用场景:

  1. 做缓存。Redis最常见的使用场景。它不需要每次重新生成数据,且缓存速度和查询速度快。缓存新闻内容、商品信息或购物车等。
  2. 做计数器。Redis操作具有原子性。可以用来记录转发数、评论数。
  3. 实现消息队列系统。支持模式匹配,可实现消息订阅发布。阻塞队列命令可实现秒杀、抢购等活动。
  4. 实时系统、消息系统。Redis set功能可用来查看用户具体操作,实现实时系统。
  5. 亿万级别排行榜。这也是Redis的一个重要应用,利用有序集合,对亿万用户进行实时排名,这得益于Redis的读写速度。
  6. 大型社交网络。如QQ、微博等,用户的浏览和聊天都需要Redis的支持。

这一篇是小白最近入门学习Redis数据库,从最简单的内容学习记录,也与大家共享!Redis作为一款高性能、开源的NoSQL数据库,区别与Mysql、SQLServer、Oracle等关系型数据库。因此,Redis的特点让它具有许多优势。 还有许多重要的地方在这里没有记录,比如持久化具体实现原理AOF、RDB,Redis集群等,以后学习之后有空在记录!

如果觉得不错欢迎“一键三连”哦,点赞收藏关注,有问题直接评论,交流学习! 


我的CSDN博客:

更多推荐

入门Redis,从底层数据结构及应用原理开始

本文发布于:2024-02-06 18:13:23,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1750934.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:数据结构   底层   入门   原理   Redis

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!