Redis(一):数据结构

您所在的位置:网站首页 字典是一种什么类型 Redis(一):数据结构

Redis(一):数据结构

2023-03-17 04:05| 来源: 网络整理| 查看: 265

前言

从本文开始,我将分享一下近期自学 Redis 的学习笔记,其中大部分来源于极客时间的《Redis核心技术与实战》和小林coding。

我将在后续文章中陆续介绍以下内容:Redis自定义的数据结构、数据类型,线程模型、持久化、内存管理、通信、网络IO、并发问题、事务、主从架构、发布订阅机制、哨兵机制、切片集群、缓存问题、性能问题等。

概览-Redis是什么

Redis 的全称为 Remote Dictionary Server,远程数据服务。是使用 C 语言编写的。

Redis 是一种基于内存的键值对数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。

Redis 的快,一方面得益于内存的快,另一方面是由于 Redis 的底层架构和许多自定义的高效数据结构。相较于同样基于内存的键值对数据库 Memcache,Redis 有更多的数据类型,能够满足更多的应用场景。

底层架构

Redis 中的数据就是一个个键值对,其中的 key 是字符串对象,而 value 可以是许多类型对象(String、List、Hash 等,后续会讲到)。Redis 使用了一个哈希表「dictht」来保存所有键值对。

Redis 存储数据所涉及到的数据结构如下图所示:

在这里插入图片描述

redisDb,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;dict,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用(当哈希冲突率过高时,会触发 rehash 来扩容,后续会详细讲解);dictht,表示哈希表的结构,结构里存放了哈希表数组,数组中的每个元素都是指向一个 dictEntry 的指针;dictEntry,表示哈希表节点的结构,结构里存放了 *void * key 和 void * value 指针, key 指向的是 String 对象,而 *value 则可以指向任意类型对象。key 指针和 value 指针各占 8 个字节。

dictht 和 dictEntry 的结构如下:

typedef struct dictht { //哈希表数组 dictEntry **table; //哈希表大小 unsigned long size; //哈希表大小掩码,用于计算索引值 unsigned long sizemask; //该哈希表已有的节点数量 unsigned long used; } dictht; typedef struct dictEntry { //键值对中的键 void *key; //键值对中的值 union { void *val; uint64_t u64; int64_t s64; double d; } v; //指向下一个哈希表节点,形成链表 struct dictEntry *next; } dictEntry;

dictEntry 结构里键值对中的值是一个「联合体 v」定义的,因此,键值对中的值可以是一个指向实际值的指针,或者是一个无符号的 64 位整数或有符号的 64 位整数或 double 类的值。这么做的好处是可以节省内存空间,因为当「值」是整数或浮点数时,就可以将值的数据内嵌在 dictEntry 结构里,无需再用一个指针指向实际的值,从而节省了内存空间。

哈希冲突

什么是哈希算法?

哈希算法也叫散列算法,一般来说满足这样的关系:f(data)=key,输入任意长度的 data 数据,经过哈希算法处理后输出一个定长的数据 key。同时这个过程是不可逆的,无法由 key 逆推出 data。

什么是哈希冲突?

哈希冲突就是多个不同的 key 通过哈希算法计算出了相同的 data。再优秀的哈希算法,也仍然可能存在哈希冲突,数据量越大,哈希冲突的可能性越高。

举一个最简单的例子,假如哈希算法采用对 8 取模这个操作,那么所有整数类型的 key 通过这个哈希算法都能得出 0~7 这八个值中的一个,就能分配到相应的哈希槽中去了。但是会有多个值会被分配到同一个哈希槽中(比如 1 和 9、2 和 10)。

Redis 为了解决哈希冲突,在哈希表槽位(dictEntry)中维护了一个链表,当多个 key 映射到同一个槽位时,这些键值对就会形成一个链表。

哈希表的大小不变,所以随着数据量的增加,哈希冲突率会越来越高,带来的影响是查找的速度会越来越慢(因为每次都要遍历一个长链表)。Redis 使用了 rehash 策略来解决这个问题,后续会详细讲解。

查找数据

在进行哈希表操作时,首先对 key 进行哈希计算,根据哈希值(对应的是数组的下标)找到对应的槽位(dictEntry),然后遍历该槽位的链表,查找与 key 相同的节点。如果哈希冲突率较低,链表的长度较短,那么整个过程的时间复杂度大约为常数级别,即 O(1)。

redisObject

Redis 中定义了许多类型对象,如 String、List、Hash、Set 等,每个对象都由 redisObject 结构表示。

对象结构里包含的成员变量:

type,4bit,标识该对象是什么类型的对象;encoding,4bit,标识该对象使用了哪种底层的数据结构;ptr,8B,指向底层数据结构的指针。lru,24bit,记录了这个对象最后一次被访问的时间以及访问频次,用于淘汰过期的键值对;refcount,4B,记录了对象的被引用计数,用于内存管理;

一个 redisObject 对象的大小为 16 字节,除了指针外的数据有时统称为元数据,元数据和指针各占 8B。

最后

本文介绍了 Redis 底层的数据结构,Redis 存储数据是使用哈希表的,所以能够实现快速地查询数据。下一节将介绍 Redis 实现的各种数据结构。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3