Skip to content

Commit d6957bd

Browse files
committed
[docs update]添加 Redis String 类型的底层实现
1 parent fe85a93 commit d6957bd

File tree

9 files changed

+122
-13
lines changed

9 files changed

+122
-13
lines changed

docs/cs-basics/data-structure/heap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ tag:
6464

6565
> 有能力的人会逐渐升职加薪,是金子总会发光的!!!
6666
67-
**2.从底向上,如果父结点比该元素大,则该节点和父结点交换,直到无法交换**
67+
**2.从底向上,如果父结点比该元素小,则该节点和父结点交换,直到无法交换**
6868

6969
![堆-插入元素2](./pictures/堆/堆-插入元素2.png)
7070

docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ tag:
99
>
1010
> JavaGuide 已获得作者授权,并对原文内容进行了完善。
1111
12-
## 数据库命令规范
12+
## 数据库命名规范
1313

1414
- 所有数据库对象名称必须使用小写字母并用下划线分割
1515
- 所有数据库对象名称禁止使用 MySQL 保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)
@@ -84,7 +84,7 @@ InnoDB 支持事务,支持行级锁,更好的恢复性,高并发下性能
8484

8585
存储字节越小,占用也就空间越小,性能也越好。
8686

87-
**a.某些字符串可以转换成数字类型存储比如可以将 IP 地址转换成整形数据**
87+
**a.某些字符串可以转换成数字类型存储比如可以将 IP 地址转换成整型数据**
8888

8989
数字是连续的,性能更好,占用空间也更小。
9090

docs/database/mysql/mysql-questions-01.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ head:
2121

2222
关系型数据库中,我们的数据都被存放在了各种表中(比如用户表),表中的每一行就存放着一条数据(比如一个用户的信息)。
2323

24-
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/5e3c1a71724a38245aa43b02_99bf70d46cc247be878de9d3a88f0c44.png)
24+
![关系型数据库表关系](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/5e3c1a71724a38245aa43b02_99bf70d46cc247be878de9d3a88f0c44.png)
2525

2626
大部分关系型数据库都使用 SQL 来操作数据库中的数据。并且,大部分关系型数据库都支持事务的四大特性(ACID)。
2727

@@ -376,7 +376,7 @@ InnoDB 不光支持表级锁(table-level locking),还支持行级锁(row-level
376376
377377
**表级锁和行级锁对比**
378378
379-
- **表级锁:** MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
379+
- **表级锁:** MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
380380
- **行级锁:** MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
381381
382382
### 行级锁的使用有什么注意事项?

docs/database/redis/redis-data-structures-01.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,24 @@ head:
1212
content: Redis基础数据结构总结:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)
1313
---
1414

15+
Redis 共有 5 种基本数据结构:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
16+
17+
这 5 种数据结构是直接提供给用户使用的,是数据的保存形式,其底层实现主要依赖这 8 种数据结构:简单动态字符串(SDS)、LinkedList(双向链表)、Hash Table(哈希表)、SkipList(跳跃表)、Intset(整数集合)、ZipList(压缩列表)、QuickList(快速列表)。
18+
19+
Redis 基本数据结构的底层数据结构实现如下:
20+
21+
| String | List | Hash | Set | Zset |
22+
| :----- | :--------------------------- | :------------------ | :-------------- | :---------------- |
23+
| SDS | LinkedList/ZipList/QuickList | Hash Table、ZipList | ZipList、Intset | ZipList、SkipList |
24+
25+
Redis 3.2 之前,List 底层实现是 LinkedList 或者 ZipList。 Redis 3.2 之后,引入了 LinkedList 和 ZipList 的结合 QuickList,List 的底层实现变为 QuickList。
26+
1527
你可以在 Redis 官网上找到 Redis 数据结构非常详细的介绍:
1628

1729
- [Redis Data Structures](https://redis.com/redis-enterprise/data-structures/)
1830
- [Redis Data types tutorial](https://redis.io/docs/manual/data-types/data-types-tutorial/)
1931

20-
未来随着 Redis 新版本的发布,可能会有新的数据结构出现,通过查阅 Redis 官网对应的介绍,你总能获取到最靠谱的信息。
32+
未来随着 Redis 新版本的发布,可能会有新的数据结构出现,通过查阅 Redis 官网对应的介绍,你总能获取到最靠谱的信息。
2133

2234
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/image-20220720181630203.png)
2335

@@ -144,7 +156,7 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据
144156

145157
更多 Redis List 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=list
146158

147-
**通过 `RPUSH/LPOP` 或者 ` LPUSH/RPOP`实现队列**
159+
**通过 `RPUSH/LPOP` 或者 `LPUSH/RPOP`实现队列**
148160

149161
```bash
150162
> RPUSH myList value1

docs/database/redis/redis-data-structures-02.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ head:
1212
content: Redis特殊数据结构总结:HyperLogLogs(基数统计)、Bitmap (位存储)、Geospatial (地理位置)。
1313
---
1414

15+
除了 5 种基本的数据结构之外,Redis 还支持 3 种特殊的数据结构 :Bitmap、HyperLogLog、GEO。
16+
1517
## Bitmap
1618

1719
### 介绍

docs/database/redis/redis-questions-01.md

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ head:
2020

2121
为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap)。并且,Redis 还支持事务 、持久化、Lua 脚本、多种开箱即用的集群方案(Redis Sentinel、Redis Cluster)。
2222

23-
Redis 没有外部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官方推荐生产环境使用 Linux 部署 Redis。
23+
Redis 没有外部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官方推荐生产环境使用 Linux 部署 Redis。
2424

2525
个人学习的话,你可以自己本机安装 Redis 或者通过 Redis 官网提供的[在线 Redis 环境](https://try.redis.io/)来实际体验 Redis。
2626

@@ -36,7 +36,7 @@ Redis 内部做了非常多的性能优化,比较重要的主要有下面 3
3636
- Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
3737
- Redis 内置了多种优化过后的数据结构实现,性能非常高。
3838

39-
下面这张图片总结的挺不错的,分享一下,出自 [Why is Redis so fast?](https://twitter.com/alexxubyte/status/1498703822528544770)
39+
下面这张图片总结的挺不错的,分享一下,出自 [Why is Redis so fast?](https://twitter.com/alexxubyte/status/1498703822528544770)
4040

4141
![why-redis-so-fast](./images/why-redis-so-fast.png)
4242

@@ -95,7 +95,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
9595

9696
### Redis 除了做缓存,还能做什么?
9797

98-
- **分布式锁** : 通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。相关阅读:[《分布式锁中的王者方案 - Redisson》](https://mp.weixin.qq.com/s/CbnPRfvq4m1sqo2uKI6qQw)
98+
- **分布式锁** : 通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:[分布式锁详解](https://javaguide.cn/distributed-system/distributed-lock.html)
9999
- **限流** :一般是通过 Redis + Lua 脚本的方式来实现限流。相关阅读:[《我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了!》](https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA)
100100
- **消息队列** :Redis 自带的 list 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
101101
- **复杂业务场景** :通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 bitmap 统计活跃用户、通过 sorted set 维护排行榜。
@@ -113,6 +113,10 @@ Redis 5.0 新增加的一个数据结构 `Stream` 可以用来做消息队列,
113113

114114
相关文章推荐:[Redis 消息队列的三种方案(List、Streams、Pub/Sub)](https://javakeeper.starfish.ink/data-management/Redis/Redis-MQ.html)
115115

116+
### 如何基于 Redis 实现分布式锁?
117+
118+
关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:[分布式锁详解](https://javaguide.cn/distributed-system/distributed-lock.html)
119+
116120
## Redis 数据结构
117121

118122
### Redis 常用的数据结构有哪些?
@@ -140,6 +144,83 @@ Redis 5.0 新增加的一个数据结构 `Stream` 可以用来做消息队列,
140144

141145
在绝大部分情况,我们建议使用 String 来存储对象数据即可!
142146

147+
### String 的底层实现是什么?
148+
149+
Redis 是基于 C 语言编写的,但 Redis 的 String 类型的底层实现并不是 C 语言中的字符串(即以空字符 `\0` 结尾的字符数组),而是自己编写了 [SDS](https://github.com/antirez/sds)(Simple Dynamic String,简单动态字符串) 来作为底层实现。
150+
151+
SDS 最早是 Redis 作者为日常 C 语言开发而设计的 C 字符串,后来被应用到了 Redis 上,并经过了大量的修改完善以适合高性能操作。
152+
153+
Redis7.0 的 SDS 的部分源码如下(https://github.com/redis/redis/blob/7.0/src/sds.h):
154+
155+
```c
156+
/* Note: sdshdr5 is never used, we just access the flags byte directly.
157+
* However is here to document the layout of type 5 SDS strings. */
158+
struct __attribute__ ((__packed__)) sdshdr5 {
159+
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
160+
char buf[];
161+
};
162+
struct __attribute__ ((__packed__)) sdshdr8 {
163+
uint8_t len; /* used */
164+
uint8_t alloc; /* excluding the header and null terminator */
165+
unsigned char flags; /* 3 lsb of type, 5 unused bits */
166+
char buf[];
167+
};
168+
struct __attribute__ ((__packed__)) sdshdr16 {
169+
uint16_t len; /* used */
170+
uint16_t alloc; /* excluding the header and null terminator */
171+
unsigned char flags; /* 3 lsb of type, 5 unused bits */
172+
char buf[];
173+
};
174+
struct __attribute__ ((__packed__)) sdshdr32 {
175+
uint32_t len; /* used */
176+
uint32_t alloc; /* excluding the header and null terminator */
177+
unsigned char flags; /* 3 lsb of type, 5 unused bits */
178+
char buf[];
179+
};
180+
struct __attribute__ ((__packed__)) sdshdr64 {
181+
uint64_t len; /* used */
182+
uint64_t alloc; /* excluding the header and null terminator */
183+
unsigned char flags; /* 3 lsb of type, 5 unused bits */
184+
char buf[];
185+
};
186+
```
187+
188+
通过源码可以看出,SDS 共有五种实现方式 SDS_TYPE_5(并未用到)、SDS_TYPE_8、SDS_TYPE_16、SDS_TYPE_32、SDS_TYPE_64,其中只有后四种实际用到。Redis 会根据初始化的长度决定使用哪种类型,从而减少内存的使用。
189+
190+
| 类型 | 字节 | 位 |
191+
| -------- | ---- | ---- |
192+
| sdshdr5 | < 1 | <8 |
193+
| sdshdr8 | 1 | 8 |
194+
| sdshdr16 | 2 | 16 |
195+
| sdshdr32 | 4 | 32 |
196+
| sdshdr64 | 8 | 64 |
197+
198+
对于后四种实现都包含了下面这 4 个属性:
199+
200+
- `len` :字符串的长度也就是已经使用的字节数
201+
- `alloc`:总共可用的字符空间大小,alloc-len 就是 SDS 剩余的空间大小
202+
- `buf[]` :实际存储字符串的数组
203+
- `flags` :低三位保存类型标志
204+
205+
SDS 相比于 C 语言中的字符串有如下提升:
206+
207+
1. **可以避免缓冲区溢出** :C 语言中的字符串被修改(比如拼接)时,一旦没有分配足够长度的内存空间,就会造成缓冲区溢出。SDS 被修改时,会先根据 len 属性检查空间大小是否满足要求,如果不满足,则先扩展至所需大小再进行修改操作。
208+
2. **获取字符串长度的复杂度较低** : C 语言中的字符串的长度通常是经过遍历计数来实现的,时间复杂度为 O(n)。SDS 的长度获取直接读取 len 属性即可,时间复杂度为 O(1)。
209+
3. **减少内存分配次数** : 为了避免修改(增加/减少)字符串时,每次都需要重新分配内存(C 语言的字符串是这样的),SDS 实现了空间预分配和惰性空间释放两种优化策略。当 SDS 需要增加字符串时,Redis 会为 SDS 分配好内存,并且根据特定的算法分配多余的内存,这样可以减少连续执行字符串增长操作所需的内存重分配次数。当 SDS 需要减少字符串时,这部分内存不会立即被回收,会被记录下来,等待后续使用(支持手动释放,有对应的 API)。
210+
4. **二进制安全** :C 语言中的字符串以空字符 `\0` 作为字符串结束的标识,这存在一些问题,像一些二进制文件(比如图片、视频、音频)就可能包括空字符,C 字符串无法正确保存。SDS 使用 len 属性判断字符串是否结束,不存在这个问题。
211+
212+
多提一嘴,很多文章里 SDS 的定义是下面这样的:
213+
214+
```c
215+
struct sdshdr {
216+
unsigned int len;
217+
unsigned int free;
218+
char buf[];
219+
};
220+
```
221+
222+
这个也没错,Redis 3.2 之前就是这样定义的。后来,由于这种方式的定义存在问题,`len``free` 的定义用了 4 个字节,造成了浪费。Redis 3.2 之后,Redis 改进了 SDS 的定义,将其划分为了现在的 5 种类型。
223+
143224
### 购物车信息用 String 还是 Hash 存储更好呢?
144225

145226
由于购物车中的商品频繁修改和变动,购物车信息建议使用 Hash 存储:

docs/java/new-features/java11.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ tag:
77

88
**Java 11** 于 2018 年 9 月 25 日正式发布,这是很重要的一个版本!Java 11 和 2017 年 9 月份发布的 Java 9 以及 2018 年 3 月份发布的 Java 10 相比,其最大的区别就是:在长期支持(Long-Term-Support)方面,**Oracle 表示会对 Java 11 提供大力支持,这一支持将会持续至 2026 年 9 月。这是据 Java 8 以后支持的首个长期版本。**
99

10-
![](https://img-blog.csdnimg.cn/20210603202746605.png)
10+
下面这张图是 Oracle 官方给出的 Oracle JDK 支持的时间线。
11+
12+
![](https://img-blog.csdnimg.cn/4c1611fad59449edbbd6e233690e9fa7.png)
1113

1214
**概览(精选了一部分)**
1315

docs/java/new-features/java17.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@ tag:
55
- Java新特性
66
---
77

8-
Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个长期支持(LTS)版本,这次更新共带来 14 个新特性:
8+
Java 17 在 2021 年 9 月 14 日正式发布,是一个长期支持(LTS)版本。
9+
10+
下面这张图是 Oracle 官方给出的 Oracle JDK 支持的时间线。可以看得到,Java
11+
12+
17 最多可以支持到 2029 年 9 月份。
13+
14+
![](https://img-blog.csdnimg.cn/4c1611fad59449edbbd6e233690e9fa7.png)
15+
16+
Java 17 将是继 Java 8 以来最重要的长期支持(LTS)版本,是 Java 社区八年努力的成果。Spring 6.x 和 Spring Boot 3.x 最低支持的就是 Java 17。
17+
18+
这次更新共带来 14 个新特性:
919

1020
- [JEP 306:Restore Always-Strict Floating-Point Semantics(恢复始终严格的浮点语义)](https://openjdk.java.net/jeps/306)
1121
- [JEP 356:Enhanced Pseudo-Random Number Generators(增强的伪随机数生成器)](https://openjdk.java.net/jeps/356)

docs/java/new-features/java18.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ tag:
55
- Java新特性
66
---
77

8-
Java 18 在 2022 年 3 月 22 日正式发布,非长期支持版本。Java 18 带来了 9 个新特性:
8+
Java 18 在 2022 年 3 月 22 日正式发布,非长期支持版本。
9+
10+
Java 18 带来了 9 个新特性:
911

1012
- [JEP 400:UTF-8 by Default(默认字符集为 UTF-8)](https://openjdk.java.net/jeps/400)
1113
- [JEP 408:Simple Web Server(简易的 Web 服务器)](https://openjdk.java.net/jeps/408)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy