ToB企服应用市场:ToB评测及商务社交产业平台

标题: 聊一聊redis十种数据类型及底层原理 [打印本页]

作者: 我爱普洱茶    时间: 2023-5-9 11:46
标题: 聊一聊redis十种数据类型及底层原理
概述

Redis 是一个开源的高性能键值数据库,它支持多种数据类型,可以满足不同的业务需求。本文将介绍 Redis 的10种数据类型,分别是
String

概述

string 是 Redis 最基本的数据类型,它可以存储任意类型的数据,比如文本、数字、图片或者序列化的对象。一个 string 类型的键最大可以存储 512 MB 的数据。
string 类型的底层实现是 SDS(simple dynamic string),它是一个动态字符串结构,由长度、空闲空间和字节数组三部分组成。SDS有3种编码类型:
embstr和raw存储字符串数据,int存储整型数据
应用场景

string 类型的应用场景非常广泛,比如:
底层原理

embstr结构

embstr 结构存储小于等于44个字节的字符串,embstr 每次开辟64个byte的空间
raw结构


embstr和raw的转换

在存储字符串的时候,redis会根据数据的长度判断使用哪种结构
  1. 127.0.0.1:6379> object encoding str
  2. "embstr"
  3. # str赋值44个字节的字符串
  4. 127.0.0.1:6379> set str 1234567890123456789012345678901234567890abcd
  5. OK
  6. 127.0.0.1:6379> object encoding str
  7. "embstr"
  8. # str2赋值45个字节的字符串
  9. 127.0.0.1:6379> set str2 1234567890123456789012345678901234567890abcde
  10. OK
  11. 127.0.0.1:6379> object encoding str2
  12. "raw"
  13. 127.0.0.1:6379> set num 123
  14. OK
  15. 127.0.0.1:6379> object encoding num
  16. "int"
复制代码
Hash

概述

hash 是一个键值对集合,它可以存储多个字段和值,类似于编程语言中的 map 对象。一个 hash 类型的键最多可以存储 2^32 - 1 个字段。
Hash类型的底层实现有三种:
应用场景

hash 类型的应用场景主要是存储对象,比如:
底层原理

Redis在存储hash结构的数据,为了达到内存和性能的平衡,也针对少量存储和大量存储分别设计了两种结构,分别为:
ziplist/listpack编码转换为hashTable编码是通过判断元素数量单个元素Key或Value的长度决定的:
ziplist与listpack

ziplist/listpack都是hash结构用来存储少量数据的结构。从Redis7.0后,hash默认使用**ziplist **结构。因为 ziplist 有一个致命的缺陷,就是连锁更新,当一个节点的长度发生变化时,可能会导致后续所有节点的长度字段都要重新编码,这会造成极差的性能
ziplist结构

ziplist是一种紧凑的链表结构,它将所有的字段和值顺序地存储在一块连续的内存中。

Redis中ziplist源码
  1. typedef struct {
  2.   /* 当使用字符串时,slen表示为字符串长度 */
  3.   unsigned char *sval;
  4.   unsigned int slen;
  5.   /* 当使用整形时,sval为NULL,lval为ziplistEntry的value */
  6.   long long lval;
  7. } ziplistEntry;
复制代码
listpack结构


zipList的连锁更新问题

ziplist的每个entry都包含previous_entry_length来记录上一个节点的大小,长度是1个或5个byte:
假设,现有有N个连续、长度为250~253个byte的entry,因此entry的previous_entry_length属性占用1个btye

当第一节长度大于等于254个bytes,导致第二节previous_entry_length变为5个bytes,第二节的长度由250变为254。而第二节长度的增加必然会影响第三节的previous_entry_length。ziplist这种特殊套娃的情况下产生的连续多次空间扩展操作成为连锁更新。新增、删除都可能导致连锁更新的产生。
listpack是如何解决的


hashTable

hashTable是一种散列表结构,它将字段和值分别存储在两个数组中,并通过哈希函数计算字段在数组中的索引

Redis中hashTable源码
  1. struct dict {
  2.     dictType *type;
  3.     dictEntry **ht_table[2];
  4.     unsigned long ht_used[2];
  5.     long rehashidx; /* 当进行rehash时,rehashidx为-1 */
  6.     int16_t pauserehash; /* 如果rehash暂停,pauserehash则大于0,(小于0表示代码错误)*/
  7.     signed char ht_size_exp[2]; /* 哈希桶的个数(size = 1<<exp) */
  8. };
  9. typedef struct dict {
  10.     dictEntry **table;
  11.     dictType *type;
  12.     unsigned long size;
  13.     unsigned long sizemask;
  14.     unsigned long used;
  15.     void *privdata;
  16. } dict;
  17. typedef struct dictEntry {
  18.     void *key;
  19.     void *val;
  20.     struct dictEntry *next;
  21. } dictEntry;
复制代码
ziplist的废弃

显然是ziplist在field个数太大、key太长、value太长三者有其一的时候会有以下问题:
hashTable变得越来越长怎么办

扩容,hashTabel是怎么扩容的?使用的是渐进式扩容rehash。rehash会重新计算哈希值,且将哈希桶的容量扩大。
rehash 步骤


扩展哈希和收缩哈希都是通过执行rehash来完成,这其中就涉及到了空间的分配和释放,主要经过以下五步:
渐进式 rehash

Redis中的这种重新哈希的操作因为不是一次性全部rehash,而是分多次来慢慢的将ht[0]中的键值对rehash到ht[1],故而这种操作也称之为渐进式rehash。渐进式rehash可以避免集中式rehash带来的庞大计算量,是一种分而治之的思想。
在渐进式rehash过程中,因为还可能会有新的键值对存进来,此时Redis的做法是新添加的键值对统一放入ht[1]中,这样就确保了ht[0]键值对的数量只会减少。
当正在执行rehash操作时,如果服务器收到来自客户端的命令请求操作,则会先查询ht[0],查找不到结果再到ht[1]中查询
List

概述

list 是一个有序的字符串列表,它按照插入顺序排序,并且支持在两端插入或删除元素。一个 list 类型的键最多可以存储 2^32 - 1 个元素。
redis3.2以后,list 类型的底层实现只有一种结构,就是quicklist。版本不同时,底层实现是不同的,下面会讲解。
应用场景

list 类型的应用场景主要是实现队列和栈,比如:
底层原理

在讲解list结构之前,需要先说明一下list结构编码的更替,如下
linkedlist与ziplist

在Redis3.2之前,linkedlist和ziplist两种编码可以选择切换,它们之间的转换关系如图

同样地,ziplist转为linkedlist的条件可在redis.conf配置
  1. 127.0.0.1:6379> hset h1 id 123456789012345678901234567890123456789012345678901234567890abcd
  2. (integer) 1
  3. 127.0.0.1:6379> object encoding h1
  4. "ziplist"
  5. 127.0.0.1:6379> hset h2 id 123456789012345678901234567890123456789012345678901234567890abcde
  6. (integer) 1
  7. 127.0.0.1:6379> object encoding h2
  8. "hashtable"
复制代码
quickList(ziplist、linkedlist结合版)

quicklist存储了一个双向列表,每个列表的节点是一个ziplist,所以实际上quicklist并不是一个新的数据结构,它就是linkedlist和ziplist的结合,然后被命名为快速列表。

ziplist内部entry个数可在redis.conf配置
[code]list-max-ziplist-size -2# -5: 每个ziplist最多为 64 kb




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4