Skip to content

Commit c6256e2

Browse files
committed
Update Java Notes
1 parent aa11659 commit c6256e2

File tree

2 files changed

+842
-51
lines changed

2 files changed

+842
-51
lines changed

DB.md

Lines changed: 205 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7815,7 +7815,42 @@ dbfilename "dump-6379.rdb"
78157815

78167816

78177817

7818-
## 结构模型
7818+
## 体系结构
7819+
7820+
### 存储对象
7821+
7822+
Redis 使用对象来表示数据库中的键和值,当在 Redis 数据库中新创建一个键值对时至少会创建两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)
7823+
7824+
Redis 中对象由一个 redisObject 结构表示,该结构中和保存数据有关的三个属性分别是type、 encoding、ptr:
7825+
7826+
```c
7827+
typedef struct redisObiect{
7828+
//类型
7829+
unsigned type:4;
7830+
//编码
7831+
unsigned encoding:4;
7832+
//指向底层数据结构的指针
7833+
void *ptr;
7834+
}
7835+
```
7836+
7837+
Redis 中主要数据结构有:简单动态字符串(SDS)、双端链表、字典、压缩列表、整数集合、跳跃表
7838+
7839+
Redis 并没有直接使用数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,而每种对象又通过不同的编码映射到不同的底层数据结构
7840+
7841+
Redis 自身是一个 Map,其中所有的数据都是采用 key : value 的形式存储,**键对象都是字符串对象**,而值对象有五种基本类型和三种高级类型对象
7842+
7843+
![](https://gitee.com/seazean/images/raw/master/DB/Redis-对象模型.png)
7844+
7845+
7846+
7847+
7848+
7849+
***
7850+
7851+
7852+
7853+
### 线程模型
78197854

78207855
Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器(file event handler),这个文件事件处理器是单线程的,所以Redis叫做单线程的模型
78217856

@@ -8011,12 +8046,6 @@ Redis为每个服务提供16个数据库,编码0-15,每个数据库之间的
80118046
80128047
#### 简介
80138048
8014-
redis 自身是一个 Map,其中所有的数据都是采用 key : value 的形式存储
8015-
8016-
数据类型指的是存储的数据的类型,也就是 value 部分的类型,**key 部分永远都是字符串**
8017-
8018-
string类型的数据:
8019-
80208049
存储的数据:单个数据,最简单的数据存储类型,也是最常用的数据存储类型,实质上是存一个字符串
80218050
80228051
存储数据的格式:一个存储空间保存一个数据,每一个空间中只能保存一个字符串信息
@@ -8144,6 +8173,18 @@ string类型的数据:
81448173
81458174
81468175
8176+
#### 实现
8177+
8178+
Redis字符串对象底层的数据结构实现主要是 int 和简单动态字符串SDS,涉及C语言相关,先不做记录
8179+
8180+
参考文章:https://www.cnblogs.com/hunternet/p/9957913.html
8181+
8182+
8183+
8184+
***
8185+
8186+
8187+
81478188
### hash
81488189
81498190
#### 简介
@@ -8230,7 +8271,50 @@ user:id:3506728370 → {"name":"春晚","fans":12210862,"blogs":83}
82308271
82318272
假如现在粉丝数量发生了变化,要把整个值都改变,但是用单条存就不存在这个问题,只需要改其中一个就可以
82328273
8233-
![](https://gitee.com/seazean/images/raw/master/DB/hash应用场景结构图.png)
8274+
<img src="https://gitee.com/seazean/images/raw/master/DB/hash应用场景结构图.png" style="zoom: 33%;" />
8275+
8276+
8277+
8278+
***
8279+
8280+
8281+
8282+
#### 实现
8283+
8284+
##### 底层结构
8285+
8286+
哈希类型的内部编码有两种:ziplist(压缩列表)、hashtable(哈希表、字典)
8287+
8288+
当存储的数据量比较小的情况下,Redis 才使用压缩列表来实现字典类型,具体需要满足两个条件:
8289+
8290+
- 当哈希类型元素个数小于 hash-max-ziplist-entries 配置(默认512个)
8291+
- 所有值都小于 hash-max-ziplist-value 配置(默认64字节)
8292+
8293+
ziplist 使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比 hashtable 更加优秀,当 ziplist 无法满足哈希类型时,Redis 会使用 hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,而hashtable的读写时间复杂度为O(1)
8294+
8295+
8296+
8297+
****
8298+
8299+
8300+
8301+
##### 压缩列表
8302+
8303+
压缩列表(ziplist)是列表和哈希的底层实现之一,压缩列表用来紧凑数据存储,节省内存:
8304+
8305+
<img src="https://gitee.com/seazean/images/raw/master/DB/Redis-压缩列表数据结构.png" style="zoom:67%;" />
8306+
8307+
压缩列表是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结枃,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值
8308+
8309+
8310+
8311+
***
8312+
8313+
8314+
8315+
##### 哈希表
8316+
8317+
Redis 字典使用散列表为底层实现,一个散列表里面有多个散列表节点,每个散列表节点就保存了字典中的一个键值对,发生哈希冲突采用链表法解决
82348318
82358319
82368320
@@ -8318,6 +8402,57 @@ list类型:保存多个数据,底层使用**双向链表**存储结构实现
83188402
83198403
83208404
8405+
****
8406+
8407+
8408+
8409+
#### 实现
8410+
8411+
##### 底层结构
8412+
8413+
在 Redis3.2 版本以前列表类型的内部编码有两种:ziplist(压缩列表)和 linkedlist(链表),在 Redis3.2版本 以后对列表数据结构进行了改造,使用 quicklist(快速列表)代替了 ziplist 和 linkedlist
8414+
8415+
8416+
8417+
****
8418+
8419+
8420+
8421+
##### 链表结构
8422+
8423+
Redis 链表为双向无环链表,使用 listNode 结构表示
8424+
8425+
```c
8426+
typedef struct listNode
8427+
{
8428+
// 前置节点
8429+
struct listNode *prev;
8430+
// 后置节点
8431+
struct listNode *next;
8432+
// 节点的值
8433+
void *value;
8434+
} listNode;
8435+
```
8436+
8437+
![](https://gitee.com/seazean/images/raw/master/DB/Redis-链表数据结构.png)
8438+
8439+
- 双向:链表节点带有前驱、后继指针,获取某个节点的前驱、后继节点的时间复杂度为O(1)
8440+
- 无环:链表为非循环链表,表头节点的前驱指针和表尾节点的后继指针都指向NULL,对链表的访问以 NULL 为终点
8441+
8442+
8443+
8444+
***
8445+
8446+
8447+
8448+
##### 快速列表
8449+
8450+
quicklist 实际上是 ziplist 和 linkedlist 的混合体,将 linkedlist 按段切分,每一段使用 ziplist 来紧凑存储,多个 ziplist 之间使用双向指针串接起来
8451+
8452+
![](https://gitee.com/seazean/images/raw/master/DB/Redis-快速列表数据结构.png)
8453+
8454+
8455+
83218456
***
83228457
83238458
@@ -8415,6 +8550,24 @@ set类型:与hash存储结构完全相同,仅存储键不存储值(nil)
84158550

84168551

84178552

8553+
***
8554+
8555+
8556+
8557+
#### 实现
8558+
8559+
集合类型的内部编码有两种:
8560+
8561+
* intset(整数集合):当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个)时,Redis 会选用 intset 来作为集合的内部实现,从而减少内存的使用
8562+
8563+
* hashtable(哈希表):当集合类型无法满足 intset 条件时,Redis会使用 hashtable 作为集合的内部实现
8564+
8565+
整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构,可以保存类型为 int16_t、int32_t 或者 int64_t的整数值,并且保证集合中的元素是有序不重复的
8566+
8567+
8568+
8569+
8570+
84188571
***
84198572

84208573

@@ -8500,6 +8653,49 @@ sorted_set类型:在set的存储结构基础上添加可排序字段,类似
85008653

85018654

85028655

8656+
#### 实现
8657+
8658+
##### 底层结构
8659+
8660+
有序集合是由 ziplist(压缩列表)或 skiplist(跳跃表)组成的
8661+
8662+
当数据比较少时,有序集合使用的是 ziplist 存储的,使用 ziplist 格式存储需要满足以下两个条件:
8663+
8664+
- 有序集合保存的元素个数要小于 128 个;
8665+
- 有序集合保存的所有元素成员的长度都必须小于 64 字节
8666+
8667+
8668+
8669+
***
8670+
8671+
8672+
8673+
##### 跳跃表
8674+
8675+
Redis 使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的**元素数量比较多**,又或者有序集合中元素的**成员是比较长的字符串**时,Redis就会使用跳跃表来作为有序集合健的底层实现
8676+
8677+
跳跃表在链表的基础上增加了多级索引以提升查找的效率,索引是占内存的,所以是一个空间换时间的方案。原始链表中存储的有可能是很大的对象,而索引结点只需要存储关键值值和几个指针,并不需要存储对象,因此当节点本身比较大或者元素数量比较多的时候,其优势可以被放大,而缺点则可以忽略
8678+
8679+
* 基于单向链表加索引的方式实现
8680+
8681+
- Redis 的跳跃表实现由 zskiplist 和 zskiplistnode 两个结构组成,其中 zskiplist 用于保存跳跃表信息(比如表头节点、表尾节点、长度),而 zskiplistnode 则用于表示跳跃表节点
8682+
- Redis 每个跳跃表节点的层高都是 1 至 32 之间的随机数
8683+
- 在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的。跳跃表中的节点按照分值大小进行排序,当分值相同时节点按照成员对象的大小进行排序
8684+
8685+
![](https://gitee.com/seazean/images/raw/master/DB/Redis-跳跃表数据结构.png)
8686+
8687+
8688+
8689+
个人笔记:Java → JUC → 并发包 → ConcurrentSkipListMap详解跳跃表
8690+
8691+
参考文章:https://www.cnblogs.com/hunternet/p/11248192.html
8692+
8693+
8694+
8695+
***
8696+
8697+
8698+
85038699
### Bitmaps
85048700

85058701
#### 布隆过滤
@@ -8970,7 +9166,7 @@ bgsave指令工作原理:
89709166
89719167
流程:当执行 bgsave 的时候,客户端发出 bgsave 指令给到 redis 服务器,服务器返回后台已经开始执行的信息给客户端,同时使用 fork函数 创建一个子进程,让这个子进程去执行save相关的操作,创建RDB文件保存起来,操作完以后把结果返回。
89729168
8973-
本质上bgsave的过程分成两个过程:第一个是服务端收到指令直接告诉客户端开始执行;另外一个过程是一个子进程在完成后台的保存操作,操作完以后返回消息两个进程不相互影响
9169+
bgsave分成两个过程:第一个是服务端收到指令直接告诉客户端开始执行;另外一个过程是一个子进程在完成后台的保存操作,操作完以后返回消息。**两个进程不相互影响**,所以在持久化期间Redis可以正常工作
89749170
89759171
注意:bgsave命令是针对save阻塞问题做的优化,Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用
89769172

0 commit comments

Comments
 (0)