Skip to content

Commit 394ebc0

Browse files
committed
docs: 分布式和数据库面试题完善
1 parent 4baa23d commit 394ebc0

6 files changed

Lines changed: 314 additions & 128 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ MySQL 中没有专门的布尔类型,而是用 `TINYINT(1)` 类型来表示布
206206
1. **格式兼容性与完整性:**
207207
- 手机号可能包含前导零(如某些地区的固话区号)、国家代码前缀('+'),甚至可能带有分隔符('-' 或空格)。INT 或 BIGINT 这种数字类型会自动丢失这些重要的格式信息(比如前导零会被去掉,'+' 和 '-' 无法存储)。
208208
- VARCHAR 可以原样存储各种格式的号码,无论是国内的 11 位手机号,还是带有国家代码的国际号码,都能完美兼容。
209-
2. **非算术性:**手机号虽然看起来是数字,但我们从不对它进行数学运算(比如求和、平均值)。它本质上是一个标识符,更像是一个字符串。用 VARCHAR 更符合其数据性质。
209+
2. **非算术性:** 手机号虽然看起来是数字,但我们从不对它进行数学运算(比如求和、平均值)。它本质上是一个标识符,更像是一个字符串。用 VARCHAR 更符合其数据性质。
210210
3. **查询灵活性:**
211211
- 业务中常常需要根据号段(前缀)进行查询,例如查找所有 "138" 开头的用户。使用 VARCHAR 类型配合 `LIKE '138%'` 这样的 SQL 查询既直观又高效。
212212
- 如果使用数字类型,进行类似的前缀匹配通常需要复杂的函数转换(如 CAST 或 SUBSTRING),或者使用范围查询(如 `WHERE phone >= 13800000000 AND phone < 13900000000`),这不仅写法繁琐,而且可能无法有效利用索引,导致性能下降。

docs/database/redis/cache-basics.md

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,33 @@ head:
2020
2121
## 缓存的基本思想
2222

23-
很多同学只知道缓存可以提高系统性能以及减少请求相应时间,但是,不太清楚缓存的本质思想是什么。
23+
很多同学只知道缓存可以提高系统性能以及减少请求 **响应时间**(Response Time),但是,不太清楚缓存的本质思想是什么。
2424

2525
缓存的基本思想其实很简单,就是我们非常熟悉的 **空间换时间** 这一经典性能优化策略的运用。所谓空间换时间,也就是用更多的存储空间来存储一些可能重复使用或计算的数据,从而减少数据的重新获取或计算的时间。
2626

2727
说到空间换时间,除了缓存之外,你还能想到什么其他的例子吗?这里再列举几个常见的:
2828

29-
- 索引:索引是一种将数据库表中的某些列或字段按照一定的排序规则组织成一个单独的数据结构,需要额外占用空间,但可以大大提高检索效率,降低数据排序成本。
30-
- 数据库表字段冗余:将经常联合查询的数据冗余存储在同一张表中,以减少对多张表的关联查询,进而提升查询性能,减轻数据库压力。
31-
- CDN(内容分发网络):将静态资源分发到多个不同的地方以实现就近访问,进而加快静态资源的访问速度,减轻服务器以及带宽的负担
29+
- **索引**:索引是一种将数据库表中的某些列或字段按照一定的排序规则组织成一个单独的数据结构,虽然需要额外占用空间,但可以大大提高检索效率,降低数据排序成本。
30+
- **数据库表字段冗余**:将经常联合查询的数据冗余存储在同一张表中,以减少对多张表的关联查询,进而提升查询性能,减轻数据库压力。
31+
- **CDN(内容分发网络)**:将静态资源分发到多个边缘节点以实现就近访问,进而加快静态资源的访问速度,减轻源站服务器以及带宽的负担
3232

3333
编程需要要学会归纳总结,将自己学到的东西串联起来!假如你在面试的时候,能聊到这些,面试官一定会对你有一个好印象的。
3434

3535
不要把缓存想的太高大上,虽然,它的确对系统的性能提升的性价比非常高。当我们在学习并应用缓存的时候,你会发现缓存的思想实际在 CPU、操作系统或者其他很多地方都被大量用到。
3636

37-
比如,CPU Cache 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题
37+
比如,**CPU Cache** 缓存的是内存数据,用于解决 **CPU** 处理速度与内存访问速度不匹配的问题;内存缓存的是硬盘数据,用于解决硬盘 **I/O** 速度过慢的问题
3838

3939
![CPU 缓存模型示意图](https://oss.javaguide.cn/github/javaguide/java/concurrent/cpu-cache.png)
4040

41-
再比如,为了提高虚拟地址到物理地址的转换速度,操作系统在页表方案基础之上引入了转址旁路缓存(Translation Lookasjde Buffer,TLB,也被称为快表)
41+
再比如,为了提高虚拟地址到物理地址的转换速度,操作系统在页表方案基础之上引入了 **转址旁路缓存**Translation Lookaside Buffer,**TLB**,也被称为快表
4242

4343
![加入 TLB 之后的地址翻译](https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/physical-virtual-address-translation-mmu.png)
4444

45-
再拿我们日常使用的软件来说,一般都会对你访问过的一些图片或者文件进行缓存,这样你下次再访问的时候加载速度就很快了
45+
拿日常使用的浏览器来说,它会对访问过的图片或静态文件进行缓存(浏览器缓存),这样下次访问相同页面时加载速度会显著提升
4646

4747
![](https://oss.javaguide.cn/github/javaguide/database/redis/chrome-clear-cache.png)
4848

49-
我们日常开发过程中用到的缓存,其中的数据通常存储于内存中,因此访问速度非常快。为了避免内存中的数据在重启或者宕机之后丢失,很多缓存中间件会利用磁盘做持久化。也就是说,缓存相比较于我们常用的关系型数据库(比如 MySQL)来说访问速度要快非常多。为了避免用户请求数据库中的数据速度过于缓慢,我们可以在数据库之上增加一层缓存。
50-
51-
除了能够提高访问速度之外,缓存支持的并发量也要更大,有了缓存之后,数据库的压力也会随之变小。
49+
我们日常开发中用到的缓存,其中的数据通常存储于 **RAM**(内存)中,访问速度极快。为了避免内存数据在重启或宕机后丢失,许多缓存中间件(如 **Redis**)提供了磁盘持久化机制。相比于关系型数据库(如 **MySQL**),缓存的访问速度和并发支持量都要高出几个数量级。在数据库之上增加一层缓存,是保护底层存储、提升系统吞吐量的核心手段。
5250

5351
## 缓存的分类
5452

@@ -66,6 +64,11 @@ head:
6664

6765
![本地缓存示意图](https://oss.javaguide.cn/github/javaguide/database/redis/local-cache.png)
6866

67+
**注意:** 在集群模式下使用本地缓存,必须考虑**负载均衡策略**。如果 Nginx 使用默认的**轮询(Round-Robin)**,同一个用户的请求会随机落在不同机器,导致本地缓存命中率极低。解决方案如下:
68+
69+
1. **网关层**:使用一致性哈希或 Sticky Session,保证同一用户的请求固定打到同一台机器。
70+
2. **应用层**:仅将本地缓存用于**“全局几乎不变”**的数据(如配置字典),而非用户维度数据。
71+
6972
#### 本地缓存的方案有哪些?
7073

7174
**1、JDK 自带的 `HashMap``ConcurrentHashMap` 了。**
@@ -85,14 +88,17 @@ head:
8588
使用 `Caffeine` 创建本地缓存的代码示例,用到了建造者模式:
8689

8790
```java
88-
Caffeine.newBuilder()
89-
// 设置最后一次写入或访问后经过固定时间过期
90-
.expireAfterWrite(60, TimeUnit.DAYS)
91-
// 初始的缓存空间大小
92-
.initialCapacity(100)
93-
// 缓存的最大条数
94-
.maximumSize(500)
95-
.build();
91+
// 使用 Caffeine 创建本地缓存示例
92+
Cache<String, String> cache = Caffeine.newBuilder()
93+
// 设置写入后 60 天过期
94+
.expireAfterWrite(60, TimeUnit.DAYS)
95+
// 初始容量
96+
.initialCapacity(100)
97+
// 最大条数限制
98+
.maximumSize(500)
99+
// 开启统计功能
100+
.recordStats()
101+
.build();
96102
```
97103

98104
#### 本地缓存有什么痛点?
@@ -170,8 +176,6 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
170176
- [J2Cache](https://gitee.com/ld/J2Cache):基于本地内存和 Redis 的两级 Java 缓存框架。
171177
- [JetCache](https://github.com/alibaba/jetcache):阿里开源的缓存框架,支持多级缓存、分布式缓存自动刷新、 TTL 等功能。
172178

173-
如果你想要在自己的项目中实践多级缓存的话,还可以参考借鉴一下 [Redis+Caffeine 两级缓存,让访问速度纵享丝滑](https://mp.weixin.qq.com/s/_ysKYrzyRGebtotGyzQUIw) 这篇文章。
174-
175179
#### 多级缓存一致性如何保证?
176180

177181
在多级缓存系统中,保证强一致性成本太高,业界的几个提供多级缓存功能的缓存框架基本都是最终一致性保证。例如,可以使用 Redis 的发布/订阅机制、Redis Stream 或者消息队列来确保当一个实例的本地缓存发生变化时,其他实例能够及时更新其本地缓存,以保持缓存一致性。

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ Redis 除了可以用作缓存之外,还可以用于分布式锁、限流、
122122
| 管理维护 | 分散,管理不便 | 集中管理,提供丰富的管理工具 |
123123
| 功能丰富性 | 功能有限,通常只提供简单的键值对存储 | 功能丰富,支持多种数据结构和功能 |
124124

125+
关于本地缓存、分布式缓存和多级缓存的详细介绍,可以看我写的这篇文章:[缓存基础常见面试题总结](http://localhost:8080/database/redis/cache-basics.html)
126+
125127
### 常见的缓存读写策略有哪些?
126128

127129
关于常见的缓存读写策略的详细介绍,可以看我写的这篇文章:[3 种常用的缓存读写策略详解](https://javaguide.cn/database/redis/3-commonly-used-cache-read-and-write-strategies.html)

0 commit comments

Comments
 (0)